Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55ff149c35 | ||
|
|
eff704c675 | ||
|
|
6e9c163047 | ||
|
|
871bde1abd | ||
|
|
5c26bbe8b5 | ||
|
|
3d7df25d00 | ||
|
|
4d589eee95 |
No files matched your search
@@ -2,6 +2,10 @@
|
||||
|
||||
## Не выпущено
|
||||
|
||||
- Все продуктовые требования Акылдаша собраны в одном документе
|
||||
[«Функциональность платформы»](docs/product/platform-functionality.md),
|
||||
включая границы публичного MVP, критерии выпуска и дальнейшее развитие.
|
||||
Заменённые планы и исходное Word-ТЗ доступны в истории Git.
|
||||
- Deployment: подготовлен внешний handoff для Windows-клиента от точного Gitea
|
||||
commit. Bundle фиксирует commit/tree/blob identity и SHA-256; installer
|
||||
проверяет bundle и ACL до записи и не запускает Git. Bootstrap на release A
|
||||
@@ -10,6 +14,12 @@
|
||||
auto-apply config и task выключены. Изолированный PowerShell 5.1 harness для
|
||||
bundle-проверки завершился успешно на точном PR SHA; реальная установка,
|
||||
регистрация задач, SSH и production не выполнялись и не активировались.
|
||||
- Infrastructure: Windows release installer и external receiver читают владельцев
|
||||
и ACL ACE напрямую как SID, без преобразования в имена учётных записей. Это
|
||||
устраняет зависимость проверки от разрешения имён; неизвестные SID остаются
|
||||
видимыми и отклоняются существующими fail-closed guard-политиками. Проверены
|
||||
synthetic unmapped-SID fixtures и read-only ACL preflight; production ACL не
|
||||
менялись.
|
||||
- Документация: объединены продуктовый, UI- и deployment-планы; уточнены
|
||||
внешняя backup-retention политика и обязательный owner-approved maintenance
|
||||
window для cold backup. Передача корпуса требует атомарной публикации
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
# Акылдаш
|
||||
|
||||
Акылдаш — проект юридической информационно-аналитической платформы. Цель —
|
||||
собирать правовые источники на законных основаниях, сохранять их происхождение
|
||||
и версии, готовить данные для поиска и RAG, а затем предоставлять результаты
|
||||
через API и пользовательский интерфейс со ссылками на первоисточники.
|
||||
Акылдаш — проект юридической информационно-аналитической платформы
|
||||
Кыргызской Республики. Целевые возможности и требования к выпуску ведутся в
|
||||
[функциональности платформы](docs/product/platform-functionality.md).
|
||||
|
||||
Telegram-бот — только часть рабочего окружения команды, а не основной продукт.
|
||||
|
||||
@@ -51,11 +50,7 @@ backend/
|
||||
```
|
||||
|
||||
Внутренний frontend отдаётся существующим Search API без отдельного
|
||||
Node-сервиса. Отдельный публичный frontend остаётся за readiness gate в
|
||||
`docs/product/frontend-design-readiness-plan.md`.
|
||||
|
||||
Начать знакомство с проектом: [документация](docs/README.md) и
|
||||
[обзор продукта](docs/product/project-overview.md).
|
||||
Node-сервиса. Остальные материалы собраны в [оглавлении документации](docs/README.md).
|
||||
|
||||
## Конфиденциальные материалы
|
||||
|
||||
|
||||
+4
-11
@@ -4,17 +4,10 @@
|
||||
|
||||
- [Дизайн-система «Түндүк»](../DESIGN.md) — утверждённые палитра,
|
||||
типографика, компоненты и правила адаптивности портала и лаборатории.
|
||||
- [Обзор проекта](product/project-overview.md) — назначение, основные области и
|
||||
правила работы с данными.
|
||||
- [Стратегическое сравнение с Токтом](product/platform-development-plan-toktom-2026-10-24.md) —
|
||||
продуктовая гипотеза и конкурентный анализ; это не план исполнения задач.
|
||||
- [План frontend поисковой СПС](product/frontend-search-sps-plan.md) — границы
|
||||
MVP, зависимости и спринты.
|
||||
- [Готовность к проектированию frontend](product/frontend-design-readiness-plan.md) —
|
||||
обязательные работы и критерии перехода к frontend.
|
||||
- [План реализации и приёмки](product/implementation-roadmap.md) — единая
|
||||
очередность UI и deployment gates, со статусом доказательств; не дублирует
|
||||
продуктовую стратегию или эксплуатационные процедуры.
|
||||
- [Функциональность платформы](product/platform-functionality.md) — единая
|
||||
спецификация возможностей, границы публичного MVP и условия выпуска.
|
||||
- [План реализации и приёмки](product/implementation-roadmap.md) — реестр
|
||||
UI-аудита, технические критерии приёмки и порядок подготовки deployment.
|
||||
- [План интерфейса оценки поисковой выдачи](product/search-relevance-review-interface-plan.md) —
|
||||
внутренняя лаборатория сбора оценок юристов для настройки OpenSearch.
|
||||
- [Разметка relevance set](../backend/search/RELEVANCE_ANNOTATION.md) — подготовка
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# Infrastructure current state
|
||||
|
||||
## Windows ACL SID resolution — 2026-11-03
|
||||
|
||||
- Read-only PowerShell 5.1 inspection found that display-name conversion of
|
||||
ACL entries can throw `IdentityNotMappedException` even though the ACL itself
|
||||
is readable. The installer and external-receiver guards now enumerate access
|
||||
rules directly as `SecurityIdentifier` values and read owners as SIDs; unknown
|
||||
principals remain visible to the existing fail-closed policy. Regression
|
||||
coverage exercises synthetic unmapped SIDs through the actual installer guard
|
||||
functions. These repository changes are pending review; no Windows ACL,
|
||||
installer, task, VPS, or production state was changed. The native PowerShell
|
||||
harness still requires execution on Windows before this behavior is confirmed
|
||||
in that runtime.
|
||||
|
||||
## External deployment runtime binding — 2026-11-03
|
||||
|
||||
- PR #59 is merged to `main`; it contains the root-only read-only status
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
# Готовность к публичному frontend
|
||||
|
||||
Этот документ определяет обязательный результат до начала проектирования и
|
||||
разработки публичной пользовательской системы. Он дополняет
|
||||
[план frontend поисковой СПС](frontend-search-sps-plan.md): тот описывает MVP и
|
||||
спринты frontend, этот — критерий перехода к ним.
|
||||
|
||||
## Правило перехода
|
||||
|
||||
Публичный frontend и его выпуск начинаются, когда выполнены все обязательные
|
||||
пункты этого плана и пройден финальный readiness gate. Внутренняя
|
||||
авторизованная поисковая страница и лаборатория могут развиваться отдельно по
|
||||
явной задаче и используют только реальные существующие API.
|
||||
|
||||
Персональные функции вне этого этапа: закладки, заметки, уведомления, RAG,
|
||||
судебная практика и внешние коммерческие сервисы. Внутренние учетные записи
|
||||
для лаборатории не включают публичную регистрацию.
|
||||
|
||||
## 1. Данные и поисковый индекс
|
||||
|
||||
### Результат
|
||||
|
||||
В локальном OpenSearch доступен воспроизводимо собранный корпус актуальных
|
||||
редакций ЦБД Минюста КР, пригодный для поиска на русском и кыргызском языках.
|
||||
|
||||
### Обязательные работы
|
||||
|
||||
1. Подтвердить для каждого индексируемого документа наличие канонических
|
||||
реквизитов: код, редакция, язык, статус, тип, орган, дата, номер, название
|
||||
и ссылка на официальный источник.
|
||||
2. Зафиксировать правила для документов без текста и одноязычных редакций:
|
||||
они не скрываются и не получают выдуманный перевод.
|
||||
3. Проверить versioned-индекс, mapping, анализаторы RU/KY, полноту актуальных
|
||||
редакций и процедуру безопасной переиндексации с переключением alias.
|
||||
4. Описать и выполнить воспроизводимый сценарий обновления:
|
||||
выгрузка → нормализация → новый индекс → проверка → переключение alias.
|
||||
|
||||
### Критерий приёмки
|
||||
|
||||
Команда может повторить обновление на чистом локальном окружении, проверить
|
||||
количество документов и фрагментов, а затем безопасно переключить поисковый
|
||||
alias без смешивания старого и нового корпуса.
|
||||
|
||||
## 2. Relevance set и качество поиска
|
||||
|
||||
### Результат
|
||||
|
||||
Есть замороженный набор `relevance set v1` из 50 практических запросов:
|
||||
минимум 25 на русском и 25 на кыргызском языках.
|
||||
|
||||
### Обязательные работы
|
||||
|
||||
1. Заполнить рабочую копию из `backend/search/relevance-set-v1.template.json`
|
||||
естественными формулировками пользователей и подтверждёнными
|
||||
`document_code` релевантных действующих актов.
|
||||
2. Подтвердить документы по официальной ЦБД и проверить их наличие в локальном
|
||||
индексе по `document_code`, не оценивая порядок поисковой выдачи. Не включать
|
||||
запросы без установленного эталонного результата.
|
||||
3. Провести независимую вторую проверку языка, формулировки и полного списка
|
||||
кодов. Спорные результаты не включать до согласования.
|
||||
4. После независимой проверки сохранить неизменяемую копию набора, затем
|
||||
оценить собственную выдачу и сохранить baseline `Recall@10`/`MRR@10`. Новые
|
||||
правила ранжирования проверять на том же наборе и сравнивать с baseline.
|
||||
5. Отдельно проверить распространённые сокращения, опечатки и запросы о
|
||||
практическом действии. Правило принимается, только если улучшает
|
||||
подтверждённые запросы и не создаёт ложных срабатываний в негативных
|
||||
сценариях.
|
||||
|
||||
### Критерий приёмки
|
||||
|
||||
Оценщик проходит на полном наборе, выводит метрики и выдачу для каждого
|
||||
запроса; baseline и результаты его повторного запуска воспроизводимы.
|
||||
|
||||
## 3. Справочники и контракт API
|
||||
|
||||
### Результат
|
||||
|
||||
Frontend получает все юридически значимые данные через версионированный
|
||||
OpenAPI-контракт, а не реконструирует их из текста фрагментов.
|
||||
|
||||
### Обязательные работы
|
||||
|
||||
1. Утвердить справочники v1: типы документов, органы принятия, статусы,
|
||||
уровни действия и темы. Для каждого значения определить стабильный код и
|
||||
подписи RU/KY.
|
||||
2. Реализовать и описать OpenAPI для:
|
||||
- `GET /search` — запрос, язык, фильтры, сортировка, серверная пагинация,
|
||||
выдержка и подсветка;
|
||||
- `GET /search/filters` — допустимые значения фильтров;
|
||||
- `GET /documents/{code}` — карточка актуального документа;
|
||||
- `GET /documents/{code}/editions` и
|
||||
`GET /documents/{code}/editions/{edition}` — редакции и их содержимое.
|
||||
3. Зафиксировать единые ответы для пустой выдачи, неизвестного документа,
|
||||
недоступной редакции и ошибки upstream; для документов с одним языком
|
||||
вернуть доступные языки явно.
|
||||
4. Добавить контрактные и интеграционные проверки API на локальном OpenSearch:
|
||||
поиск, фильтры, пагинация, сортировка, документ, редакции и одноязычные
|
||||
акты.
|
||||
|
||||
### Критерий приёмки
|
||||
|
||||
OpenAPI опубликован вместе с backend, тестовый клиент получает реальные данные
|
||||
по всем endpoint без mock-слоя, а результаты поиска открывают актуальный
|
||||
документ и выбранную редакцию.
|
||||
|
||||
## 4. Финальный readiness gate
|
||||
|
||||
Перед началом frontend выполнить и зафиксировать один сквозной сценарий:
|
||||
|
||||
1. Обновить корпус из официальной ЦБД.
|
||||
2. Нормализовать данные и собрать новый versioned-индекс.
|
||||
3. Прогнать проверки индекса и relevance baseline.
|
||||
4. Переключить alias на проверенный индекс.
|
||||
5. Выполнить API-сценарии поиска, фильтрации, просмотра документа и редакции
|
||||
на RU, KY и одноязычном документе.
|
||||
|
||||
Gate считается пройденным, если все проверки успешны, зафиксированы версии
|
||||
backend и индекса, опубликованы известные ограничения и назначен ответственный
|
||||
за юридическую проверку relevance set.
|
||||
|
||||
## Порядок issues
|
||||
|
||||
1. Собрать и независимо проверить relevance set v1.
|
||||
2. Завершить проверку полноты данных и воспроизводимую переиндексацию с alias.
|
||||
3. Утвердить справочники v1.
|
||||
4. Реализовать OpenAPI и интеграционные проверки.
|
||||
5. Провести финальный readiness gate и только затем открыть задачу на
|
||||
проектирование frontend.
|
||||
@@ -1,265 +0,0 @@
|
||||
# План реализации frontend поисковой СПС
|
||||
|
||||
Основание: `docs/Функциональные_возможности_поисковой_СПС.docx`.
|
||||
|
||||
Документ с требованиями описывает не только frontend, но и поиск, юридическую
|
||||
обработку, персональные данные, уведомления и внешние источники. Поэтому
|
||||
frontend следует начинать после появления нормализованной базы, поискового API
|
||||
и API документов.
|
||||
|
||||
Требования необходимо адаптировать под Кыргызскую Республику: в исходном
|
||||
документе используются примеры ТК РФ и деление
|
||||
«федеральный/региональный/муниципальный», которое нельзя переносить без
|
||||
изменений.
|
||||
|
||||
## Задачи до начала frontend-разработки
|
||||
|
||||
### Обязательные для MVP
|
||||
|
||||
1. Нормализовать архив Минюста:
|
||||
- очистить HTML;
|
||||
- выделить структуру документа и редакций;
|
||||
- унифицировать статусы, органы, виды документов и даты;
|
||||
- сохранить ссылки на официальный источник и дату получения;
|
||||
- определить правила отображения документов без текста.
|
||||
2. Подготовить backend API:
|
||||
- `GET /search`;
|
||||
- `GET /search/filters`;
|
||||
- `GET /documents/{code}`;
|
||||
- `GET /documents/{code}/editions`;
|
||||
- `GET /documents/{code}/editions/{edition}`;
|
||||
- описание API в OpenAPI;
|
||||
- серверную пагинацию, фильтрацию и сортировку.
|
||||
3. Развернуть поисковый сервис. Рекомендуемый вариант — self-hosted OpenSearch:
|
||||
- отдельные поля и анализаторы для русского и кыргызского текстов;
|
||||
- русский морфологический анализ;
|
||||
- ICU-нормализация кыргызского текста;
|
||||
- словари синонимов и сокращений;
|
||||
- подсветка совпадений;
|
||||
- индексирование всех редакций.
|
||||
4. Подготовить тестовый набор из 50–100 реальных запросов на русском и
|
||||
кыргызском языках и вручную отметить ожидаемые результаты.
|
||||
5. Утвердить справочники:
|
||||
- виды документов;
|
||||
- органы принятия;
|
||||
- статусы;
|
||||
- уровни действия;
|
||||
- тематический классификатор первой версии.
|
||||
|
||||
OpenSearch имеет встроенный
|
||||
[русский морфологический анализатор](https://docs.opensearch.org/latest/analyzers/language-analyzers/russian/),
|
||||
поддерживает [синонимы и нечёткий поиск](https://docs.opensearch.org/latest/query-dsl/full-text/match/)
|
||||
и [подсветку результатов](https://docs.opensearch.org/latest/search-plugins/searching-data/highlight).
|
||||
Встроенного кыргызского морфологического анализатора в перечне нет, поэтому
|
||||
нужно отдельно проверить ICU и словари на реальных запросах.
|
||||
[ICU-анализатор](https://docs.opensearch.org/latest/analyzers/language-analyzers/icu/)
|
||||
обеспечивает Unicode-нормализацию, но сам по себе не гарантирует кыргызскую
|
||||
морфологию.
|
||||
|
||||
### Сторонние сервисы, не нужные для MVP
|
||||
|
||||
Их не следует подключать заранее:
|
||||
|
||||
- Keycloak или другой OIDC-провайдер — перед закладками, папками и ролями;
|
||||
- SMTP, Telegram или Web Push — перед «документами на контроле»;
|
||||
- LibreOffice или Gotenberg — перед экспортом в Word, PDF и RTF;
|
||||
- поставщики судебной практики и экспертных комментариев — после проверки
|
||||
лицензий;
|
||||
- источники курсов, календарей и справочных данных — перед соответствующим
|
||||
разделом;
|
||||
- Sentry или аналог — опционально перед публичным запуском.
|
||||
|
||||
## Граница MVP
|
||||
|
||||
MVP — публичная справочно-поисковая система без регистрации и персональных
|
||||
функций.
|
||||
|
||||
В MVP входят:
|
||||
|
||||
- интерфейс на русском и кыргызском языках;
|
||||
- строка полнотекстового поиска;
|
||||
- исправление распространённых опечаток;
|
||||
- базовые синонимы и сокращения;
|
||||
- список результатов с подсвеченными фрагментами;
|
||||
- фильтры по языку, виду документа, органу, статусу и дате;
|
||||
- сортировка по релевантности и дате;
|
||||
- пагинация;
|
||||
- карточка документа;
|
||||
- актуальная редакция, статус и дата актуальности;
|
||||
- переключение между доступными языками;
|
||||
- поиск внутри открытого документа;
|
||||
- список редакций и открытие выбранной редакции;
|
||||
- ссылка на официальный источник и сведения о происхождении данных;
|
||||
- адаптивность, доступность, состояния загрузки и ошибок.
|
||||
|
||||
Сравнение редакций, аккаунты, заметки, уведомления, RAG и судебная практика в
|
||||
MVP не входят.
|
||||
|
||||
Интерфейс не должен предполагать наличие обоих языков. На момент полного
|
||||
скачивания архива распределение следующее:
|
||||
|
||||
- только русский язык — 29 433 документа;
|
||||
- только кыргызский язык — 98 905 документов;
|
||||
- оба языка — 80 930 документов;
|
||||
- нет HTML-текста — 690 документов.
|
||||
|
||||
## Рекомендуемая основа frontend
|
||||
|
||||
Для внутреннего frontend лаборатории и авторизованного поиска используется
|
||||
одна HTML-страница, которую отдаёт существующий Search API, и браузерный
|
||||
JavaScript без сборщика и runtime-зависимостей. Это сохраняет текущую схему
|
||||
развёртывания. Публичный frontend после readiness gate можно оценить отдельно.
|
||||
|
||||
- Next.js App Router и TypeScript;
|
||||
- CSS Modules с BEM-именованием;
|
||||
- дизайн-токены для цветов, отступов, типографики и состояний;
|
||||
- серверный `fetch` и URL-параметры вместо отдельного глобального хранилища;
|
||||
- Playwright для основных пользовательских сценариев;
|
||||
- адаптивный web-интерфейс без отдельного мобильного приложения.
|
||||
|
||||
Next.js App Router поддерживает серверные компоненты, маршрутизацию и
|
||||
TypeScript в стандартной конфигурации. См.
|
||||
[официальную документацию](https://nextjs.org/docs/app).
|
||||
|
||||
## Дизайн-процесс и внешние ориентиры
|
||||
|
||||
При проектировании и проверке интерфейса используются следующие источники:
|
||||
|
||||
- [jakubkrehel/skills](https://github.com/jakubkrehel/skills) — обязательная
|
||||
комплексная проверка интерфейса через `better-interface`, включая UI,
|
||||
типографику, цвета, доступность, layout и тексты;
|
||||
- [UI Skills](https://www.ui-skills.com/) — каталог практик и узких skills,
|
||||
которые подключаются только под конкретную задачу после проверки их
|
||||
содержания и лицензии;
|
||||
- [Refero Styles](https://styles.refero.design/) — библиотека визуальных
|
||||
направлений и примеров `DESIGN.md` для поиска референсов.
|
||||
|
||||
Правила применения:
|
||||
|
||||
1. Визуальное направление «Түндүк» утверждено пользователем. Применять
|
||||
корневой [DESIGN.md](../../DESIGN.md); повторный выбор направления не нужен.
|
||||
2. Не копировать чужую дизайн-систему целиком. Цвета, типографика, плотность и
|
||||
компоненты должны учитывать длинные юридические тексты, два языка и
|
||||
доступность.
|
||||
3. Перенести значения из корневого `DESIGN.md` в дизайн-токены проекта.
|
||||
Не создавать отдельный `frontend/DESIGN.md` или конкурирующие спецификации.
|
||||
4. Дизайн-токены и компоненты Акылдаша являются источником истины. Внешние
|
||||
рекомендации не могут отменять BEM, доступность, требования безопасности и
|
||||
продуктовые ограничения проекта.
|
||||
5. Каждый завершённый пользовательский сценарий проходит `better-interface`
|
||||
review. Перед выпуском MVP выполняется полный review поиска, фильтров и
|
||||
просмотра документа.
|
||||
6. UI Skills используется для точечного поиска решения, а не для одновременного
|
||||
смешивания нескольких визуальных стилей.
|
||||
|
||||
Эти ресурсы используются на этапе проектирования и review и не являются
|
||||
runtime-зависимостями frontend. Регистрация в стороннем SaaS для MVP не нужна.
|
||||
|
||||
## План спринтов MVP
|
||||
|
||||
### Спринт 0 — фундамент, 1 неделя
|
||||
|
||||
- создать `frontend/`;
|
||||
- настроить Next.js, TypeScript, lint и сборку;
|
||||
- применить утверждённую дизайн-систему «Түндүк» из корневого `DESIGN.md`;
|
||||
- установить полный набор `jakubkrehel/skills` для проектных design review;
|
||||
- определить маршруты и типы API;
|
||||
- создать дизайн-токены;
|
||||
- реализовать базовые компоненты: кнопка, поле, селект, статус, карточка,
|
||||
пагинация;
|
||||
- создать общий layout и двуязычную навигацию;
|
||||
- добавить footer с версиями frontend и backend;
|
||||
- подготовить макеты поиска, результатов и документа;
|
||||
- провести первый `better-interface` review макетов;
|
||||
- настроить CI.
|
||||
|
||||
Результат: интерфейсный каркас работает на mock-ответах API.
|
||||
|
||||
### Спринт 1 — быстрый поиск, 2 недели
|
||||
|
||||
- главная страница с поиском;
|
||||
- интеграция с `/search`;
|
||||
- список результатов;
|
||||
- подсветка совпадений;
|
||||
- URL, которым можно поделиться;
|
||||
- переключение RU/KY;
|
||||
- состояния загрузки, отсутствия результатов и ошибки API;
|
||||
- базовая мобильная версия.
|
||||
|
||||
Результат: пользователь может найти документ и открыть результат.
|
||||
|
||||
### Спринт 2 — точный отбор, 2 недели
|
||||
|
||||
- фильтры по реквизитам;
|
||||
- сортировка;
|
||||
- пагинация;
|
||||
- отображение числа результатов;
|
||||
- сброс отдельных и всех фильтров;
|
||||
- сохранение состояния в URL;
|
||||
- доступное управление с клавиатуры;
|
||||
- адаптивная панель фильтров.
|
||||
|
||||
Результат: поддерживается быстрый и реквизитный поиск.
|
||||
|
||||
### Спринт 3 — просмотр документа, 2 недели
|
||||
|
||||
- заголовок, реквизиты, статус и дата актуальности;
|
||||
- безопасное отображение очищенного HTML;
|
||||
- переключение языка;
|
||||
- поиск внутри документа;
|
||||
- навигация по найденным фрагментам;
|
||||
- список редакций;
|
||||
- открытие предыдущей редакции;
|
||||
- ссылка на ЦБД Минюста;
|
||||
- печать средствами браузера.
|
||||
|
||||
Результат: пользователь может проверить текст и его происхождение.
|
||||
|
||||
### Спринт 4 — стабилизация и выпуск, 2 недели
|
||||
|
||||
- сквозные тесты поиска и просмотра;
|
||||
- проверка русских, кыргызских и одноязычных документов;
|
||||
- соответствие WCAG 2.2 AA;
|
||||
- защита от внедрения небезопасного HTML;
|
||||
- проверка производительности;
|
||||
- корректные метаданные страниц;
|
||||
- обработка недоступности API;
|
||||
- production-сборка и развёртывание;
|
||||
- пользовательское тестирование на 10–15 реальных юридических задачах.
|
||||
|
||||
Результат: публичный MVP.
|
||||
|
||||
Оценка frontend-части после готовности API: **9 недель**.
|
||||
|
||||
## Спринты после MVP
|
||||
|
||||
### Спринт 5 — персональные функции
|
||||
|
||||
Базовый вход и смена пароля для внутреннего frontend уже реализованы. Для
|
||||
публичного продукта остаются закладки, заметки, подборки и сохранённые фильтры.
|
||||
|
||||
### Спринт 6 — контроль изменений
|
||||
|
||||
Документы на контроле, подписки на редакции и уведомления.
|
||||
|
||||
### Спринт 7 — юридические связи
|
||||
|
||||
Сравнение редакций, прямые и обратные ссылки, утратившие силу фрагменты.
|
||||
|
||||
### Спринт 8 — практические материалы
|
||||
|
||||
Формы, образцы, инструкции, чек-листы, календари и справочные данные.
|
||||
|
||||
### Спринт 9 — расширенный анализ
|
||||
|
||||
Судебная практика, экспертные комментарии, дерево связей и RAG с обязательными
|
||||
ссылками на источники.
|
||||
|
||||
### Спринт 10 — корпоративные функции
|
||||
|
||||
Роли, журналирование, API, интеграция с СЭД, расширенный экспорт и
|
||||
персонализация.
|
||||
|
||||
---
|
||||
|
||||
Акылдаш · Telegram-бот v0.2.2 · Backend v0.12.2 · Frontend v0.1.2
|
||||
@@ -1,9 +1,9 @@
|
||||
# План реализации и приёмки Акылдаш
|
||||
|
||||
Дата актуализации: **2 ноября 2026 года**. Основа сверки: `main` на
|
||||
`7ece39f212b67e497a8dfb360d5d271ab088ef57`. Это единый план работ по продукту,
|
||||
интерфейсу и переходу к production. Он не заменяет эксплуатационные runbooks
|
||||
и не подтверждает состояние живого сервиса.
|
||||
Основа аудита: `main` на `7ece39f212b67e497a8dfb360d5d271ab088ef57`
|
||||
от 2 ноября 2026 года. Документ хранит результаты UI-аудита, критерии
|
||||
интерфейсной приёмки и порядок подготовки production. Целевые возможности
|
||||
и условия публичного MVP ведутся в [функциональности платформы](platform-functionality.md).
|
||||
|
||||
## 1. Статус и границы достоверности
|
||||
|
||||
@@ -43,24 +43,7 @@ deploy-кода. Подтверждённое состояние и ограни
|
||||
|
||||
## 2. Очерёдность работ
|
||||
|
||||
### 2.1 Продукт и качество правовых данных
|
||||
|
||||
1. Поддерживать воспроизводимое получение и нормализацию официальных
|
||||
правовых материалов с сохранением источника, редакции и контрольных сумм.
|
||||
2. Подтвердить полноту и актуальность RU/KY корпуса, правила для отсутствующих
|
||||
текстов и одноязычных редакций; не выдумывать перевод или реквизиты.
|
||||
3. Развивать замороженный relevance set и измерять качество на одной и той же
|
||||
выборке до и после изменений ранжирования.
|
||||
4. Развивать версионируемые API и справочники, сохраняя совместимость
|
||||
существующих клиентов.
|
||||
5. Публичный frontend, персональные функции и RAG остаются отдельными
|
||||
продуктовыми решениями и readiness gates. Сверяться с [обзором проекта](project-overview.md),
|
||||
[планом frontend](frontend-search-sps-plan.md) и
|
||||
[готовностью публичного frontend](frontend-design-readiness-plan.md). Конкурентная
|
||||
гипотеза и сегмент рынка отдельно описаны в
|
||||
[стратегическом сравнении с Токтом](platform-development-plan-toktom-2026-10-24.md).
|
||||
|
||||
### 2.2 Интерфейс: датированный реестр UI-01—UI-33
|
||||
### 2.1 Интерфейс: датированный реестр UI-01—UI-33
|
||||
|
||||
Реестр ниже сохраняет исходные наблюдения аудита **23 октября 2026 года** и
|
||||
критерии приёмки; это не список текущих открытых дефектов. Актуальный план на
|
||||
@@ -241,6 +224,13 @@ source footer и версия кода — `0.3.3`. Его отметка «ож
|
||||
относилась к конкретному pushed SHA и не задаёт статус этого или любого нового
|
||||
PR.
|
||||
|
||||
#### Проверка сценариев публичного MVP
|
||||
|
||||
Каждый завершённый пользовательский сценарий проходит проверку
|
||||
через `better-interface` с оценкой интерфейса, типографики, цвета,
|
||||
доступности, компоновки и текстов. Перед выпуском публичного MVP провести
|
||||
итоговую комплексную проверку поиска, фильтров и просмотра документа.
|
||||
|
||||
#### Общие критерии окон и форм
|
||||
|
||||
- Desktop-окно обычно 480–640 px и не шире viewport; на mobile — отступ 16 px
|
||||
@@ -312,7 +302,7 @@ PR.
|
||||
орнамент в свободную область без влияния на управление, либо получить явное
|
||||
одобрение на изменение `DESIGN.md` и подтвердить его визуальной приёмкой.
|
||||
|
||||
### 2.3 Инфраструктура и переход к production
|
||||
### 2.2 Инфраструктура и переход к production
|
||||
|
||||
Инфраструктурные источники содержали повторяющиеся этапы deployment и
|
||||
устаревшие снимки VPS. В этом плане оставлены только gates; актуальные
|
||||
|
||||
@@ -1,307 +0,0 @@
|
||||
# План развития «Акылдаш»: конкурентный анализ «Токтом Онлайн»
|
||||
|
||||
Дата: 24 октября 2026 года. Статус: предложение для продуктового решения;
|
||||
не утверждённый бюджет или срок выпуска. Изменения приложения этим документом
|
||||
не выполняются.
|
||||
|
||||
## 1. Вывод и оценка шансов
|
||||
|
||||
**На текущем этапе «Акылдаш» не готов заменить профессиональную СПС целиком.
|
||||
Шанс построить востребованный продукт есть, если начать с ограниченного
|
||||
практического сегмента и доказать качество данных и экономию времени.**
|
||||
|
||||
Копирование всей широты конкурента потребует не только разработки, но и
|
||||
постоянной юридической редакции, получения материалов, сопровождения клиентов
|
||||
и продаж. Рекомендуемая стартовая гипотеза — поиск и контроль норм для
|
||||
небольших юридических и бухгалтерских команд, работающих с ОсОО/ЖЧК,
|
||||
налогообложением и трудовыми вопросами. Наличие отдельных поисковых улучшений
|
||||
по ОсОО и налогам делает этот сегмент удобным для проверки, но само по себе
|
||||
не подтверждает спрос.
|
||||
|
||||
| Сценарий | Оценка | Что должно измениться |
|
||||
| --- | --- | --- |
|
||||
| Быстрая полная замена привычной СПС | Низкие шансы сейчас | Нужны надёжный выпуск, полнота материалов и профессиональные инструменты |
|
||||
| Дополнительный инструмент для одной практической задачи | Наиболее реалистичная гипотеза | Пилот должен показать измеримую пользу без обязательного отказа от прежней системы |
|
||||
| Основное рабочее место небольшой команды | Условно достижимо после пилота | Нужны удержание, обновления, персональная работа и подтверждённая готовность платить |
|
||||
| Массовая платформа с ИИ-консультантом | Долгосрочная гипотеза | Нужны проверяемые ответы, экономика обслуживания и контроль юридических ошибок |
|
||||
|
||||
Проценты вероятности успеха не рассчитываются: нет данных о числе клиентов
|
||||
конкурента, продажах, удержании, нашей команде, бюджете и стоимости привлечения.
|
||||
Цены, скорость и качество поиска систем в одинаковых условиях не сравнивались.
|
||||
Техническая возможность создать функцию не доказывает коммерческое преимущество.
|
||||
|
||||
## 2. Основание и ограничения исследования
|
||||
|
||||
Изучено открытое [руководство «Токтом Онлайн»](https://online.toktom.kg/cms/Topic/273)
|
||||
целиком: портал, учётные записи, продукты, поиск и работа с документами.
|
||||
В таблице ниже номера означают разделы этого руководства. Описание функции
|
||||
подтверждает её документирование, но не результат испытания платной системы.
|
||||
Дата последнего обновления руководства не установлена; старый год в подвале
|
||||
не позволяет определить актуальность отдельных инструкций.
|
||||
|
||||
Отдельная [страница продукта](https://online.toktom.kg/cms/Topic/292)
|
||||
заявляет ежедневное обновление и работу через интернет с синхронизацией
|
||||
персональных данных. Это заявления поставщика; их фактическое выполнение
|
||||
не проверялось. Актуальный тариф на этой странице предлагается запрашивать.
|
||||
|
||||
База сравнения «Акылдаш»: актуальная на повторной сверке `origin/main`, commit
|
||||
`c8d0086a9f20485c5efe6f9d3ccd476dd9f07065` (2 ноября 2026 года). Матрица
|
||||
G01–G25, описание текущих возможностей в разделе 3 и этапы A–F в разделе 6
|
||||
повторно сверены с этой базой. Проверены:
|
||||
|
||||
- [README](../../README.md), [обзор продукта](project-overview.md);
|
||||
- [API](../../backend/search/api.py), [поисковый интерфейс](../../backend/search/review.html),
|
||||
[учётные записи](../../backend/search/auth.py), [запросы](../../backend/search/query.py);
|
||||
- [каталоги](search-catalog-v1.md), [план frontend](frontend-search-sps-plan.md),
|
||||
[readiness gate](frontend-design-readiness-plan.md);
|
||||
- [состояние инфраструктуры и корпуса](../operations/CURRENT_STATE.md),
|
||||
[доступ](../operations/ACCESS.md), а также актуальные реализации Search API,
|
||||
учётных записей и поискового интерфейса.
|
||||
|
||||
Матрица отражает возможности реализации на указанном commit, а не только
|
||||
состояние прежней базы исследования. В частности, в текущем внутреннем контуре
|
||||
администратор может создавать и удалять учётные записи и сбрасывать пароль;
|
||||
публичная регистрация и восстановление по email по-прежнему отсутствуют.
|
||||
Технические версии кода: Backend 0.13.3 (`APP_VERSION` в
|
||||
`backend/search/minjust_opensearch.py`), Frontend 0.3.3 (подвал
|
||||
`backend/search/review.html`), Telegram-бот 0.2.2 (`APP_VERSION` в
|
||||
`tools/telegram-bot/bot.py`). Корневой README указывает Backend 0.13.2;
|
||||
`backend/README.md` содержит более старые Backend 0.13.0 / Frontend 0.2.3.
|
||||
При расхождении использованы версия backend и подвал интерфейса в указанном
|
||||
commit. Эти версии не подтверждают развёртывание соответствующего кода.
|
||||
Живой production, полнота локального корпуса и пользовательская доступность
|
||||
не подтверждены текущими проверками. Нельзя считать функцию доступной клиентам
|
||||
только на основании наличия кода.
|
||||
|
||||
## 3. Что уже есть у нас
|
||||
|
||||
В реализации есть полнотекстовый поиск RU/KY, фильтры по виду, статусу, группе
|
||||
органа, юридической силе и дате; пагинация и сортировка по релевантности/дате.
|
||||
Есть API карточки и редакций, чтение выбранной редакции и языка, оглавление,
|
||||
поиск внутри текста, печать и переход на официальный источник.
|
||||
Это **не нужно разрабатывать заново**.
|
||||
|
||||
Учётные записи SQLite, проверка полномочий на сервере,
|
||||
смена пароля и административные создание, удаление и сброс пароля доступны
|
||||
во внутреннем контуре. Лаборатория собирает оценки выдачи и комментарии
|
||||
проверяющих. Это не публичная регистрация, не восстановление пароля по email,
|
||||
не пользовательские заметки к нормам и не история исследований между устройствами.
|
||||
|
||||
По состоянию, записанному в `CURRENT_STATE.md`, публичный запуск и готовность
|
||||
всего корпуса не подтверждены. В документе есть исторические проверки архива
|
||||
и ограниченного staging; успешная обработка одного документа не доказывает
|
||||
полноту и актуальность базы. RAG в README отмечен как ещё не созданный.
|
||||
|
||||
## 4. Функциональные пробелы
|
||||
|
||||
Обозначения: **нет** — в проверенных API, UI и профильных планах отсутствует
|
||||
готовая возможность; **частично** — есть основа, но сценарий неполон;
|
||||
**не подтверждено** — без аудита корпуса или рабочего сервиса вывод невозможен.
|
||||
«Нет» относится к реализации указанного commit, а не ко всем внешним наработкам.
|
||||
P0 — до публичного пилота; P1 — основное рабочее место; P2 — после спроса;
|
||||
P3 — только при отдельном обосновании.
|
||||
|
||||
| ID | Возможность в руководстве «Токтом» | Состояние «Акылдаш» и необходимое дополнение | Приоритет |
|
||||
| --- | --- | --- | --- |
|
||||
| G01 | Регистрация, восстановление, соцвход (§1.1–1.2) | Частично: внутренние аккаунты есть; публичный onboarding и восстановление по email отсутствуют. Соцвход отложить | P1 / P3 |
|
||||
| G02 | Организация и профиль (§3.1.1) | Частично: есть кабинет, но нет полноценной модели организаций | P2 |
|
||||
| G03 | Счёт, платежи (§3.1.2) | Нет: коммерческий учёт и сверка оплат | P2 |
|
||||
| G04 | Продукты, сроки, промокоды (§3.2.1–3) | Нет: тарифные права и жизненный цикл подписки | P2 |
|
||||
| G05 | Подключение коллег (§3.2.4–5) | Нет: приглашения и права внутри организации; внутренние роли не заменяют это | P2 |
|
||||
| G06 | Настройки выдачи (§3.2.6) | Частично: параметры доступны, серверного сохранения предпочтений нет | P1 |
|
||||
| G07 | Тематические разделы (§3.3.1) | Частично: черновая таксономия есть, публичный тематический фильтр отсутствует | P1 |
|
||||
| G08 | Судебные, справочные, зарубежные материалы (§3.3.1) | Не подтверждено: специализированные полные коллекции и их поиск не показаны; наличие отдельных актов в ЦБД не исключается | P2 / P3 |
|
||||
| G09 | Новые документы (§3.3.3) | Нет отдельной пользовательской ленты поступлений | P1 |
|
||||
| G10 | Избранное, изменения (§3.3.4) | Нет серверного списка и пользовательского контроля изменений | P1 |
|
||||
| G11 | История документов/запросов (§3.3.5) | Нет синхронизированной истории исследования; URL браузера и история входов — другие данные | P1 |
|
||||
| G12 | Обзор обновлений (§3.3.6) | Нет журнала новых, изменённых и отменённых актов для пользователя | P0 / P1 |
|
||||
| G13 | Открытие по коду (§3.4.2) | Частично: API принимает код, отдельного пользовательского сценария нет | P1 |
|
||||
| G14 | Расширенные реквизиты (§3.4.3) | Частично: нужны номер, раздельные поля названия/текста, тематический отбор, множественные значения и поиск без обязательного q | P1 |
|
||||
| G15 | Детальные органы, публикация (§3.4.3.1) | Частично: API ограничен группами органов; нет полного реестра и фильтров по регистрации/источнику публикации | P2 |
|
||||
| G16 | Шаблоны запросов (§3.4.3.2) | Нет именованных сохранённых поисков | P1 |
|
||||
| G17 | Сортировка по органу/виду (§3.5.3) | Нет таких режимов и выбора направления; текущая дата имеет дополнительный приоритет статуса | P2 |
|
||||
| G18 | Word: выдача/документ (§3.5.4; 3.6.6) | Нет DOCX-экспорта; браузерная печать уже есть | P1 |
|
||||
| G19 | Справка и реквизиты (§3.6.1) | Частично: метаданные есть, полная читаемая паспортная карточка требует доработки | P1 |
|
||||
| G20 | Связи, обратные ссылки (§3.6.7–8) | Нет собственного проверенного графа связей и обратного индекса; исходные HTML-ссылки не равнозначны | P1 |
|
||||
| G21 | Предпросмотр, «Связной» (§3.6.9; 13) | Нет контекстного просмотра и связей на уровне конкретной нормы | P2 |
|
||||
| G22 | Графические копии (§3.6.10) | Частично на уровне архива: готовая выдача оригиналов не подтверждена; вложения staging не верифицированы | P2 |
|
||||
| G23 | Сравнение редакций (§3.6.12) | Нет сопоставления двух текстов и выделения изменений; переключатель редакций уже есть | P1 |
|
||||
| G24 | Языковое сравнение (§3.6.14) | Нет параллельного просмотра; переключение RU/KY уже есть | P1 |
|
||||
| G25 | Форум, сообщения (§2; 3.2.7–8) | Нет пользовательского сообщества; рабочий Telegram-бот команды его не заменяет | P3 |
|
||||
|
||||
Редакции, языки, оглавление и печать (§3.6.2–5) в перечень отсутствующих
|
||||
функций не включены. Для сравнительного списка использовано
|
||||
[руководство поставщика](https://online.toktom.kg/cms/Topic/273).
|
||||
|
||||
## 5. Стратегия отличия
|
||||
|
||||
Это гипотезы развития, а не установленные преимущества над конкурентом.
|
||||
Отсутствие описания в его руководстве не доказывает отсутствия функции.
|
||||
|
||||
1. **Проверяемая актуальность.** На каждой норме показывать источник, редакцию,
|
||||
время последней успешной проверки и ограничения данных. Разделять дату
|
||||
получения, принятия, вступления в силу и период применимости.
|
||||
2. **Практический двуязычный поиск.** Измерять RU и KY отдельно на реальных
|
||||
задачах, сокращениях и опечатках. Не обещать кыргызскую морфологию только
|
||||
из-за наличия ICU. Не подменять отсутствующий официальный текст переводом.
|
||||
3. **Рабочая задача вместо набора документов.** Проверить сценарий
|
||||
«найти применимую норму → проверить редакцию → сохранить доказательство →
|
||||
получить сигнал об изменении». Он должен занимать меньше времени у пилота.
|
||||
4. **Объяснение с проверяемыми ссылками.** После надёжного поиска добавить
|
||||
ответы с точными цитатами и открытием фрагмента. При недостатке источников
|
||||
система сообщает об этом; юридические выводы требуют проверки специалистом.
|
||||
|
||||
Удобный интерфейс и низкая цена сами по себе не обеспечат переход клиентов.
|
||||
Открытые источники также не дают автоматического разрешения на любой способ
|
||||
их использования. Права на каждый новый источник проверяются отдельно;
|
||||
копирование базы и редакционных материалов конкурента в план не входит.
|
||||
|
||||
## 6. Последовательность развития и приёмка
|
||||
|
||||
Сроки ниже — предварительные диапазоны для команды из двух разработчиков
|
||||
с регулярным участием юриста и частичным участием дизайнера/эксплуатации.
|
||||
Это предположение для обсуждения, не оценка существующего штата. При иной
|
||||
команде и после обследования данных сроки нужно пересчитать. Переходы
|
||||
определяются результатами, а не истечением недель.
|
||||
|
||||
### Этап A. Данные и воспроизводимый запуск — P0, ориентир 3–6 недель
|
||||
|
||||
Зависимости: состояние официального API, доступный корпус и участие владельца
|
||||
инфраструктуры. Назначить ответственных за данные, эксплуатацию и юридическую
|
||||
проверку. Согласовать правила текущей редакции и обработки неизвестного статуса.
|
||||
|
||||
- Завершить квалификацию источника и полный аудит корпуса; отдельно проверить
|
||||
языки, порядок редакций, контрольные суммы, ошибки, вложения и пропуски.
|
||||
- Запустить воспроизводимый цикл проверки обновлений с журналом изменений
|
||||
и безопасным переключением индекса. Из этого журнала строить G12 и G09.
|
||||
- Выполнить существующий readiness gate, зафиксировать baseline и ограничения.
|
||||
- Подготовить пилотное развёртывание, наблюдение за ошибками, проверенный backup
|
||||
и восстановление в изолированной среде по существующим процедурам.
|
||||
|
||||
Приёмка: завершённый отчёт о полноте с объяснёнными исключениями; повторный
|
||||
запуск не создаёт дубликатов; неудачное обновление сохраняет рабочий индекс;
|
||||
сквозные сценарии RU/KY проходят; восстановление проверено. Рекомендуемая цель
|
||||
актуализации — не более суток от доступности изменения у источника; сначала
|
||||
измерить достижимость и только затем обещать SLA пользователям.
|
||||
|
||||
### Этап B. Поисковое рабочее место — P1, ориентир 4–6 недель
|
||||
|
||||
Зависимость: этап A. Закрыть G07, G13–14, G18–19 и уточнить G06.
|
||||
Использовать готовые просмотр, печать и оглавление; не переписывать их.
|
||||
|
||||
- Утвердить темы и устойчивые идентификаторы; не превращать черновые хеши
|
||||
классификатора в публичный контракт без проверки.
|
||||
- Добавить точный номер и независимые поля поиска; разрешить отбор только
|
||||
по реквизитам; определить семантику AND/OR и множественного выбора.
|
||||
- Сделать паспорт документа, пользовательский сценарий открытия по коду и
|
||||
DOCX с редакцией, языком, источником и временем выгрузки. Не обещать PDF-
|
||||
экспорт сверх печати до подтверждения отдельной потребности.
|
||||
|
||||
Приёмка: 20 задач пилота успешно проходят путь от запроса до нужного фрагмента;
|
||||
документы без языка/текста имеют понятное состояние; файл экспорта воспроизводит
|
||||
выбранную редакцию; новые правила не ухудшают замороженный relevance baseline.
|
||||
|
||||
### Этап C. Возвращение к работе и контроль — P1, ориентир 3–5 недель
|
||||
|
||||
Зависимости: A–B; для внешних пользователей — выбранная модель доступа и
|
||||
проверенное восстановление аккаунта G01. Для закрытого пилота допустимы
|
||||
создаваемые администратором внутренние аккаунты без публичной регистрации.
|
||||
|
||||
Закрыть G06, G10–11, G16: серверные избранное, история и шаблоны; G09/G12
|
||||
показать как пользовательскую ленту. Добавить настройки контроля и доставки
|
||||
уведомлений только после появления надёжного журнала обновлений. Выбор канала
|
||||
и согласие пользователя — отдельное решение; наличие бота команды не означает
|
||||
готовность продуктовых уведомлений.
|
||||
|
||||
Приёмка: сохранённое доступно со второго устройства; данные разных аккаунтов
|
||||
изолированы; удаление/отключение контроля работает; повторная обработка события
|
||||
не дублирует сигнал. Правила хранения истории и удаления аккаунта документированы.
|
||||
|
||||
### Этап D. Анализ нормы — P1/P2, ориентир 5–8 недель
|
||||
|
||||
Зависимости: проверенные редакции, структура фрагментов и метаданные A–B.
|
||||
Закрыть G23–24 и базовую часть G20; затем G21, G22 и G15 по результатам пилота.
|
||||
Для связей сохранять источник, целевую редакцию и уверенность сопоставления.
|
||||
|
||||
Приёмка: юрист проверяет не менее 30 разнообразных пар редакций, включая
|
||||
перенумерацию и перемещение статей; интерфейс отличает техническую правку от
|
||||
содержательной, а неопределённость показывает явно. Параллельный RU/KY-просмотр
|
||||
использует одну редакцию и не выдумывает соответствия. Неуверенные ссылки
|
||||
не выдаются как подтверждённые связи. Текстовый diff не считается юридическим
|
||||
заключением о дате применимости нормы.
|
||||
|
||||
### Этап E. Платный командный продукт — P2, ориентир 4–8 недель
|
||||
|
||||
Зависимость: подтверждённые повторное использование и готовность платить.
|
||||
Закрыть G02–05 и недостающую часть G01. Сначала выбрать одну понятную модель
|
||||
подписки и необходимый канал оплаты; не воспроизводить весь биллинг конкурента.
|
||||
|
||||
Приёмка: организация приглашает и отзывает участника; сервер проверяет права
|
||||
и срок доступа; повторное платёжное событие не создаёт повторное начисление;
|
||||
просроченная подписка не уничтожает сохранённые данные. Стоимость эксплуатации,
|
||||
юридической редакции и поддержки учитывается в экономике тарифа.
|
||||
|
||||
### Этап F. Новые источники и ответы — P2/P3, отдельная оценка
|
||||
|
||||
G08, G17, G25 и оставшиеся расширения включать по измеренному спросу.
|
||||
Для судебной практики сначала исследовать законность получения, полноту,
|
||||
обезличивание и обновляемость; для экспертных материалов — лицензирование
|
||||
и редакционную ответственность. Международные акты проверять в существующем
|
||||
корпусе прежде, чем создавать отдельный загрузчик. Зарубежную базу и форум
|
||||
не ставить на критический путь первого продукта.
|
||||
|
||||
RAG — отдельная разработка после A–D. Начать с поиска доказательств и цитат,
|
||||
затем краткого ответа. Приёмка: замороженный экспертный набор, проверка каждой
|
||||
ссылки и редакции, отказ при недостатке данных и ноль критических неподтверждённых
|
||||
юридических утверждений на приёмочном наборе. Это критерий допуска, а не гарантия
|
||||
безошибочности вне набора. Сравнение качества ИИ конкурентов пока не выполнено.
|
||||
|
||||
## 7. Проверка рыночной гипотезы и условия остановки
|
||||
|
||||
Параллельно этапу A провести 12–15 интервью с юристами и бухгалтерами, включая
|
||||
пользователей действующих СПС. Собирать обезличенные задачи, частоту,
|
||||
потери времени, критичные источники и причины отказа от нового сервиса.
|
||||
Цены сравнивать после получения актуальных коммерческих предложений.
|
||||
|
||||
Предлагаемый пилот: 10–15 специалистов, 4 недели, 20 одинаковых задач с RU/KY
|
||||
и простыми/сложными запросами. С согласия участников сравнить время и долю
|
||||
правильных решений в текущем инструменте и «Акылдаш»; менять порядок систем,
|
||||
чтобы уменьшить эффект обучения. Результат оценивает юрист по первоисточнику.
|
||||
|
||||
Цели ниже — предлагаемые пороги решения, не уже достигнутые показатели:
|
||||
|
||||
| Измерение | Предлагаемый порог | Решение |
|
||||
| --- | --- | --- |
|
||||
| Качество поиска | Recall@10 ≥ 0,90 и MRR@10 ≥ 0,75 на независимо проверенном наборе, отдельно RU и KY | При недостижении улучшать поиск до расширения функций |
|
||||
| Практическая польза | Медианное время правильного выполнения задач меньше минимум на 25% | При отсутствии выигрыша пересмотреть сценарий и сегмент |
|
||||
| Возвращаемость | Не менее 60% активированных участников пользуются продуктом на четвёртой неделе | При провале сначала исследовать причины, затем решать о биллинге |
|
||||
| Готовность платить | Не менее трёх независимых организаций готовы на платный пилот по названной цене | Переходить к этапу E после проверки экономики |
|
||||
| Доверие | Нет критических ошибок редакции/источника в приёмочных задачах | Любая такая ошибка блокирует публичные обещания надёжности |
|
||||
|
||||
Малый пилот помогает выбрать направление, но не даёт оценки доли рынка.
|
||||
Если пользователям необходимы источники, которые мы не можем законно получить,
|
||||
либо стоимость сопровождения превышает приемлемый тариф, сузить сегмент или
|
||||
остановить выбранную модель до дальнейших вложений.
|
||||
|
||||
## 8. Ближайший рабочий цикл
|
||||
|
||||
1. Назначить владельца продукта и юридического редактора; согласовать сегмент.
|
||||
2. Зафиксировать состояние корпуса и readiness gate на актуальной main.
|
||||
3. Провести интервью, собрать задачи и заморозить критерии пилота.
|
||||
4. Превратить этап A и минимальную часть B в отдельные задачи с зависимостями
|
||||
и критериями приёмки. Согласовать связь с существующим планом frontend,
|
||||
не отменяя его gate этим документом.
|
||||
5. После пилота пересчитать сроки, стоимость и порядок C–F; выбрать, какие
|
||||
пробелы действительно мешают продаже, а какие можно отложить.
|
||||
|
||||
Для реализации сохранять существующую архитектуру Python/SQLite/OpenSearch,
|
||||
использовать текущие API и компоненты, добавлять зависимости только по
|
||||
обоснованной необходимости. Изменения проводить отдельными PR с проверками,
|
||||
версией при изменении кода, независимым review и changelog. Production-действия
|
||||
и изменение инфраструктуры требуют действующих процедур доступа и backup.
|
||||
|
||||
---
|
||||
|
||||
Повторная сверка реализации: `main` `c8d0086a9f20485c5efe6f9d3ccd476dd9f07065`.
|
||||
Backend v0.13.3 · Frontend v0.3.3 · Telegram-бот v0.2.2. Это версии
|
||||
исследованного кода; они не подтверждают развёртывание этих версий.
|
||||
@@ -0,0 +1,222 @@
|
||||
# Функциональность платформы Акылдаш
|
||||
|
||||
Этот документ определяет целевой функциональный состав Акылдаша — юридической
|
||||
информационно-аналитической платформы для Кыргызской Республики. Пользователь
|
||||
сможет найти применимую норму, проверить источник и редакцию, сохранить
|
||||
материалы исследования и узнать об изменениях. Существенные юридические выводы
|
||||
проверяет специалист.
|
||||
|
||||
## Текущая реализация
|
||||
|
||||
Сверено с `main` на `871bde1`. В коде доступны полнотекстовый поиск по русским
|
||||
и кыргызским текстам, фильтры по языку, виду акта, статусу, группе органов,
|
||||
юридической силе и дате, сортировка по релевантности и дате, пагинация.
|
||||
Есть карточка документа, выбор редакции и доступного языка, оглавление,
|
||||
поиск в открытом тексте, печать и ссылка на официальный источник.
|
||||
Внутренние учётные записи поддерживают вход, смену пароля, серверную проверку
|
||||
прав, административное создание и удаление аккаунтов и сброс пароля.
|
||||
Лаборатория собирает оценки релевантности и комментарии проверяющих.
|
||||
Публичный запуск и готовность полного корпуса требуют подтверждения по
|
||||
[состоянию инфраструктуры](../operations/CURRENT_STATE.md).
|
||||
Telegram-бот команды описан в [решении о рабочем пространстве](../decisions/001-telegram-workspace-mvp.md).
|
||||
|
||||
## Правовые данные и условия публичного выпуска
|
||||
|
||||
- Получать открытые или лицензированные НПА и иные правовые материалы из
|
||||
официальных источников. Для каждого источника проверить законное основание,
|
||||
условия использования и распространения. Хранить происхождение, ссылку,
|
||||
лицензию, идентификаторы, контрольные суммы, даты получения и последней
|
||||
успешной проверки. Секреты и персональные данные исключать из Git, логов
|
||||
и тестовых наборов. Закрытые юридические документы хранить с поимённым
|
||||
доступом в защищённом хранилище.
|
||||
- Поддерживать регулярное обновление ЦБД Минюста КР с проверкой подлинности,
|
||||
полноты, языков, порядка редакций, вложений и объяснённых пропусков.
|
||||
Нормализовать HTML и структуру до статей и пунктов, сохранять реквизиты
|
||||
и двуязычные коды справочников. У каждого индексируемого документа
|
||||
проверять код, редакцию, язык, статус, тип, орган, дату, номер, название
|
||||
и ссылку на первоисточник. Показывать только доступные официальные
|
||||
языковые версии, явно отмечать отсутствие текста и неизвестный статус.
|
||||
Различать принятие, публикацию, вступление в силу, период применимости,
|
||||
редакцию и дату проверки. Проверять, какая редакция применима на нужную дату.
|
||||
- Вести журнал новых, изменённых и отменённых актов. Повторная обработка
|
||||
изменения идемпотентна. Цель обновления в течение суток после появления
|
||||
у источника измеряется на пилоте до принятия SLA.
|
||||
- Сборку индекса проверять в чистом окружении. Пройти цепочку от обновления
|
||||
корпуса и нормализации до нового версионированного индекса с раздельным
|
||||
анализом RU/KY, проверки mapping, числа документов и фрагментов, затем
|
||||
атомарного переключения alias. При неудаче сохранять рабочий индекс. Приёмка корпуса
|
||||
и выкладка описаны в [процедуре передачи корпуса](../operations/CORPUS_HANDOFF.md)
|
||||
и [плане реализации и приёмки](implementation-roadmap.md).
|
||||
- Собрать `relevance set v1` из 50 практических запросов, минимум по 25 на
|
||||
каждом языке. Юрист сверяет эталонные документы с первоисточником и кодами
|
||||
в индексе. Второй проверяющий подтверждает формулировки и коды. Спорные
|
||||
случаи включаются после согласования. Заморозить набор, воспроизвести
|
||||
Recall@10 и MRR@10 отдельно по языкам, сохранять выдачу для каждого запроса.
|
||||
Сравнивать изменения на том же наборе, проверять опечатки, сокращения,
|
||||
практические запросы и ложные срабатывания. См.
|
||||
[разметку](../../backend/search/RELEVANCE_ANNOTATION.md)
|
||||
и [диагностику поиска](search-relevance-findings-2026-11-03.md).
|
||||
- Утвердить [справочники v1](search-catalog-v1.md) типов, органов, статусов,
|
||||
уровней действия и тем со стабильными кодами и подписями RU/KY. Публичный
|
||||
тематический отбор добавляется после MVP. Проверить опубликованный
|
||||
OpenAPI и реальные интеграции на локальном OpenSearch.
|
||||
Зафиксировать совместимый контракт для поиска, фильтров, пагинации,
|
||||
сортировки, карточки, списка и содержимого редакций. Покрыть пустую
|
||||
выдачу, неизвестный документ, недоступную редакцию и сбой источника.
|
||||
API явно возвращает список доступных языков, в том числе для одноязычных актов.
|
||||
Маршруты и запуск описаны в [Backend README](../../backend/README.md#http-api-v1).
|
||||
- До проектирования публичного frontend зафиксировать сквозное прохождение
|
||||
перечисленных проверок с RU, KY и одноязычным документом, версии backend и
|
||||
индекса, известные ограничения и ответственного за юридическую проверку.
|
||||
До публичного выпуска проверить мониторинг ошибок и восстановление из
|
||||
резервной копии на изолированном окружении. Внутреннее рабочее место и
|
||||
лаборатория используют существующие API и развиваются по своим задачам.
|
||||
|
||||
## Публичный поисковый MVP
|
||||
|
||||
Публичный адаптивный веб-интерфейс на русском и кыргызском языках. Доступ к
|
||||
базовому поиску и чтению открыт без регистрации.
|
||||
|
||||
- **Поиск.** Свободный полнотекстовый запрос, раздельный анализ двух языков,
|
||||
морфология русского текста, проверенные синонимы, сокращения и опечатки.
|
||||
Для кыргызского текста отдельно проверять качество нормализации и словарей.
|
||||
Выдача показывает число результатов, подсвеченные фрагменты и пагинацию.
|
||||
- **Отбор.** Фильтры по языку, виду документа, статусу, органу, юридической
|
||||
силе и дате. Сортировка по релевантности и дате. Сброс отдельных и всех
|
||||
фильтров, сохранение состояния поиска в URL для повторного открытия.
|
||||
- **Документ.** Заголовок и реквизиты, актуальная редакция, юридический
|
||||
статус и дата актуальности, происхождение и официальный источник.
|
||||
Доступны оглавление, поиск по тексту с подсветкой и переходом между
|
||||
совпадениями, выбор доступного языка, список редакций, открытие предыдущей
|
||||
редакции и печать.
|
||||
Одноязычные документы и карточки без текста имеют явное состояние.
|
||||
- **Качество интерфейса.** Состояния загрузки, пустой выдачи и ошибок,
|
||||
клавиатурная навигация, адаптивные фильтры и длинные правовые тексты,
|
||||
WCAG 2.2 AA, безопасное отображение очищенного HTML, проверка
|
||||
производительности, метаданные страниц и версии frontend/backend в
|
||||
подвале. Дизайн опирается на [Түндүк](../../DESIGN.md). Перед выпуском
|
||||
пройти сквозные проверки поиска и чтения на 10–15 практических задачах.
|
||||
|
||||
## Поисковое рабочее место
|
||||
|
||||
- Реквизитный поиск с отдельными полями названия и текста, точным номером,
|
||||
открытием по коду и поиском без текстового запроса. Поддержать множественный
|
||||
выбор и определить правила AND/OR. Тематический отбор строить на
|
||||
иерархическом рубрикаторе КР с проверенными стабильными кодами.
|
||||
- Расширить паспорт документа. По мере готовности источников отображать
|
||||
точный орган принятия, территорию действия по классификации КР, данные
|
||||
регистрации и источник публикации. По результатам пилота добавить отбор
|
||||
по этим реквизитам и оценить сортировку по органу, виду, дате изменения
|
||||
и юридической силе с выбором направления.
|
||||
- Экспорт выбранной редакции, результатов и подборок в DOCX с сохранением
|
||||
структуры и выделений. Включать язык, редакцию, источник и время выгрузки.
|
||||
При копировании фрагмента указывать первоисточник. PDF сверх браузерной
|
||||
печати и RTF зависят от подтверждённой потребности.
|
||||
- Открывать несколько документов одновременно, сохранять контекст во вкладках
|
||||
и показывать материалы бок о бок. Приёмка рабочего места включает 20
|
||||
практических задач от запроса до нужного фрагмента, проверку экспорта
|
||||
нужной редакции и отсутствие ухудшения на замороженном наборе поиска.
|
||||
|
||||
## Личный кабинет и контроль изменений
|
||||
|
||||
- Выбрать модель пользовательского доступа. Для закрытого пилота подходят
|
||||
внутренние аккаунты, выданные администратором. Для персональных функций
|
||||
внешних пользователей определить регистрацию, подтверждение аккаунта и
|
||||
проверяемое восстановление доступа по email.
|
||||
- Хранить на сервере избранное, историю запросов и документов, именованные
|
||||
поиски и шаблоны фильтров, параметры выдачи, закладки и заметки к статьям и
|
||||
пунктам. Поддержать папки и подборки по проектам, доступ по приглашению
|
||||
или ссылке с проверкой прав и экспорт подборок. Персонализировать быстрые
|
||||
ссылки, избранные разделы и панель инструментов.
|
||||
- Публиковать ленту поступлений и обзор новых, изменённых и отменённых
|
||||
актов. Пользователь ставит документ или отдельную норму на контроль и
|
||||
получает сигнал об изменении редакции либо о новой связанной судебной
|
||||
практике после появления соответствующей коллекции.
|
||||
- Выбрать каналы уведомлений из email, Telegram и Web Push по результатам
|
||||
пилота. Предусмотреть согласие, настройки частоты и отключение.
|
||||
Проверять синхронизацию на втором устройстве, изоляцию аккаунтов,
|
||||
отзыв доступа, отключение и удаление контроля, отсутствие дубликатов
|
||||
событий. Документировать сроки хранения истории и удаление аккаунта.
|
||||
|
||||
## Юридическое сравнение и связи
|
||||
|
||||
- Сравнивать редакции бок о бок с подсветкой добавлений, исключений,
|
||||
перемещений и перенумерации. Различать технические и содержательные правки,
|
||||
отмечать утратившие силу фрагменты и показывать неопределённость.
|
||||
Юрист проверяет минимум 30 разнообразных пар. Юридическую применимость
|
||||
оценивать по реквизитам и источникам.
|
||||
- Показывать русский и кыргызский тексты одной редакции параллельно,
|
||||
сопоставляя только подтверждённые фрагменты.
|
||||
- Хранить прямые и обратные связи с другими нормами, актами, разъяснениями
|
||||
и судебной практикой. Показывать контекстный предпросмотр, цепочку норм
|
||||
и дерево или граф. Фиксировать источник, целевую редакцию и уверенность
|
||||
сопоставления. Отдельно обозначать непроверенные связи.
|
||||
- Выдавать проверенные вложения, сканы и графические копии при наличии
|
||||
в источнике и прав на распространение.
|
||||
|
||||
## Практические материалы и проверяемые ответы
|
||||
|
||||
- По результатам пилота оценить включение правоприменительной практики КР,
|
||||
писем и позиций госорганов, редакционных пояснений и экспертных
|
||||
комментариев. Проверять полноту, обновляемость, обезличивание и права
|
||||
на использование. Сначала выяснить покрытие международных актов
|
||||
существующим корпусом.
|
||||
- Подготовить типовые договоры, заявления, претензии и акты с заполняемыми
|
||||
полями и подсказками. Добавить инструкции, готовые решения, чек-листы,
|
||||
сроки и ответственные органы. Справочный раздел включает производственный
|
||||
календарь, сроки отчётности, ставки, курсы, индексы и реквизиты госорганов.
|
||||
Для справочных данных определить источник и частоту обновления.
|
||||
- Построить базу знаний для RAG поверх проверенного корпуса и структуры
|
||||
редакций. Сначала выдавать доказательства и точные цитаты с переходом
|
||||
к фрагменту, затем формировать краткий ответ. Отделять найденные факты
|
||||
от вывода, сообщать о недостатке данных и проверять актуальность нормы.
|
||||
Приёмка на замороженном экспертном наборе проверяет каждую ссылку,
|
||||
цитату и редакцию и требует отсутствия критических неподтверждённых
|
||||
юридических утверждений.
|
||||
|
||||
## Организации и интеграции
|
||||
|
||||
- Профили организаций, приглашение и удаление участников, роли и права
|
||||
юриста, бухгалтера, кадровика и администратора. Сервер проверяет доступ
|
||||
к материалам и срок действия прав. Журналировать административные и
|
||||
юридически значимые действия с соблюдением правил хранения данных.
|
||||
- Тарифы и права по продуктам, срок подписки, счета, платежи и сверка.
|
||||
Начать с одной модели подписки и канала оплаты после подтверждения
|
||||
спроса. Повторное событие обрабатывается один раз. Данные сохраняются
|
||||
при истечении подписки. Промокоды зависят от выбранной модели продаж.
|
||||
- Корпоративный API, структурированные выгрузки для дальнейшей обработки
|
||||
и интеграция с системами электронного документооборота с проверкой доступа.
|
||||
- Социальный вход, самостоятельные мобильный и десктопный клиенты,
|
||||
зарубежные коллекции, форум и пользовательские сообщения требуют
|
||||
отдельного решения о спросе, лицензиях и стоимости сопровождения.
|
||||
|
||||
## Порядок развития и проверка спроса
|
||||
|
||||
Данные и готовность выпуска предшествуют публичному MVP. Далее развивать
|
||||
поисковое рабочее место, личные функции и контроль изменений. Сравнение,
|
||||
связи и ответы зависят от проверенных редакций и структуры норм. Командные
|
||||
тарифы требуют доказанного повторного использования и готовности платить.
|
||||
Инфраструктурные и визуальные критерии ведутся в
|
||||
[плане реализации и приёмки](implementation-roadmap.md).
|
||||
|
||||
Исходная гипотеза из анализа «Токтом Онлайн» от 24 октября 2026 года —
|
||||
пилот для небольших юридических и бухгалтерских команд по ОсОО/ЖЧК,
|
||||
налогообложению и труду. Предложено провести 12–15 интервью, затем
|
||||
четырёхнедельный пилот с 10–15 специалистами на 20 обезличенных RU/KY-задачах.
|
||||
Юрист проверяет правильность по первоисточникам. Для каждого языка предложены
|
||||
пороги Recall@10 ≥ 0,90 и MRR@10 ≥ 0,75. Предложенный выигрыш медианного времени
|
||||
правильного решения составляет ≥ 25%, возвращаемость на четвёртой неделе
|
||||
составляет ≥ 60%. На пилоте также нужно подтвердить готовность минимум трёх
|
||||
организаций платить названную цену и отсутствие критических ошибок редакции
|
||||
или источника в приёмочных задачах. Пороги, сроки реализации и бюджет требуют
|
||||
подтверждения. Сужать сегмент, если нужные материалы недоступны законно или
|
||||
расходы на редакцию, поддержку и эксплуатацию делают тариф нежизнеспособным.
|
||||
|
||||
История требований сохранена в Git на `871bde1`. В основу этого документа
|
||||
легли Word-ТЗ поисковой СПС, обзор проекта, планы поискового frontend и
|
||||
готовности к нему, а также датированный анализ «Токтом Онлайн» (матрица
|
||||
G01–G25 и этапы A–F). Оценки конкурента относятся к датированному исследованию.
|
||||
|
||||
---
|
||||
|
||||
Акылдаш · Telegram-бот v0.2.2 · Backend v0.13.4 · Frontend v0.3.4
|
||||
@@ -1,47 +0,0 @@
|
||||
# Обзор проекта
|
||||
|
||||
## Назначение
|
||||
|
||||
Акылдаш — юридическая информационно-аналитическая платформа. Она должна помочь
|
||||
пользователю находить применимые нормы и материалы, понимать их актуальность и
|
||||
получать ответ со ссылками на проверяемые первоисточники. Платформа не заменяет
|
||||
профессиональное юридическое заключение.
|
||||
|
||||
## Основные области
|
||||
|
||||
1. Получение открытых или лицензированных законов, актов и иных правовых
|
||||
источников.
|
||||
2. Очистка, нормализация, связывание редакций и сохранение происхождения данных.
|
||||
3. Хранение структурированных документов и поисковых индексов.
|
||||
4. Формирование базы знаний и RAG с обязательными ссылками на источники.
|
||||
5. Backend для доступа к данным и функциям платформы.
|
||||
6. Frontend для поиска, анализа и работы с результатами.
|
||||
7. Инструменты рабочего окружения команды, включая Telegram-бота.
|
||||
|
||||
Физическая структура каждой области появится вместе с её первой задачей. До
|
||||
этого список служит картой продукта, а не обещанием заранее выбранной
|
||||
архитектуры.
|
||||
|
||||
## Обязательные принципы
|
||||
|
||||
- Источник, дата получения, редакция и лицензия документа должны быть
|
||||
прослеживаемыми.
|
||||
- Ответ системы должен отделять найденный факт от вывода модели и ссылаться на
|
||||
конкретный первоисточник.
|
||||
- Актуальность правовых данных должна проверяться до использования в ответе.
|
||||
- Существенные юридические выводы проверяет человек.
|
||||
- Секреты и персональные данные не попадают в Git, логи и тестовые наборы.
|
||||
- Данные загружаются только при наличии законного основания и с соблюдением
|
||||
условий источника.
|
||||
|
||||
## Правовая защита проекта
|
||||
|
||||
Договоры, заявки, материалы об интеллектуальной собственности и другие
|
||||
закрытые юридические документы следует хранить отдельно от исходного кода: в
|
||||
закрытом репозитории или защищённом документном хранилище. Доступ выдаётся
|
||||
поимённо и только тем, кому он нужен. В основном репозитории можно хранить
|
||||
несекретный реестр документов, правила доступа и ссылки на место хранения.
|
||||
|
||||
---
|
||||
|
||||
Акылдаш · Telegram-бот v0.2.2 · Backend v0.12.2 · Frontend v0.1.2
|
||||
Binary file not shown.
@@ -83,8 +83,9 @@ $allowed = @($owner, 'S-1-5-18', 'S-1-5-32-544')
|
||||
$aclOwner = $acl.GetOwner([System.Security.Principal.SecurityIdentifier]).Value
|
||||
if ($aclOwner -notin $allowed) { exit 6 }
|
||||
$ownerAllowed = $false
|
||||
foreach ($rule in $acl.Access) {
|
||||
$sid = $rule.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier]).Value
|
||||
$rules = $acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])
|
||||
foreach ($rule in $rules) {
|
||||
$sid = $rule.IdentityReference.Value
|
||||
if ($rule.AccessControlType -ne [System.Security.AccessControl.AccessControlType]::Allow -or $sid -notin $allowed) { exit 2 }
|
||||
if (($rule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -ne [System.Security.AccessControl.FileSystemRights]::FullControl) { exit 3 }
|
||||
if ($sid -eq $owner) { $ownerAllowed = $true }
|
||||
|
||||
@@ -203,6 +203,9 @@ class ExternalReceiverTests(unittest.TestCase):
|
||||
self.assertIn("System.IO.DriveInfo", run_mock.call_args.args[0][-1])
|
||||
self.assertNotIn("Get-Volume", run_mock.call_args.args[0][-1])
|
||||
self.assertIn("$acl.GetOwner(", run_mock.call_args.args[0][-1])
|
||||
self.assertIn("$acl.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier])", run_mock.call_args.args[0][-1])
|
||||
self.assertIn("$rule.IdentityReference.Value", run_mock.call_args.args[0][-1])
|
||||
self.assertNotIn(".Translate(", run_mock.call_args.args[0][-1])
|
||||
self.assertIn("$aclOwner -notin $allowed", run_mock.call_args.args[0][-1])
|
||||
|
||||
def test_private_acl_rejects_an_unapproved_acl_owner(self):
|
||||
|
||||
@@ -50,7 +50,8 @@ function Assert-ProtectedPath([string]$Path, [bool]$AllowOwnerWrite = $false) {
|
||||
$item = Get-Item -LiteralPath $Path -Force
|
||||
if (($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne 0) { throw "Refusing reparse path: $Path" }
|
||||
$acl = Get-Acl -LiteralPath $Path
|
||||
$ownerSid = ([Security.Principal.NTAccount]$acl.Owner).Translate([Security.Principal.SecurityIdentifier]).Value
|
||||
$ownerSid = $acl.GetOwner([Security.Principal.SecurityIdentifier]).Value
|
||||
$rules = $acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier])
|
||||
$trustedInstaller = 'S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464'
|
||||
if ($AllowOwnerWrite) {
|
||||
$approvedOwners = @('S-1-5-18', 'S-1-5-32-544', $identity.User.Value)
|
||||
@@ -60,24 +61,24 @@ function Assert-ProtectedPath([string]$Path, [bool]$AllowOwnerWrite = $false) {
|
||||
if ($ownerSid -notin $approvedOwners) { throw "Protected path has an unapproved owner: $Path" }
|
||||
if ($AllowOwnerWrite) {
|
||||
$allowed = @('S-1-5-18', 'S-1-5-32-544', $identity.User.Value)
|
||||
foreach ($rule in $acl.Access) {
|
||||
$sid = $rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
|
||||
foreach ($rule in $rules) {
|
||||
$sid = $rule.IdentityReference.Value
|
||||
if ($rule.AccessControlType -ne 'Allow' -or $sid -notin $allowed -or
|
||||
($rule.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -ne [System.Security.AccessControl.FileSystemRights]::FullControl) {
|
||||
throw "Private state path ACL must grant only full control to owner/SYSTEM/Administrators: $Path"
|
||||
}
|
||||
}
|
||||
$ownerHasFullControl = @($acl.Access | Where-Object {
|
||||
$ownerHasFullControl = @($rules | Where-Object {
|
||||
$_.AccessControlType -eq 'Allow' -and
|
||||
$_.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value -eq $identity.User.Value -and
|
||||
$_.IdentityReference.Value -eq $identity.User.Value -and
|
||||
($_.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::FullControl) -eq [System.Security.AccessControl.FileSystemRights]::FullControl
|
||||
}).Count -gt 0
|
||||
if (-not $ownerHasFullControl) { throw "Task owner needs full control on protected state path: $Path" }
|
||||
} else {
|
||||
$writableUntrusted = @($acl.Access | Where-Object {
|
||||
$writableUntrusted = @($rules | Where-Object {
|
||||
$_.AccessControlType -eq 'Allow' -and
|
||||
(([int]$_.FileSystemRights -band [int]$mutationRights) -ne 0) -and
|
||||
$_.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value -notin @('S-1-5-18', 'S-1-5-32-544', $trustedInstaller)
|
||||
$_.IdentityReference.Value -notin @('S-1-5-18', 'S-1-5-32-544', $trustedInstaller)
|
||||
})
|
||||
if ($writableUntrusted.Count -gt 0) { throw "Protected path grants write access outside SYSTEM/Administrators: $Path" }
|
||||
}
|
||||
@@ -102,13 +103,14 @@ function Assert-TrustedPath([string]$Path, [bool]$ParentsOnly = $false) {
|
||||
throw "Protected path contains a reparse point: $($current.FullName)"
|
||||
}
|
||||
$acl = Get-Acl -LiteralPath $current.FullName
|
||||
$ownerSid = ([Security.Principal.NTAccount]$acl.Owner).Translate([Security.Principal.SecurityIdentifier]).Value
|
||||
$ownerSid = $acl.GetOwner([Security.Principal.SecurityIdentifier]).Value
|
||||
if ($ownerSid -notin @('S-1-5-18', 'S-1-5-32-544', $trustedInstaller)) {
|
||||
throw "Protected path has an untrusted owner: $($current.FullName)"
|
||||
}
|
||||
$checkedRights = if ($isLeaf) { $mutationRights } else { $pathReplacementRights }
|
||||
foreach ($rule in $acl.Access) {
|
||||
$sid = $rule.IdentityReference.Translate([Security.Principal.SecurityIdentifier]).Value
|
||||
$rules = $acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier])
|
||||
foreach ($rule in $rules) {
|
||||
$sid = $rule.IdentityReference.Value
|
||||
if (($rule.PropagationFlags -band [System.Security.AccessControl.PropagationFlags]::InheritOnly) -ne 0) { continue }
|
||||
if ($rule.AccessControlType -eq 'Allow' -and
|
||||
(([int]$rule.FileSystemRights -band [int]$checkedRights) -ne 0) -and
|
||||
|
||||
@@ -17,8 +17,7 @@ if (($temporaryRootItem.Attributes -band [IO.FileAttributes]::ReparsePoint) -ne
|
||||
throw 'Temporary fixture root must not be a reparse point.'
|
||||
}
|
||||
$temporaryRootCreated = $temporaryRootItem.CreationTimeUtc
|
||||
$temporaryAclOwner = (Get-Acl -LiteralPath $temporaryRoot).Owner
|
||||
$temporaryOwnerSid = ([Security.Principal.NTAccount]$temporaryAclOwner).Translate(
|
||||
$temporaryOwnerSid = (Get-Acl -LiteralPath $temporaryRoot).GetOwner(
|
||||
[Security.Principal.SecurityIdentifier]).Value
|
||||
$currentSid = [Security.Principal.WindowsIdentity]::GetCurrent().User.Value
|
||||
if ($temporaryOwnerSid -cne $currentSid) { throw 'Temporary fixture root is not owned by the current user.' }
|
||||
@@ -44,8 +43,7 @@ function Get-VerifiedFixtureRoot([string]$Candidate) {
|
||||
$item.CreationTimeUtc -ne $temporaryRootCreated) {
|
||||
throw 'Fixture root was replaced, moved, or resolved outside its original identity.'
|
||||
}
|
||||
$ownerName = (Get-Acl -LiteralPath $candidatePath).Owner
|
||||
$ownerSid = ([Security.Principal.NTAccount]$ownerName).Translate(
|
||||
$ownerSid = (Get-Acl -LiteralPath $candidatePath).GetOwner(
|
||||
[Security.Principal.SecurityIdentifier]).Value
|
||||
if ($ownerSid -cne $temporaryOwnerSid -or $ownerSid -cne $currentSid) {
|
||||
throw 'Fixture root owner changed.'
|
||||
@@ -63,8 +61,7 @@ function Get-VerifiedFixtureEntry([string]$Path, [string]$ResolvedRoot) {
|
||||
if (-not $resolved.StartsWith($ResolvedRoot + '\', [StringComparison]::OrdinalIgnoreCase)) {
|
||||
throw 'Fixture entry resolved outside its verified temporary root.'
|
||||
}
|
||||
$ownerName = (Get-Acl -LiteralPath $resolved).Owner
|
||||
$ownerSid = ([Security.Principal.NTAccount]$ownerName).Translate(
|
||||
$ownerSid = (Get-Acl -LiteralPath $resolved).GetOwner(
|
||||
[Security.Principal.SecurityIdentifier]).Value
|
||||
if ($ownerSid -cne $temporaryOwnerSid -or $ownerSid -cne $currentSid) {
|
||||
throw 'Fixture entry is not owned by the current user.'
|
||||
@@ -104,6 +101,88 @@ try {
|
||||
if ($null -eq $validatorAst) { throw 'Installer bundle-mode validator was not found.' }
|
||||
. ([scriptblock]::Create($validatorAst.Extent.Text))
|
||||
|
||||
$identity = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$mutationRights = [System.Security.AccessControl.FileSystemRights](
|
||||
[int][System.Security.AccessControl.FileSystemRights]::WriteData -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::AppendData -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::WriteExtendedAttributes -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::WriteAttributes -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::Delete -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::ChangePermissions -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::TakeOwnership
|
||||
)
|
||||
$pathReplacementRights = [System.Security.AccessControl.FileSystemRights](
|
||||
[int][System.Security.AccessControl.FileSystemRights]::DeleteSubdirectoriesAndFiles -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::Delete -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::ChangePermissions -bor
|
||||
[int][System.Security.AccessControl.FileSystemRights]::TakeOwnership
|
||||
)
|
||||
foreach ($guardName in @('Assert-ProtectedPath', 'Assert-TrustedPath')) {
|
||||
$guardAst = $installerAst.Find({
|
||||
param($node)
|
||||
$node -is [System.Management.Automation.Language.FunctionDefinitionAst] -and
|
||||
$node.Name -ceq $guardName
|
||||
}, $true)
|
||||
if ($null -eq $guardAst) { throw "Installer ACL guard was not found: $guardName" }
|
||||
. ([scriptblock]::Create($guardAst.Extent.Text))
|
||||
}
|
||||
|
||||
$unknownSid = [Security.Principal.SecurityIdentifier]::new(
|
||||
'S-1-5-21-3141592653-2718281828-1618033988-987654')
|
||||
$unmapped = $false
|
||||
try { $null = $unknownSid.Translate([Security.Principal.NTAccount]) }
|
||||
catch [Security.Principal.IdentityNotMappedException] { $unmapped = $true }
|
||||
if (-not $unmapped) { throw 'The synthetic ACL SID unexpectedly resolves to an NTAccount.' }
|
||||
|
||||
$savedMockAcl = $script:akyldashAclFixture
|
||||
function Get-Acl([string]$LiteralPath) { return $script:akyldashAclFixture }
|
||||
try {
|
||||
$sidSystem = [Security.Principal.SecurityIdentifier]::new('S-1-5-18')
|
||||
$sidAdmins = [Security.Principal.SecurityIdentifier]::new('S-1-5-32-544')
|
||||
$privateFixtureAcl = [System.Security.AccessControl.DirectorySecurity]::new()
|
||||
$privateFixtureAcl.SetAccessRuleProtection($true, $false)
|
||||
$privateFixtureAcl.SetOwner($identity.User)
|
||||
foreach ($sid in @($sidSystem, $sidAdmins, $identity.User)) {
|
||||
$null = $privateFixtureAcl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
|
||||
$sid, [System.Security.AccessControl.FileSystemRights]::FullControl,
|
||||
[System.Security.AccessControl.AccessControlType]::Allow))
|
||||
}
|
||||
$null = $privateFixtureAcl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
|
||||
$unknownSid, [System.Security.AccessControl.FileSystemRights]::FullControl,
|
||||
[System.Security.AccessControl.AccessControlType]::Allow))
|
||||
$script:akyldashAclFixture = $privateFixtureAcl
|
||||
$privateRejected = $false
|
||||
try { Assert-ProtectedPath -Path $temporaryRoot -AllowOwnerWrite $true }
|
||||
catch {
|
||||
if ($_.Exception.Message -notlike 'Private state path ACL must grant only full control*') { throw }
|
||||
$privateRejected = $true
|
||||
}
|
||||
if (-not $privateRejected) { throw 'Unmapped SID was not rejected by the protected-path guard.' }
|
||||
|
||||
$trustedFixtureAcl = [System.Security.AccessControl.DirectorySecurity]::new()
|
||||
$trustedFixtureAcl.SetAccessRuleProtection($true, $false)
|
||||
$trustedFixtureAcl.SetOwner($sidSystem)
|
||||
$null = $trustedFixtureAcl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
|
||||
$sidSystem, [System.Security.AccessControl.FileSystemRights]::FullControl,
|
||||
[System.Security.AccessControl.AccessControlType]::Allow))
|
||||
$null = $trustedFixtureAcl.AddAccessRule([System.Security.AccessControl.FileSystemAccessRule]::new(
|
||||
$unknownSid, [System.Security.AccessControl.FileSystemRights]::WriteData,
|
||||
[System.Security.AccessControl.AccessControlType]::Allow))
|
||||
$script:akyldashAclFixture = $trustedFixtureAcl
|
||||
$trustedRejected = $false
|
||||
try { Assert-TrustedPath -Path $temporaryRoot }
|
||||
catch {
|
||||
if ($_.Exception.Message -notlike 'Protected path grants replacement/write access*') { throw }
|
||||
$trustedRejected = $true
|
||||
}
|
||||
if (-not $trustedRejected) { throw 'Unmapped SID was not rejected by the trusted-path guard.' }
|
||||
} finally {
|
||||
Remove-Item Function:\Get-Acl -ErrorAction SilentlyContinue
|
||||
if ($null -eq $savedMockAcl) { Remove-Variable -Name akyldashAclFixture -Scope Script -ErrorAction SilentlyContinue }
|
||||
else { $script:akyldashAclFixture = $savedMockAcl }
|
||||
}
|
||||
|
||||
Add-Type -AssemblyName System.IO.Compression
|
||||
$stream = [IO.File]::OpenRead($bundlePath)
|
||||
try {
|
||||
|
||||
@@ -113,6 +113,10 @@ class WindowsReleaseBootstrapTests(unittest.TestCase):
|
||||
script.index("$bundleStream = [IO.File]::Open"))
|
||||
self.assertIn("$approvedOwners = @('S-1-5-18', 'S-1-5-32-544', $trustedInstaller)", script)
|
||||
self.assertIn("$approvedOwners = @('S-1-5-18', 'S-1-5-32-544', $identity.User.Value)", script)
|
||||
self.assertIn("$acl.GetOwner([Security.Principal.SecurityIdentifier]).Value", script)
|
||||
self.assertGreaterEqual(script.count("$acl.GetAccessRules($true, $true, [Security.Principal.SecurityIdentifier])"), 2)
|
||||
self.assertIn("$rule.IdentityReference.Value", script)
|
||||
self.assertNotIn(".Translate(", script)
|
||||
self.assertIn("Assert-TrustedPath $appRoot", script)
|
||||
self.assertIn("Assert-TrustedPath $programData -ParentsOnly $true", script)
|
||||
self.assertIn("Set-TrustedCodeOwner $app", script)
|
||||
@@ -120,6 +124,15 @@ class WindowsReleaseBootstrapTests(unittest.TestCase):
|
||||
for name in release_bundle.FILES:
|
||||
self.assertIn(f"files/{name}", script)
|
||||
|
||||
def test_windows_acl_harness_exercises_unmapped_sid_with_real_installer_guards(self):
|
||||
harness = (ROOT / "infra/deploy/test_windows_release_bundle_mode.ps1").read_text(encoding="utf-8")
|
||||
self.assertIn("Assert-ProtectedPath", harness)
|
||||
self.assertIn("Assert-TrustedPath", harness)
|
||||
self.assertIn("IdentityNotMappedException", harness)
|
||||
self.assertIn("$unknownSid", harness)
|
||||
self.assertIn("$guardAst.Extent.Text", harness)
|
||||
self.assertIn("Unmapped SID was not rejected by the protected-path guard", harness)
|
||||
|
||||
def test_release_bundle_uses_exact_commit_objects_not_dirty_worktree_or_git_hooks(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
|
||||
Reference in new issue
Block a user