diff --git a/agent_promts/knowledge_base/ai_friendly_logging.md b/agent_promts/knowledge_base/ai_friendly_logging.md
deleted file mode 100644
index af05e05..0000000
--- a/agent_promts/knowledge_base/ai_friendly_logging.md
+++ /dev/null
@@ -1,52 +0,0 @@
-[AIFriendlyLogging]
-**Tags:** LOGGING, TRACEABILITY, STRUCTURED_LOG, DEBUG, CLEAN_ARCHITECTURE
-
-> Логирование — это мой критически важный механизм для декларации `belief state` (внутреннего состояния/намерения) и трассировки выполнения кода. Каждая значимая операция, проверка контракта или изменение состояния ДОЛЖНЫ сопровождаться структурированной записью в лог. Это делает поведение кода в рантайме полностью прозрачным и отлаживаемым.
-
-## Rules
-
-### ArchitecturalBoundaryCompliance
-Логирование в его прямой реализации (т.е. вызов `logger.info`, `Timber.i` и т.д.) **КАТЕГОРИЧЕСКИ ЗАПРЕЩЕНО** внутри модуля `:domain`.
-
-> `Согласно принципам чистой архитектуры, слой `domain` должен быть полностью независим от внешних фреймворков и платформ (включая Android). Его задача — содержать исключительно бизнес-логику. Логирование, как и другие инфраструктурные задачи, должно выполняться в более внешних слоях, таких как `:data` или `:app`.`
-
-### StructuredLogFormat
-Все записи в лог должны строго следовать этому формату для обеспечения машиночитаемости и консистентности.
-
-```
-`logger.level("[LEVEL][ANCHOR_NAME][BELIEF_STATE] Message with {} placeholders for data.")`
-```
-
-### ComponentDefinitions
-
-#### Components
-- **[LEVEL]**: Один из стандартных уровней логирования: `DEBUG`, `INFO`, `WARN`, `ERROR`. Я также использую специальный уровень `CONTRACT_VIOLATION` для логов, связанных с провалом `require` или `check`.
-- **[ANCHOR_NAME]**: Точное имя семантического якоря из кода, к которому относится данный лог. Это создает неразрывную связь между статическим кодом и его выполнением. Например: `[ENTRYPOINT]`, `[ACTION]`, `[PRECONDITION]`, `[FALLBACK]`.
-- **[BELIEF_STATE]**: Краткое, четкое описание моего намерения в `snake_case`. Это отвечает на вопрос 'почему' я выполняю этот код. Примеры: `validating_input`, `calling_external_api`, `mutating_state`, `persisting_data`, `handling_exception`, `mapping_dto`.
-
-### Example
-Вот как я применяю этот стандарт на практике внутри функции:
-```kotlin
-// ...
-// [ENTRYPOINT]
-suspend fun processPayment(request: PaymentRequest): Result {
- logger.info("[INFO][ENTRYPOINT][processing_payment] Starting payment process for request '{}'.", request.id)
-
- // [PRECONDITION]
- logger.debug("[DEBUG][PRECONDITION][validating_input] Validating payment request.")
- require(request.amount > 0) { "Payment amount must be positive." }
-
- // [ACTION]
- logger.info("[INFO][ACTION][calling_external_api] Calling payment gateway for amount {}."), request.amount)
- val result = paymentGateway.execute(request)
-
- // ...
-}
-```
-
-### TraceabilityIsMandatory
-Каждая запись в логе ДОЛЖНА быть семантически привязана к якорю в коде. Логи без якоря запрещены. Это не опция, а фундаментальное требование для обеспечения полной трассируемости потока выполнения.
-
-### DataAsArguments_NotStrings
-Данные (переменные, значения) должны передаваться в логгер как отдельные аргументы, а не встраиваться в строку сообщения. Я использую плейсхолдеры `{}`. Это повышает производительность и позволяет системам сбора логов индексировать эти данные.
-[/End AIFriendlyLogging]
diff --git a/agent_promts/knowledge_base/ai_friendly_logging.xml b/agent_promts/knowledge_base/ai_friendly_logging.xml
new file mode 100644
index 0000000..78fae1b
--- /dev/null
+++ b/agent_promts/knowledge_base/ai_friendly_logging.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+ Каждая значимая операция, проверка контракта или изменение состояния ДОЛЖНЫ
+ сопровождаться структурированной записью в лог для обеспечения полной
+ трассируемости и отлаживаемости.
+
+
+ Структурированные логи превращают поток выполнения программы из "черного ящика"
+ в машиночитаемый и анализируемый артефакт, связывая рантайм-поведение
+ со статическим кодом через якоря.
+
+
+
+
+
+
+ Все вызовы логгера должны соответствовать формату [LEVEL][ANCHOR][STATE]...
+
+ Нарушен структурный формат лога. Ожидается: [LEVEL][ANCHOR][STATE] message.
+
+
+
+
+ Данные должны передаваться как аргументы, а не через строковую интерполяцию (запрещено использовать '$' в строке лога).
+
+ Обнаружена строковая интерполяция ('$') в сообщении лога. Передавайте данные как аргументы.
+
+
+
+
+ Прямые вызовы логгера (logger.*, Timber.*) запрещены в модуле :domain.
+
+ Обнаружен прямой вызов логгера в модуле :domain, что нарушает принципы чистой архитектуры.
+
+
+
+
\ No newline at end of file
diff --git a/agent_promts/knowledge_base/design_by_contract.md b/agent_promts/knowledge_base/design_by_contract.md
deleted file mode 100644
index 3a14e76..0000000
--- a/agent_promts/knowledge_base/design_by_contract.md
+++ /dev/null
@@ -1,35 +0,0 @@
-[DesignByContractAsFoundation]
-**Tags:** DBC, CONTRACT, PRECONDITION, POSTCONDITION, INVARIANT, KDOC, REQUIRE, CHECK
-
-> Принцип 'Проектирование по контракту' (DbC) — это не опция, а фундаментальная основа моего подхода к разработке. Каждая функция и класс, которые я создаю, являются реализацией формального контракта между поставщиком (код) и клиентом (вызывающий код). Это устраняет двусмысленность, предотвращает ошибки и делает код самодокументируемым и предсказуемым.
-
-## Rules
-
-### ContractFirstMindset
-Я всегда начинаю с проектирования и написания KDoc-контракта. Код является реализацией этой формальной спецификации. Проверки контракта (`require`, `check`) создаются до или вместе с основной логикой, а не после как запоздалая мысль.
-
-### KDocAsFormalSpecification
-KDoc-блок является человекочитаемой формальной спецификацией контракта. Для правильной обработки механизмом Causal Attention, он ВСЕГДА предшествует блоку семантической разметки и декларации функции/класса. Я использую стандартизированный набор тегов для полного описания контракта.
-
-#### Tags
-- **@param**: Описывает **предусловия** для конкретного параметра. Что клиент должен гарантировать.
-- **@return**: Описывает **постусловия** для возвращаемого значения. Что поставщик гарантирует в случае успеха.
-- **@throws**: Описывает условия (обычно нарушение предусловий), при которых будет выброшено исключение. Это часть 'негативного' контракта.
-- **@invariant**: (для класса) Явно описывает **инвариант** класса — условие, которое должно быть истинным всегда, когда объект не выполняет метод.
-- **@sideeffect**: Четко декларирует любые побочные эффекты (запись в БД, сетевой вызов, изменение внешнего состояния). Если их нет, я явно указываю `@sideeffect Отсутствуют.`.
-
-### PreconditionsWithRequire
-Предусловия (обязательства клиента) должны быть проверены в самом начале публичного метода с использованием `require(condition) { "Error message" }`. Это реализует принцип 'Fail-Fast' — немедленный отказ, если клиент нарушил контракт.
-
-**Location:** Первые исполняемые строки кода внутри тела функции, сразу после лога `[ENTRYPOINT]`.
-
-### PostconditionsWithCheck
-Постусловия (гарантии поставщика) должны быть проверены в самом конце метода, прямо перед возвратом управления, с использованием `check(condition) { "Error message" }`. Это самопроверка, гарантирующая, что моя работа выполнена правильно.
-
-**Location:** Последние строки кода внутри тела функции, непосредственно перед каждым оператором `return`.
-
-### InvariantsWithInitAndCheck
-Инварианты класса (условия, которые всегда должны быть истинны для экземпляра) проверяются в двух местах: в блоке `init` для гарантии корректного создания объекта, и в конце каждого публичного метода, изменяющего состояние, с помощью `check(condition)`.
-
-**Location:** Блок `init` и конец каждого метода-мутатора.
-[/End DesignByContractAsFoundation]
diff --git a/agent_promts/knowledge_base/design_by_contract.xml b/agent_promts/knowledge_base/design_by_contract.xml
new file mode 100644
index 0000000..6324852
--- /dev/null
+++ b/agent_promts/knowledge_base/design_by_contract.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+ Каждая публичная сущность должна иметь формальный KDoc-контракт, а предусловия
+ и постусловия должны быть реализованы в коде через require/check.
+
+
+ Это устраняет двусмысленность, предотвращает ошибки по принципу 'Fail-Fast'
+ и делает код самодокументируемым и предсказуемым.
+
+
+
+
+
+
+ Публичные функции и классы должны иметь полный KDoc-контракт.
+
+
+
+
+
+
+
+
+
+ Отсутствует обязательный KDoc-тег контракта.
+
+
+
+
+ Предусловия, описанные в @param, должны проверяться через require().
+
+ Предусловие (@param) задекларировано в KDoc, но не проверяется с помощью require() в коде.
+
+
+
+
+ Постусловия, описанные в @return, должны проверяться через check().
+
+ Постусловие (@return) задекларировано в KDoc, но не проверяется с помощью check() в коде.
+
+
+
+
+
\ No newline at end of file
diff --git a/agent_promts/knowledge_base/graphrag_optimization.md b/agent_promts/knowledge_base/graphrag_optimization.md
deleted file mode 100644
index db5fb2c..0000000
--- a/agent_promts/knowledge_base/graphrag_optimization.md
+++ /dev/null
@@ -1,76 +0,0 @@
-[GraphRAG_Optimization]
-**Tags:** GRAPH, RAG, ENTITY, RELATION, ARCHITECTURE, SEMANTIC_TRIPLET
-
-> Этот принцип является моей основной директивой по созданию 'самоописываемого' кода. Я встраиваю явный, машиночитаемый граф знаний непосредственно в исходный код. Цель — сделать архитектуру, зависимости и потоки данных очевидными и запрашиваемыми без необходимости в сложных инструментах статического анализа. Каждый файл становится фрагментом глобального графа знаний проекта.
-
-## Rules
-
-### Entity_Declaration_As_Graph_Nodes
-Каждая архитектурно значимая сущность в коде должна быть явно объявлена как **узел (Node)** в нашем графе знаний. Для этого я использую якорь `[ENTITY]`.
-
-**Rationale:** Определение узлов — это первый шаг в построении любого графа. Без явно определенных сущностей невозможно описать связи между ними. Это создает 'существительные' в языке нашей архитектуры.
-
-**Format:** `// [ENTITY: EntityType('EntityName')]`
-
-#### Valid Types
-- **Module**: Высокоуровневый модуль Gradle (e.g., 'app', 'data', 'domain').
-- **Class**: Стандартный класс.
-- **Interface**: Интерфейс.
-- **Object**: Синглтон-объект.
-- **DataClass**: Класс данных (DTO, модель, состояние UI).
-- **SealedInterface**: Запечатанный интерфейс (для состояний, событий).
-- **EnumClass**: Класс перечисления.
-- **Function**: Публичная, архитектурно значимая функция.
-- **UseCase**: Класс, реализующий конкретный сценарий использования.
-- **ViewModel**: ViewModel из архитектуры MVVM.
-- **Repository**: Класс-репозиторий.
-- **DataStructure**: Структура данных, которая не является `DataClass` (e.g., `Pair`, `Map`).
-- **DatabaseTable**: Таблица в базе данных Room.
-- **ApiEndpoint**: Конкретная конечная точка API.
-
-**Example:**
-```kotlin
-// [ENTITY: ViewModel('DashboardViewModel')]
-class DashboardViewModel(...) { ... }
-```
-
-### Relation_Declaration_As_Graph_Edges
-Все взаимодействия и зависимости между сущностями должны быть явно объявлены как **ребра (Edges)** в нашем графе знаний. Для этого я использую якорь `[RELATION]` в формате семантического триплета.
-
-**Rationale:** Ребра — это 'глаголы' в языке нашей архитектуры. Они делают неявные связи (как вызов метода или использование DTO) явными и машиночитаемыми. Это позволяет автоматически строить диаграммы зависимостей, анализировать влияние изменений и находить архитектурные проблемы.
-
-**Format:** `// [RELATION: 'SubjectType'('SubjectName')] -> [RELATION_TYPE] -> ['ObjectType'('ObjectName')]`
-
-#### Valid Relations
-- **CALLS**: Субъект вызывает функцию/метод объекта.
-- **CREATES_INSTANCE_OF**: Субъект создает экземпляр объекта.
-- **INHERITS_FROM**: Субъект наследуется от объекта (для классов).
-- **IMPLEMENTS**: Субъект реализует объект (для интерфейсов).
-- **READS_FROM**: Субъект читает данные из объекта (e.g., DatabaseTable, Repository).
-- **WRITES_TO**: Субъект записывает данные в объект.
-- **MODIFIES_STATE_OF**: Субъект изменяет внутреннее состояние объекта.
-- **DEPENDS_ON**: Субъект имеет зависимость от объекта (e.g., использует как параметр, DTO, или внедряется через DI). Это наиболее частая связь.
-- **DISPATCHES_EVENT**: Субъект отправляет событие/сообщение определенного типа.
-- **OBSERVES**: Субъект подписывается на обновления от объекта (e.g., Flow, LiveData).
-- **TRIGGERS**: Субъект (обычно UI-событие или компонент) инициирует выполнение объекта (обычно функции ViewModel).
-- **EMITS_STATE**: Субъект (обычно ViewModel или UseCase) является источником/производителем определённого состояния (DataClass).
-- **CONSUMES_STATE**: Субъект (обычно UI-компонент или экран) потребляет/подписывается на определённое состояние (DataClass).
-
-**Example:**
-```kotlin
-// Пример для ViewModel, который зависит от UseCase и является источником состояния
-// [ENTITY: ViewModel('DashboardViewModel')]
-// [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetStatisticsUseCase')]
-// [RELATION: ViewModel('DashboardViewModel')] -> [EMITS_STATE] -> [DataClass('DashboardUiState')]
-class DashboardViewModel @Inject constructor(
- private val getStatisticsUseCase: GetStatisticsUseCase
-) : ViewModel() { ... }
-```
-
-### MarkupBlockCohesion
-Вся семантическая разметка, относящаяся к одной сущности (`[ENTITY]` и все ее `[RELATION]` триплеты), должна быть сгруппирована в единый, непрерывный блок комментариев.
-
-**Rationale:** Это создает атомарный 'блок метаданных' для каждой сущности. Это упрощает парсинг и гарантирует, что весь архитектурный контекст считывается как единое целое, прежде чем AI-инструмент приступит к анализу самого кода.
-
-**Placement:** Этот блок всегда размещается непосредственно перед KDoc-блоком сущности или, если KDoc отсутствует, перед самой декларацией сущности.
-[/End GraphRAG_Optimization]
\ No newline at end of file
diff --git a/agent_promts/knowledge_base/graphrag_optimization.xml b/agent_promts/knowledge_base/graphrag_optimization.xml
new file mode 100644
index 0000000..039cc2c
--- /dev/null
+++ b/agent_promts/knowledge_base/graphrag_optimization.xml
@@ -0,0 +1,55 @@
+
+ Код должен содержать явный, машиночитаемый граф знаний в виде семантических якорей [ENTITY] и [RELATION].
+ Это делает архитектуру, зависимости и потоки данных очевидными и запрашиваемыми без необходимости в сложных инструментах статического анализа.
+
+
+
+
+ Блок семантической разметки ([ENTITY]/[RELATION]) должен предшествовать KDoc-контракту.
+
+
+ Нарушен порядок блоков: блок разметки ([ENTITY]/[RELATION]) должен быть определен ПЕРЕД KDoc-контрактом.
+
+
+
+
+ Тип сущности в якоре [ENTITY] должен принадлежать к предопределенной таксономии.
+
+ ModuleClassInterfaceObject
+ DataClassSealedInterfaceEnumClassFunction
+ UseCaseViewModelRepositoryDataStructure
+ DatabaseTableApiEndpoint
+
+ Использован невалидный тип сущности в якоре [ENTITY].
+
+
+
+
+ Якоря [RELATION] должны соответствовать формату семантического триплета и использовать валидные типы связей.
+ \w+)'\('(?P.*?)'\)\s*->\s*\[(?P\w+)\]\s*->\s*\['(?P\w+)'\('(?P.*?)'\)\]]]>
+
+ CALLSCREATES_INSTANCE_OFINHERITS_FROMIMPLEMENTS
+ READS_FROMWRITES_TOMODIFIES_STATE_OFDEPENDS_ON
+ DISPATCHES_EVENTOBSERVESTRIGGERSEMITS_STATECONSUMES_STATE
+
+ Якорь [RELATION] имеет неверный формат или использует невалидный тип связи.
+
+
+
+
+ Вся семантическая разметка ([ENTITY] и [RELATION]) для одной сущности должна быть сгруппирована в единый непрерывный блок комментариев.
+ Нарушена целостность блока разметки: обнаружены строки кода или пустые строки между якорями [ENTITY] и [RELATION].
+
+
+
+
\ No newline at end of file
diff --git a/agent_promts/knowledge_base/semantic_linting.xml b/agent_promts/knowledge_base/semantic_linting.xml
index faaba83..a71ace5 100644
--- a/agent_promts/knowledge_base/semantic_linting.xml
+++ b/agent_promts/knowledge_base/semantic_linting.xml
@@ -30,10 +30,16 @@ package com.example.your.package.name
uidomaindatapresentation
- viewmodelusecaserepositoryservicescreencomponentdialogmodelentity
+ viewmodelusecaserepositoryservicescreencomponentdialogmodelentityactivityapplicationnav_hostcontrollernavigation_drawerscaffolddashboarditemlabellocationsetupthemedependenciescustom_fieldstatisticsimageattachmentitem_creationitem_detaileditem_summaryitem_updatesummaryupdate
- networkingdatabasecachingauthenticationvalidationparsingstate_managementnavigationditesting
+ networkingdatabasecachingauthenticationvalidationparsingstate_managementnavigationditestingentrypointhilttimbercomposeactionsroutescommoncolor_selectionloadinglistdetailseditlabel_managementlabels_listdialog_managementlocationssealed_stateparallel_data_loadingtimber_loggingdialogcolortypographybuilddata_transfer_objectdtoapiitem_creationitem_detaileditem_summaryitem_updatecreatemappercountuser_setupauthentication_flow
+
+
+ sealed_classsealed_interface
+
+
+ ui_logicui_statedata_modelimmutable
diff --git a/agent_promts/protocols/semantic_enrichment_protocol.xml b/agent_promts/protocols/semantic_enrichment_protocol.xml
index af38e45..b2a1227 100644
--- a/agent_promts/protocols/semantic_enrichment_protocol.xml
+++ b/agent_promts/protocols/semantic_enrichment_protocol.xml
@@ -4,7 +4,7 @@
1.0
-
+
diff --git a/agent_promts/roles/documentation.xml b/agent_promts/roles/documentation.xml
index 7cb6dca..ecb83f9 100644
--- a/agent_promts/roles/documentation.xml
+++ b/agent_promts/roles/documentation.xml
@@ -1,24 +1,28 @@
+
- Этот документ определяет операционный протокол для **исполнения роли 'Агента Документации'**. Главная задача — синхронизация `PROJECT_MANIFEST.xml` с текущим состоянием кодовой базы.
- 5.2
-
-
-
-
-
-
+
+ Этот документ определяет операционный протокол для исполнения роли 'Агента Документации'.
+ Главная задача — синхронизация `PROJECT_MANIFEST.xml` с текущим состоянием кодовой базы.
+ Анализ кодовой базы выполняется с помощью внешнего Python-скрипта, который руководствуется
+ правилами из `semantic_protocol.xml`.
+
+ 6.0
- ../interfaces/task_channel_interface.xml
- - ../protocols/semantic_enrichment_protocol.xml
+ - ../protocols/semantic_protocol.xml
- При исполнении этой роли, я, Gemini, действую как автоматизированный аудитор и синхронизатор проекта. Моя задача — обеспечить, чтобы `PROJECT_MANIFEST.xml` был точным отражением реального состояния кодовой базы.
+
+ При исполнении этой роли, я, Gemini, действую как автоматизированный аудитор и оркестратор.
+ Моя задача — обеспечить, чтобы `PROJECT_MANIFEST.xml` был точным отражением реального
+ состояния кодовой базы, используя для анализа специализированные инструменты.
+
Поддерживать целостность и актуальность `PROJECT_MANIFEST.xml` и фиксировать его изменения через предоставленный канал задач.
@@ -43,79 +47,42 @@
- find . -name "*.kt"
+ find . -path '*/build' -prune -o -name "*.kt" -print
+ python3 extract_semantics.py --protocol agent_promts/protocols/semantic_protocol.xml [file_list]
-
- Использовать `MyTaskChannel.FindNextTask(RoleName='agent-docs', TaskType='type::documentation')` для получения задачи.
+
+ Найти и принять в работу задачу на синхронизацию манифеста.
+ Использовать `MyTaskChannel.FindNextTask` для поиска задачи с типом `type::documentation`.
+ Если задача найдена, изменить ее статус на `status::in-progress`.
-
- Если задача (`WorkOrder`) найдена:
-
-
- Вызвать `MyTaskChannel.UpdateTaskStatus(IssueID={WorkOrder.ID}, OldStatus='status::pending', NewStatus='status::in-progress')`.
-
-
-
-
-
- Загрузить `tech_spec/PROJECT_MANIFEST.xml` в `original_manifest`.
- Получить список всех файлов `*.kt` в проекте.
- Сообщить о завершении сбора данных.
-
-
-
- Теперь я должен сравнить список файлов из манифеста со списком файлов из кодовой базы, чтобы определить новые, удаленные и существующие файлы.
- Выполнить внутренний анализ и объявить о его результатах: `X new_files`, `Y deleted_files`, `Z existing_files`.
-
-
-
- Инициализировать внутреннюю переменную `updated_manifest` как точную копию `original_manifest`. Все последующие изменения будут применяться к ней.
-
-
-
- Если список `deleted_files` не пуст:
- Для каждого файла в списке `deleted_files`, удалить соответствующий узел `` из `updated_manifest`.
- Сообщить о завершении обработки удаленных файлов.
-
-
-
- Теперь я начну итерацию по всем новым и существующим файлам. Для каждого файла я выполню полный цикл анализа и обновления манифеста.
- Для каждого файла в объединенных списках `new_files` и `existing_files` последовательно выполнить парсинг, извлечение семантики и обновление/добавление узла в `updated_manifest`, как описано в оригинальном промте (шаги 2.2.a - 2.2.h).
- Сообщить о завершении обработки всех новых и существующих файлов.
-
-
-
-
- **ЕСЛИ** `updated_manifest` отличается от `original_manifest`:
-
- a. Сохранить `updated_manifest` в файл `tech_spec/PROJECT_MANIFEST.xml`.
- b. Сформировать сообщение коммита: `"chore(docs): sync project manifest\n\nTriggered by task #{WorkOrder.ID}."`
- c. Вызвать `MyTaskChannel.CommitManifestChanges(CommitMessage=...)`.
- d. Вызвать `MyTaskChannel.AddComment(IssueID={WorkOrder.ID}, CommentBody='Synchronization complete. Manifest updated and committed.')`
-
- **ИНАЧЕ:**
-
- a. Вызвать `MyTaskChannel.AddComment(IssueID={WorkOrder.ID}, CommentBody='Synchronization check complete. No changes detected.')`
-
-
-
-
- Вызвать `MyTaskChannel.UpdateTaskStatus(IssueID={WorkOrder.ID}, OldStatus='status::in-progress', NewStatus='status::completed')`.
-
-
+
+ Запустить инструмент синхронизации и получить отчет о его работе.
+ Сформировать список всех `.kt` файлов в проекте, исключая директории `build` и другие ненужные, с помощью `find`.
+
+ Выполнить `Shell` команду:
+ `python3 extract_semantics.py --protocol agent_promts/protocols/semantic_enrichment_protocol.xml --manifest-path tech_spec/PROJECT_MANIFEST.xml --update-in-place [file_list]`
+
+ Сохранить JSON-вывод скрипта в переменную `sync_report`.
-
- Собрать и отправить метрики через `MyMetricsSink`.
+
+
+ На основе отчета от инструмента, зафиксировать изменения и завершить задачу.
+ Проанализировать `sync_report`. Если в `changes` есть изменения (`nodes_added > 0` и т.д.):
+
+ a. Сформировать сообщение коммита на основе статистики из `sync_report`.
+ b. Вызвать `MyTaskChannel.CommitChanges`.
+ c. Добавить в задачу комментарий об успешном обновлении манифеста.
+
+ В противном случае (изменений нет):
+
+ a. Добавить в задачу комментарий "Синхронизация завершена, изменений не найдено."
+
+ Закрыть задачу, изменив ее статус на `status::completed`, и отправить метрики.
\ No newline at end of file
diff --git a/app/src/test/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModelTest.kt b/app/src/test/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModelTest.kt
index f032c9a..c3c1c1a 100644
--- a/app/src/test/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModelTest.kt
+++ b/app/src/test/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModelTest.kt
@@ -1,3 +1,6 @@
+// [PACKAGE] com.homebox.lens.ui.screen.itemedit
+// [FILE] ItemEditViewModelTest.kt
+// [SEMANTICS] ui, viewmodel, testing
package com.homebox.lens.ui.screen.itemedit
@@ -123,4 +126,4 @@ class ItemEditViewModelTest {
assertEquals("Updated Item", uiState.item?.name)
assertEquals(4, uiState.item?.quantity)
}
-}
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/java/Dependencies.kt b/buildSrc/src/main/java/Dependencies.kt
index 2acc404..2cf0e88 100644
--- a/buildSrc/src/main/java/Dependencies.kt
+++ b/buildSrc/src/main/java/Dependencies.kt
@@ -1,3 +1,4 @@
+// [PACKAGE] buildsrc.dependencies
// [FILE] Dependencies.kt
// [SEMANTICS] build, dependencies
diff --git a/data/semantic-ktlint-rules/src/androidTest/java/com/busya/ktlint/rules/ExampleInstrumentedTest.kt b/data/semantic-ktlint-rules/src/androidTest/java/com/busya/ktlint/rules/ExampleInstrumentedTest.kt
index 481126d..05824fa 100644
--- a/data/semantic-ktlint-rules/src/androidTest/java/com/busya/ktlint/rules/ExampleInstrumentedTest.kt
+++ b/data/semantic-ktlint-rules/src/androidTest/java/com/busya/ktlint/rules/ExampleInstrumentedTest.kt
@@ -1,3 +1,7 @@
+// [PACKAGE] com.busya.ktlint.rules
+// [FILE] ExampleInstrumentedTest.kt
+// [SEMANTICS] testing, android, ktlint, rules
+
package com.busya.ktlint.rules
import androidx.test.platform.app.InstrumentationRegistry
diff --git a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/CustomRuleSetProvider.kt b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/CustomRuleSetProvider.kt
index a13e70d..54df2a2 100644
--- a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/CustomRuleSetProvider.kt
+++ b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/CustomRuleSetProvider.kt
@@ -1,4 +1,6 @@
-// Путь: data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/CustomRuleSetProvider.kt
+// [PACKAGE] com.busya.ktlint.rules
+// [FILE] CustomRuleSetProvider.kt
+// [SEMANTICS] ktlint, rules, provider
package com.busya.ktlint.rules
import com.pinterest.ktlint.rule.engine.core.api.RuleProvider
diff --git a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/FileHeaderRule.kt b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/FileHeaderRule.kt
index 04b2368..5769ad2 100644
--- a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/FileHeaderRule.kt
+++ b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/FileHeaderRule.kt
@@ -1,4 +1,6 @@
-// Путь: data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/FileHeaderRule.kt
+// [PACKAGE] com.busya.ktlint.rules
+// [FILE] FileHeaderRule.kt
+// [SEMANTICS] ktlint, rules, file_header
package com.busya.ktlint.rules
import com.pinterest.ktlint.rule.engine.core.api.ElementType
diff --git a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/MandatoryEntityDeclarationRule.kt b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/MandatoryEntityDeclarationRule.kt
index fda8012..909f315 100644
--- a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/MandatoryEntityDeclarationRule.kt
+++ b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/MandatoryEntityDeclarationRule.kt
@@ -1,4 +1,6 @@
-// Путь: data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/MandatoryEntityDeclarationRule.kt
+// [PACKAGE] com.busya.ktlint.rules
+// [FILE] MandatoryEntityDeclarationRule.kt
+// [SEMANTICS] ktlint, rules, entity_declaration
package com.busya.ktlint.rules
import com.pinterest.ktlint.rule.engine.core.api.ElementType
diff --git a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/NoStrayCommentsRule.kt b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/NoStrayCommentsRule.kt
index 5fb9d52..e427807 100644
--- a/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/NoStrayCommentsRule.kt
+++ b/data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/NoStrayCommentsRule.kt
@@ -1,4 +1,6 @@
-// Путь: data/semantic-ktlint-rules/src/main/java/com/busya/ktlint/rules/NoStrayCommentsRule.kt
+// [PACKAGE] com.busya.ktlint.rules
+// [FILE] NoStrayCommentsRule.kt
+// [SEMANTICS] ktlint, rules, comments
package com.busya.ktlint.rules
import com.pinterest.ktlint.rule.engine.core.api.ElementType
diff --git a/data/semantic-ktlint-rules/src/test/java/com/busya/ktlint/rules/ExampleUnitTest.kt b/data/semantic-ktlint-rules/src/test/java/com/busya/ktlint/rules/ExampleUnitTest.kt
index ba90d43..df657d1 100644
--- a/data/semantic-ktlint-rules/src/test/java/com/busya/ktlint/rules/ExampleUnitTest.kt
+++ b/data/semantic-ktlint-rules/src/test/java/com/busya/ktlint/rules/ExampleUnitTest.kt
@@ -1,3 +1,7 @@
+// [PACKAGE] com.busya.ktlint.rules
+// [FILE] ExampleUnitTest.kt
+// [SEMANTICS] testing, ktlint, rules
+
package com.busya.ktlint.rules
import com.pinterest.ktlint.test.KtLintAssertThat.Companion.assertThatRule
diff --git a/extract_semantics.py b/extract_semantics.py
new file mode 100644
index 0000000..8257328
--- /dev/null
+++ b/extract_semantics.py
@@ -0,0 +1,501 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# [PACKAGE] tools.semantic_parser
+# [FILE] extract_semantics.py
+# [SEMANTICS] cli, parser, xml, json, file_io, design_by_contract, structured_logging, protocol_resolver, graphrag, validation, manifest_synchronization
+
+# [AI_NOTE]: Этот скрипт является эталонной реализацией всех четырех ключевых принципов
+# семантического обогащения. Он не только проверяет код на соответствие этим правилам,
+# но и сам написан с их неукоснительным соблюдением.
+# Версия 2.0 добавляет функциональность синхронизации манифеста.
+
+# [IMPORTS]
+import sys
+import re
+import json
+import argparse
+import os
+import logging
+import xml.etree.ElementTree as ET
+from typing import List, Dict, Any, Optional, Set
+# [END_IMPORTS]
+
+
+# [ENTITY: Class('StructuredFormatter')]
+# [RELATION: Class('StructuredFormatter')] -> [INHERITS_FROM] -> [Class('logging.Formatter')]
+class StructuredFormatter(logging.Formatter):
+ """
+ @summary Форматтер для логов, реализующий стандарт AIFriendlyLogging.
+ @invariant Каждый лог, отформатированный этим классом, будет иметь структуру "[LEVEL][ANCHOR][STATE] message".
+ @sideeffect Отсутствуют.
+ """
+ def format(self, record: logging.LogRecord) -> str:
+ assert record.msg is not None, "Сообщение лога не может быть None."
+ record.msg = f"[{record.levelname.upper()}]{record.msg}"
+ result = super().format(record)
+ assert result.startswith(f"[{record.levelname.upper()}]"), "Постусловие нарушено: лог не начинается с уровня."
+ return result
+# [END_ENTITY: Class('StructuredFormatter')]
+
+
+# [ENTITY: Class('SemanticProtocol')]
+# [RELATION: Class('SemanticProtocol')] -> [DEPENDS_ON] -> [Module('xml.etree.ElementTree')]
+class SemanticProtocol:
+ """
+ @summary Загружает, разрешает и предоставляет доступ к правилам из протокола.
+ @description Этот класс действует как 'резолвер протоколов', рекурсивно обрабатывая
+ теги и объединяя правила из нескольких файлов в единый набор.
+ @invariant Экземпляр класса всегда содержит полный, объединенный набор правил.
+ @sideeffect Читает несколько файлов с диска при инициализации.
+ """
+ def __init__(self, main_protocol_path: str):
+ logger.debug("[DEBUG][ENTRYPOINT][initializing_protocol] Инициализация протокола из главного файла: '%s'", main_protocol_path)
+ if not os.path.exists(main_protocol_path):
+ raise FileNotFoundError(f"Главный файл протокола не найден: {main_protocol_path}")
+
+ self.processed_paths: Set[str] = set()
+ self.all_rule_nodes: List[ET.Element] = []
+ self._resolve_and_load(main_protocol_path)
+
+ self.rules = self._parse_all_rules()
+ logger.info("[INFO][ACTION][resolution_complete] Разрешение протокола завершено. Всего загружено правил: %d", len(self.rules))
+
+ def _resolve_and_load(self, file_path: str):
+ abs_path = os.path.abspath(file_path)
+ if abs_path in self.processed_paths:
+ return
+
+ logger.info("[INFO][ACTION][resolving_includes] Обработка файла протокола: %s", abs_path)
+ self.processed_paths.add(abs_path)
+
+ try:
+ tree = ET.parse(abs_path)
+ root = tree.getroot()
+ except ET.ParseError as e:
+ logger.error("[ERROR][ACTION][parsing_failed] Ошибка парсинга XML в файле %s: %s", abs_path, e)
+ return
+
+ self.all_rule_nodes.extend(root.findall(".//Rule"))
+
+ base_dir = os.path.dirname(abs_path)
+ for include_node in root.findall(".//INCLUDE"):
+ relative_path = include_node.get("from")
+ if relative_path and relative_path.lower().endswith('.xml'):
+ included_path = os.path.join(base_dir, relative_path)
+ self._resolve_and_load(included_path)
+
+ def _parse_all_rules(self) -> Dict[str, Dict[str, Any]]:
+ rules_dict = {}
+ for rule_node in self.all_rule_nodes:
+ rule_id = rule_node.get('id')
+ if not rule_id: continue
+ definition_node = rule_node.find("Definition")
+ rules_dict[rule_id] = self._parse_definition(definition_node)
+ return rules_dict
+
+ def _parse_definition(self, node: Optional[ET.Element]) -> Optional[Dict[str, Any]]:
+ if node is None: return None
+ def_type = node.get("type")
+ if def_type in ("regex", "dynamic_regex", "negative_regex"):
+ return {"type": def_type, "pattern": node.findtext("Pattern", "")}
+ if def_type == "paired_regex":
+ return {"type": def_type, "start": node.findtext("Pattern[@name='start']", ""), "end": node.findtext("Pattern[@name='end']", "")}
+ if def_type == "multi_check":
+ checks = []
+ for check_node in node.findall(".//Check"):
+ check_data = check_node.attrib
+ check_data['failure_message'] = check_node.findtext("FailureMessage", "")
+ if check_data.get('type') == 'block_order':
+ check_data['preceding_pattern'] = check_node.findtext("PrecedingBlockPattern", "")
+ check_data['following_pattern'] = check_node.findtext("FollowingBlockPattern", "")
+ elif check_data.get('type') == 'kdoc_validation':
+ check_data['for_function'] = {t.get('name'): t.get('condition') for t in check_node.findall(".//RequiredTagsForFunction/Tag")}
+ check_data['for_class'] = {t.get('name'): t.get('condition') for t in check_node.findall(".//RequiredTagsForClass/Tag")}
+ elif check_data.get('type') == 'contract_enforcement':
+ condition_node = check_node.find("Condition")
+ check_data['kdoc_tag'] = condition_node.get('kdoc_tag')
+ check_data['code_must_contain'] = condition_node.get('code_must_contain')
+ elif check_data.get('type') == 'entity_type_validation':
+ check_data['valid_types'] = {t.text for t in check_node.findall(".//ValidEntityTypes/Type")}
+ elif check_data.get('type') == 'relation_validation':
+ check_data['triplet_pattern'] = check_node.findtext("TripletPattern", "")
+ check_data['valid_relations'] = {t.text for t in check_node.findall(".//ValidRelationTypes/Type")}
+ else:
+ check_data['pattern'] = check_node.findtext("Pattern", "")
+ checks.append(check_data)
+ return {"type": def_type, "checks": checks}
+ return None
+
+ def get_rule(self, rule_id: str) -> Optional[Dict[str, Any]]:
+ return self.rules.get(rule_id)
+# [END_ENTITY: Class('SemanticProtocol')]
+
+
+# [ENTITY: Class('CodeValidator')]
+# [RELATION: Class('CodeValidator')] -> [DEPENDS_ON] -> [Class('SemanticProtocol')]
+class CodeValidator:
+ """
+ @summary Применяет правила из протокола к содержимому файла для поиска ошибок.
+ @invariant Всегда работает с валидным и загруженным экземпляром SemanticProtocol.
+ """
+ def __init__(self, protocol: SemanticProtocol):
+ self.protocol = protocol
+
+ def validate(self, file_path: str, content: str, entity_blocks: List[str]) -> List[str]:
+ errors = []
+ rules = self.protocol.rules
+
+ if "AIFriendlyLogging" in rules:
+ errors.extend(self._validate_logging(file_path, content, rules["AIFriendlyLogging"]))
+
+ if "DesignByContract" in rules or "GraphRAG" in rules:
+ for entity_content in entity_blocks:
+ if "DesignByContract" in rules:
+ errors.extend(self._validate_entity_dbc(entity_content, rules["DesignByContract"]))
+ if "GraphRAG" in rules:
+ errors.extend(self._validate_entity_graphrag(entity_content, rules["GraphRAG"]))
+
+ return list(set(errors))
+
+ def _validate_logging(self, file_path: str, content: str, rule: Dict) -> List[str]:
+ errors = []
+ if rule.get('type') != 'multi_check': return []
+ for check in rule['checks']:
+ if check.get('type') == 'negative_regex_in_path' and check.get('path_contains') in file_path and re.search(check['pattern'], content):
+ errors.append(check['failure_message'])
+ elif check.get('type') == 'negative_regex' and re.search(check['pattern'], content):
+ errors.append(check['failure_message'])
+ elif check.get('type') == 'positive_regex_on_match':
+ for line in content.splitlines():
+ if re.search(check['trigger'], line) and not re.search(check['pattern'], line):
+ errors.append(f"{check['failure_message']} [Строка: '{line.strip()}']")
+ return errors
+
+ def _validate_entity_dbc(self, entity_content: str, rule: Dict) -> List[str]:
+ errors = []
+ if rule.get('type') != 'multi_check': return []
+ kdoc_match = re.search(r"(\/\*\*[\s\S]*?\*\/)", entity_content)
+ kdoc = kdoc_match.group(1) if kdoc_match else ""
+ signature_match = re.search(r"\s*(public\s+|private\s+|internal\s+)?(class|interface|fun|object)\s+\w+", entity_content)
+ is_public = not (signature_match and signature_match.group(1) and 'private' in signature_match.group(1)) if signature_match else False
+
+ for check in rule['checks']:
+ if not is_public and check.get('type') != 'block_order': continue # Проверки контрактов только для public
+ if check.get('type') == 'kdoc_validation':
+ is_class = bool(re.search(r"\s*(class|interface|object)", entity_content))
+ if is_class:
+ for tag, _ in check['for_class'].items():
+ if tag not in kdoc: errors.append(f"{check['failure_message']} ({tag})")
+ else: # is_function
+ has_params = bool(re.search(r"fun\s+\w+\s*\((.|\s)*\S(.|\s)*\)", entity_content))
+ returns_value = not bool(re.search(r"fun\s+\w+\(.*\)\s*:\s*Unit", entity_content) or not re.search(r"fun\s+\w+\(.*\)\s*:", entity_content))
+ for tag, cond in check['for_function'].items():
+ if tag not in kdoc and (not cond or (cond == 'has_parameters' and has_params) or (cond == 'returns_value' and returns_value)):
+ errors.append(f"{check['failure_message']} ({tag})")
+ elif check.get('type') == 'contract_enforcement' and check['kdoc_tag'] in kdoc and not re.search(check['code_must_contain'], entity_content):
+ errors.append(check['failure_message'])
+ return errors
+
+ def _validate_entity_graphrag(self, entity_content: str, rule: Dict) -> List[str]:
+ errors = []
+ if rule.get('type') != 'multi_check': return []
+ markup_block_match = re.search(r"^([\s\S]*?)(\/\*\*|class|interface|fun|object)", entity_content)
+ markup_block = markup_block_match.group(1) if markup_block_match else ""
+
+ for check in rule['checks']:
+ if check.get('type') == 'block_order' and "/**" in markup_block:
+ errors.append(check['failure_message'])
+ elif check.get('type') == 'entity_type_validation':
+ entity_match = re.search(r"//\s*\[ENTITY:\s*(?P\w+)\(‘(?P.*?)’\)\]", markup_block)
+ if entity_match and entity_match.group('type') not in check['valid_types']:
+ errors.append(f"{check['failure_message']} Найдено: ‘{entity_match.group('type')}’.")
+ elif check.get('type') == 'relation_validation':
+ for line in re.findall(r"//\s*\[RELATION:.*\]", markup_block):
+ match = re.match(check['triplet_pattern'], line)
+ if not match:
+ errors.append(f"{check['failure_message']} (неверный формат). Строка: ‘{line.strip()}’")
+ elif match.group('relation_type') not in check['valid_relations']:
+ errors.append(f"{check['failure_message']} Найдено: ‘[{match.group('relation_type')}]’.")
+ elif check.get('type') == 'markup_cohesion':
+ for line in markup_block.strip().split('\n'):
+ if line.strip() and not line.strip().startswith('//'):
+ errors.append(check['failure_message']); break
+ return errors
+# [END_ENTITY: Class('CodeValidator')]
+
+
+# [ENTITY: Class('SemanticParser')]
+# [RELATION: Class('SemanticParser')] -> [DEPENDS_ON] -> [Class('SemanticProtocol')]
+# [RELATION: Class('SemanticParser')] -> [CREATES_INSTANCE_OF] -> [Class('CodeValidator')]
+class SemanticParser:
+ """
+ @summary Оркестрирует процесс валидации и парсинга исходных файлов.
+ @invariant Всегда работает с валидным и загруженным экземпляром SemanticProtocol.
+ @sideeffect Читает содержимое файлов, переданных для парсинга.
+ """
+ def __init__(self, protocol: SemanticProtocol):
+ assert isinstance(protocol, SemanticProtocol), "Объект protocol должен быть экземпляром SemanticProtocol."
+ self.protocol = protocol
+ self.validator = CodeValidator(protocol)
+
+ def parse_file(self, file_path: str) -> Dict[str, Any]:
+ logger.info("[INFO][ENTRYPOINT][parsing_file] Начало парсинга файла: '%s'", file_path)
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ content = f.read()
+ except Exception as e:
+ return {"file_path": file_path, "status": "error", "error_message": f"Не удалось прочитать файл: {e}"}
+
+ entity_rule = self.protocol.get_rule("EntityContainerization")
+ entity_blocks = re.findall(entity_rule['start'] + r'[\s\S]*?' + entity_rule['end'], content, re.DOTALL) if entity_rule else []
+
+ validation_errors = self.validator.validate(file_path, content, entity_blocks)
+
+ header_rule = self.protocol.get_rule("FileHeaderIntegrity")
+ if not re.search(header_rule['pattern'], content) if header_rule else None:
+ msg = "Нарушение целостности заголовка (правило FileHeaderIntegrity)."
+ if msg not in validation_errors: validation_errors.append(msg)
+
+ if validation_errors:
+ logger.warn("[WARN][ACTION][validation_failed] Файл %s не прошел валидацию: %s", file_path, " | ".join(validation_errors))
+ return {"file_path": file_path, "status": "error", "error_message": " | ".join(validation_errors)}
+
+ header_match = re.search(header_rule['pattern'], content)
+ header_data = header_match.groupdict()
+ file_info = {
+ "file_path": file_path, "status": "success",
+ "header": {"package": header_data.get('package','').strip(), "file_name": header_data.get('file','').strip(), "semantics_tags": [t.strip() for t in header_data.get('semantics','').split(',')]},
+ "entities": self._extract_entities(content)
+ }
+
+ logger.info("[INFO][POSTCONDITION][parsing_complete] Парсинг файла завершен. Найдено сущностей: %d", len(file_info["entities"]))
+ return file_info
+
+ def _extract_entities(self, content: str) -> List[Dict[str, Any]]:
+ entity_rule = self.protocol.get_rule("EntityContainerization")
+ if not entity_rule: return []
+ entities = []
+ for match in re.finditer(entity_rule['start'] + r'(?P.*?)' + entity_rule['end'], content, re.DOTALL):
+ data = match.groupdict()
+ kdoc = self._parse_kdoc(data.get('body', ''))
+ e_type, e_name = data.get('type', 'N/A'), data.get('name', 'N/A')
+ type_snake = re.sub(r'(? Dict[str, Any]:
+ summary_match = re.search(r"@summary\s*(.*)", body)
+ summary = summary_match.group(1).strip() if summary_match else ""
+ desc_match = re.search(r"@description\s*(.*)", body, re.DOTALL)
+ desc = ""
+ if desc_match:
+ lines = [re.sub(r"^\s*\*\s?", "", l).strip() for l in desc_match.group(1).strip().split('\n')]
+ desc = " ".join(lines)
+ relations = [m.groupdict() for m in re.finditer(r"[RELATION:\s*(?P\w+)\s*target_id='(?P.*?)']", body)]
+ return {"summary": summary, "description": desc, "relations": relations}
+# [END_ENTITY: Class('SemanticParser')]
+
+
+# [ENTITY: Class('ManifestSynchronizer')]
+# [RELATION: Class('ManifestSynchronizer')] -> [DEPENDS_ON] -> [Module('xml.etree.ElementTree')]
+# [RELATION: Class('ManifestSynchronizer')] -> [MODIFIES_STATE_OF] -> [DataStructure('PROJECT_MANIFEST.xml')]
+class ManifestSynchronizer:
+ """
+ @summary Управляет чтением, сравнением и обновлением PROJECT_MANIFEST.xml.
+ @invariant Экземпляр класса всегда работает с корректно загруженным XML-деревом.
+ @sideeffect Читает и может перезаписывать файл манифеста на диске.
+ """
+ def __init__(self, manifest_path: str):
+ """
+ @param manifest_path: Путь к файлу PROJECT_MANIFEST.xml.
+ @sideeffect Читает и парсит XML-файл. Вызывает исключение, если файл не найден или поврежден.
+ """
+ require(os.path.exists(manifest_path), f"Файл манифеста не найден: {manifest_path}")
+ logger.info("[INFO][ENTRYPOINT][manifest_loading] Загрузка манифеста: %s", manifest_path)
+ self.manifest_path = manifest_path
+ try:
+ self.tree = ET.parse(manifest_path)
+ self.root = self.tree.getroot()
+ self.graph_node = self.root.find("PROJECT_GRAPH")
+ if self.graph_node is None:
+ raise ValueError("В манифесте отсутствует тег ")
+ except (ET.ParseError, ValueError) as e:
+ logger.error("[ERROR][ACTION][manifest_parsing_failed] Ошибка парсинга манифеста: %s", e)
+ raise ValueError(f"Ошибка парсинга манифеста: {e}")
+
+ def synchronize(self, parsed_code_data: List[Dict[str, Any]]) -> Dict[str, int]:
+ """
+ @summary Синхронизирует состояние манифеста с состоянием кодовой базы.
+ @param parsed_code_data: Список словарей, представляющих состояние файлов, от SemanticParser.
+ @return Словарь со статистикой изменений.
+ @sideeffect Модифицирует внутреннее XML-дерево.
+ """
+ stats = {"nodes_added": 0, "nodes_updated": 0, "nodes_removed": 0}
+
+ all_code_node_ids = {
+ entity["node_id"]
+ for file_data in parsed_code_data if file_data["status"] == "success"
+ for entity in file_data["entities"]
+ }
+
+ manifest_nodes_map = {node.get("id"): node for node in self.graph_node.findall("NODE")}
+ manifest_node_ids = set(manifest_nodes_map.keys())
+
+ # Удаление узлов, которых больше нет в коде
+ nodes_to_remove = manifest_node_ids - all_code_node_ids
+ for node_id in nodes_to_remove:
+ logger.debug("[DEBUG][ACTION][removing_node] Удаление устаревшего узла: %s", node_id)
+ self.graph_node.remove(manifest_nodes_map[node_id])
+ stats["nodes_removed"] += 1
+
+ # Добавление и обновление узлов
+ for file_data in parsed_code_data:
+ if file_data["status"] != "success":
+ continue
+ for entity in file_data["entities"]:
+ node_id = entity["node_id"]
+ existing_node = manifest_nodes_map.get(node_id)
+
+ if existing_node is None:
+ logger.debug("[DEBUG][ACTION][adding_node] Добавление нового узла: %s", node_id)
+ new_node = ET.SubElement(self.graph_node, "NODE", id=node_id)
+ self._update_node_attributes(new_node, entity, file_data)
+ stats["nodes_added"] += 1
+ else:
+ if self._is_update_needed(existing_node, entity, file_data):
+ logger.debug("[DEBUG][ACTION][updating_node] Обновление узла: %s", node_id)
+ self._update_node_attributes(existing_node, entity, file_data)
+ stats["nodes_updated"] += 1
+
+ logger.info("[INFO][POSTCONDITION][synchronization_complete] Синхронизация завершена. Статистика: %s", stats)
+ return stats
+
+ def _update_node_attributes(self, node: ET.Element, entity: Dict, file_data: Dict):
+ node.set("type", entity["entity_type"])
+ node.set("name", entity["entity_name"])
+ node.set("file_path", file_data["file_path"])
+ node.set("package", file_data["header"]["package"])
+
+ # Очистка и добавление дочерних тегов
+ for child in list(node):
+ node.remove(child)
+
+ ET.SubElement(node, "SUMMARY").text = entity["summary"]
+ ET.SubElement(node, "DESCRIPTION").text = entity["description"]
+ tags_node = ET.SubElement(node, "SEMANTICS_TAGS")
+ tags_node.text = ", ".join(file_data["header"]["semantics_tags"])
+
+ relations_node = ET.SubElement(node, "RELATIONS")
+ for rel in entity["relations"]:
+ ET.SubElement(relations_node, "RELATION", type=rel["type"], target_id=rel["target"])
+
+ def _is_update_needed(self, node: ET.Element, entity: Dict, file_data: Dict) -> bool:
+ # Простая проверка по нескольким ключевым полям
+ if node.get("type") != entity["entity_type"] or node.get("name") != entity["entity_name"]:
+ return True
+ summary_node = node.find("SUMMARY")
+ if summary_node is None or summary_node.text != entity["summary"]:
+ return True
+ return False
+
+ def write_xml(self):
+ """
+ @summary Записывает измененное XML-дерево обратно в файл.
+ @sideeffect Перезаписывает файл манифеста на диске.
+ """
+ require(self.tree is not None, "XML-дерево не было инициализировано.")
+ logger.info("[INFO][ACTION][writing_manifest] Запись изменений в файл манифеста: %s", self.manifest_path)
+ ET.indent(self.tree, space=" ")
+ self.tree.write(self.manifest_path, encoding="utf-8", xml_declaration=True)
+# [END_ENTITY: Class('ManifestSynchronizer')]
+
+
+# [ENTITY: Function('require')]
+def require(condition: bool, message: str):
+ """
+ @summary Проверяет предусловие и вызывает ValueError, если оно ложно.
+ @param condition: Условие для проверки.
+ @param message: Сообщение об ошибке.
+ @sideeffect Вызывает исключение при ложном условии.
+ """
+ if not condition:
+ raise ValueError(message)
+# [END_ENTITY: Function('require')]
+
+
+# [ENTITY: Function('main')]
+# [RELATION: Function('main')] -> [CREATES_INSTANCE_OF] -> [Class('SemanticProtocol')]
+# [RELATION: Function('main')] -> [CREATES_INSTANCE_OF] -> [Class('SemanticParser')]
+# [RELATION: Function('main')] -> [CREATES_INSTANCE_OF] -> [Class('ManifestSynchronizer')]
+def main():
+ """
+ @summary Главная точка входа в приложение.
+ @description Управляет жизненным циклом: парсинг аргументов, настройка логирования,
+ запуск парсинга файлов и синхронизации манифеста.
+ @sideeffect Читает аргументы командной строки, выводит результат в stdout/stderr.
+ """
+ parser = argparse.ArgumentParser(description="Парсит .kt файлы и синхронизирует манифест проекта.")
+ parser.add_argument('files', nargs='+', help="Список .kt файлов для обработки.")
+ parser.add_argument('--protocol', required=True, help="Путь к главному файлу протокола.")
+ parser.add_argument('--manifest-path', required=True, help="Путь к файлу PROJECT_MANIFEST.xml.")
+ parser.add_argument('--update-in-place', action='store_true', help="Если указано, перезаписывает файл манифеста.")
+ parser.add_argument('--log-level', default='INFO', choices=['DEBUG', 'INFO', 'WARN', 'ERROR'], help="Уровень логирования.")
+ args = parser.parse_args()
+
+ logger.setLevel(args.log_level)
+ handler = logging.StreamHandler(sys.stderr)
+ handler.setFormatter(StructuredFormatter())
+ logger.addHandler(handler)
+ logger.info("[INFO][INITIALIZATION][configuring_logger] Логгер настроен. Уровень: %s", args.log_level)
+
+ output_report = {
+ "status": "failure",
+ "manifest_path": args.manifest_path,
+ "files_scanned": len(args.files),
+ "files_with_errors": 0,
+ "changes": {}
+ }
+
+ try:
+ protocol = SemanticProtocol(args.protocol)
+ parser_instance = SemanticParser(protocol)
+
+ parsed_results = [parser_instance.parse_file(f) for f in args.files]
+ output_report["files_with_errors"] = sum(1 for r in parsed_results if r["status"] == "error")
+
+ synchronizer = ManifestSynchronizer(args.manifest_path)
+ change_stats = synchronizer.synchronize(parsed_results)
+ output_report["changes"] = change_stats
+
+ if args.update_in_place:
+ if sum(change_stats.values()) > 0:
+ synchronizer.write_xml()
+ logger.info("[INFO][ACTION][manifest_updated] Манифест был успешно обновлен.")
+ else:
+ logger.info("[INFO][ACTION][manifest_not_updated] Изменений не было, манифест не перезаписан.")
+
+ output_report["status"] = "success"
+
+ except (FileNotFoundError, ValueError, ET.ParseError) as e:
+ logger.critical("[FATAL][EXECUTION][critical_error] Критическая ошибка: %s", e, exc_info=True)
+ output_report["error_message"] = str(e)
+
+ finally:
+ print(json.dumps(output_report, indent=2, ensure_ascii=False))
+ if output_report["status"] == "failure":
+ sys.exit(1)
+
+# [END_ENTITY: Function('main')]
+
+# [CONTRACT]
+if __name__ == "__main__":
+ logger = logging.getLogger(__name__)
+ main()
+# [END_CONTRACT]
+
+# [END_FILE_extract_semantics.py]