diff --git a/README.md b/README.md index a1188ed..f6a41a7 100755 --- a/README.md +++ b/README.md @@ -155,6 +155,29 @@ python src/scripts/init_auth_db.py python src/scripts/create_admin.py --username admin --password admin ``` +## 🏢 Enterprise Clean Deployment (internal-only) + +Для разворота в корпоративной сети используйте профиль enterprise clean: + +- очищенный дистрибутив без test/demo/load-test данных; +- запрет внешних интернет-источников; +- загрузка ресурсов только с внутренних серверов компании; +- обязательная блокирующая проверка clean/compliance перед выпуском. + +Быстрый запуск TUI-проверки: + +```bash +cd /home/busya/dev/ss-tools +./backend/.venv/bin/python3 -m backend.src.scripts.clean_release_tui +``` + +Типовые внутренние источники: +- `repo.intra.company.local` +- `artifacts.intra.company.local` +- `pypi.intra.company.local` + +Если найден внешний endpoint, выпуск получает статус `BLOCKED` до исправления. + ## 📖 Документация - [Установка и настройка](docs/installation.md) diff --git a/backend/src/api/routes/__init__.py b/backend/src/api/routes/__init__.py index 4f991d2..fdd1a9f 100755 --- a/backend/src/api/routes/__init__.py +++ b/backend/src/api/routes/__init__.py @@ -6,7 +6,7 @@ # @RELATION: DEPENDS_ON -> importlib # @INVARIANT: Only names listed in __all__ are importable via __getattr__. -__all__ = ['plugins', 'tasks', 'settings', 'connections', 'environments', 'mappings', 'migration', 'git', 'storage', 'admin', 'reports', 'assistant'] +__all__ = ['plugins', 'tasks', 'settings', 'connections', 'environments', 'mappings', 'migration', 'git', 'storage', 'admin', 'reports', 'assistant', 'clean_release'] # [DEF:__getattr__:Function] diff --git a/backend/src/app.py b/backend/src/app.py index 36989fc..3a25541 100755 --- a/backend/src/app.py +++ b/backend/src/app.py @@ -21,7 +21,7 @@ import asyncio from .dependencies import get_task_manager, get_scheduler_service from .core.utils.network import NetworkError from .core.logger import logger, belief_scope -from .api.routes import plugins, tasks, settings, environments, mappings, migration, connections, git, storage, admin, llm, dashboards, datasets, reports, assistant +from .api.routes import plugins, tasks, settings, environments, mappings, migration, connections, git, storage, admin, llm, dashboards, datasets, reports, assistant, clean_release from .api import auth # [DEF:App:Global] @@ -133,6 +133,7 @@ app.include_router(dashboards.router) app.include_router(datasets.router) app.include_router(reports.router) app.include_router(assistant.router, prefix="/api/assistant", tags=["Assistant"]) +app.include_router(clean_release.router) # [DEF:api.include_routers:Action] diff --git a/backend/src/dependencies.py b/backend/src/dependencies.py index c49ba02..4801c9d 100755 --- a/backend/src/dependencies.py +++ b/backend/src/dependencies.py @@ -14,20 +14,21 @@ from .core.config_manager import ConfigManager from .core.scheduler import SchedulerService from .services.resource_service import ResourceService from .services.mapping_service import MappingService +from .services.clean_release.repository import CleanReleaseRepository from .core.database import init_db, get_auth_db from .core.logger import logger from .core.auth.jwt import decode_token from .core.auth.repository import AuthRepository from .models.auth import User -# Initialize singletons -# Use absolute path relative to this file to ensure plugins are found regardless of CWD -project_root = Path(__file__).parent.parent.parent -config_path = project_root / "config.json" - -# Initialize database before services that use persisted configuration. -init_db() -config_manager = ConfigManager(config_path=str(config_path)) +# Initialize singletons +# Use absolute path relative to this file to ensure plugins are found regardless of CWD +project_root = Path(__file__).parent.parent.parent +config_path = project_root / "config.json" + +# Initialize database before services that use persisted configuration. +init_db() +config_manager = ConfigManager(config_path=str(config_path)) # [DEF:get_config_manager:Function] # @PURPOSE: Dependency injector for ConfigManager. @@ -54,6 +55,9 @@ logger.info("SchedulerService initialized") resource_service = ResourceService() logger.info("ResourceService initialized") +clean_release_repository = CleanReleaseRepository() +logger.info("CleanReleaseRepository initialized") + # [DEF:get_plugin_loader:Function] # @PURPOSE: Dependency injector for PluginLoader. # @PRE: Global plugin_loader must be initialized. @@ -104,6 +108,16 @@ def get_mapping_service() -> MappingService: return MappingService(config_manager) # [/DEF:get_mapping_service:Function] + +# [DEF:get_clean_release_repository:Function] +# @PURPOSE: Dependency injector for CleanReleaseRepository. +# @PRE: Global clean_release_repository must be initialized. +# @POST: Returns shared CleanReleaseRepository instance. +# @RETURN: CleanReleaseRepository - Shared clean release repository instance. +def get_clean_release_repository() -> CleanReleaseRepository: + return clean_release_repository +# [/DEF:get_clean_release_repository:Function] + # [DEF:oauth2_scheme:Variable] # @PURPOSE: OAuth2 password bearer scheme for token extraction. oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login") diff --git a/backend/tests/test_logger.py b/backend/tests/test_logger.py index 7c540ca..b87a73f 100644 --- a/backend/tests/test_logger.py +++ b/backend/tests/test_logger.py @@ -1,3 +1,11 @@ +# [DEF:tests.test_logger:Module] +# @TIER: STANDARD +# @SEMANTICS: logging, tests, belief_state +# @PURPOSE: Unit tests for the custom logger formatters and configuration context manager. +# @LAYER: Logging (Tests) +# @RELATION: VERIFIES -> src/core/logger.py +# @INVARIANT: All required log statements must correctly check the threshold. + import pytest from src.core.logger import ( belief_scope, @@ -210,4 +218,5 @@ def test_enable_belief_state_flag(caplog): enable_belief_state=True ) configure_logger(config) -# [/DEF:test_enable_belief_state_flag:Function] \ No newline at end of file +# [/DEF:test_enable_belief_state_flag:Function] +# [/DEF:tests.test_logger:Module] \ No newline at end of file diff --git a/docs/installation.md b/docs/installation.md index d373f37..0f5b6ac 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -282,6 +282,50 @@ export TASK_LOG_LEVEL=DEBUG export RETENTION_PERIOD_DAYS=90 ``` +## Enterprise Clean Release (изолированный контур) + +### Назначение + +Сценарий enterprise clean-профиля предназначен для подготовки дистрибутива: +- без test/demo/load-test данных; +- без внешних интернет-источников; +- только с внутренними серверами ресурсов компании. + +### Операторский цикл (TUI) + +```bash +cd /home/busya/dev/ss-tools +./backend/.venv/bin/python3 -m backend.src.scripts.clean_release_tui +``` + +Ожидаемый flow: +1. Выбрать `candidate_id`. +2. Подтвердить `profile=enterprise-clean`. +3. Запустить проверку (F5). +4. Дождаться терминального статуса: + - `COMPLIANT` — кандидат готов к следующему этапу выпуска; + - `BLOCKED` — выпуск запрещён до устранения нарушений. + +### Политика источников (internal-only) + +Разрешены только хосты из внутреннего реестра компании, например: +- `repo.intra.company.local` +- `artifacts.intra.company.local` +- `pypi.intra.company.local` + +Любой внешний endpoint (например `pypi.org`) трактуется как `external-source` и блокирует выпуск. + +### Recovery при BLOCKED + +1. Открыть детали нарушений (категория, location, remediation). +2. Удалить запрещённые данные или заменить внешний источник на внутренний. +3. Повторить запуск проверки (F5) до статуса `COMPLIANT`. + +### Обязательный CI gate + +После операторского TUI-прогона тот же профиль должен пройти в CI. +Только `COMPLIANT` в CI допускает релиз в корпоративный контур. + ## Troubleshooting ### Проблемы с Docker diff --git a/frontend/src/components/__tests__/task_log_viewer.test.js b/frontend/src/components/__tests__/task_log_viewer.test.js index 8096569..eb4dd8c 100644 --- a/frontend/src/components/__tests__/task_log_viewer.test.js +++ b/frontend/src/components/__tests__/task_log_viewer.test.js @@ -1,9 +1,9 @@ // [DEF:frontend.src.components.__tests__.task_log_viewer:Module] -// @TIER: STANDARD +// @TIER: CRITICAL // @SEMANTICS: tests, task-log, viewer, mount, components // @PURPOSE: Unit tests for TaskLogViewer component by mounting it and observing the DOM. // @LAYER: UI (Tests) -// @RELATION: TESTS -> frontend.src.components.TaskLogViewer +// @RELATION: VERIFIES -> frontend/src/components/TaskLogViewer.svelte // @INVARIANT: Duplicate logs are never appended. Polling only active for in-progress tasks. import { describe, it, expect, vi, beforeEach } from 'vitest'; @@ -20,10 +20,12 @@ vi.mock('../../lib/i18n', () => ({ subscribe: (fn) => { fn({ tasks: { - loading: 'Loading...' + loading: 'Loading...', + logs_title: 'Task Logs' }, common: { - retry: 'Retry' + retry: 'Retry', + close: 'Close' } }); return () => { }; @@ -127,5 +129,33 @@ describe('TaskLogViewer Component', () => { // getByText asserts there is exactly *one* match. expect(() => screen.getByText(/Duplicate log entry/)).not.toThrow(); }); + + // @TEST_FIXTURE valid_viewer + it('fetches and displays historical logs in modal mode under valid_viewer fixture', async () => { + getTaskLogs.mockResolvedValue([ + { timestamp: '2024-01-01T00:00:00', level: 'INFO', message: 'Modal log entry' } + ]); + + render(TaskLogViewer, { show: true, inline: false, taskId: 'task-123' }); + + await waitFor(() => { + expect(screen.getByText(/Modal log entry/)).toBeDefined(); + expect(screen.getByText('Task Logs')).toBeDefined(); + }); + + expect(getTaskLogs).toHaveBeenCalledWith('task-123'); + }); + + // @TEST_EDGE no_task_id + it('does not fetch logs if taskId is null', () => { + render(TaskLogViewer, { inline: true, taskId: null }); + expect(getTaskLogs).not.toHaveBeenCalled(); + }); + + // @UX_FEEDBACK + it('passes autoScroll feedback properly down to the panel by rendering without crashing', () => { + const { component } = render(TaskLogViewer, { inline: true, taskId: 'task-123' }); + expect(component).toBeDefined(); + }); }); // [/DEF:frontend.src.components.__tests__.task_log_viewer:Module] diff --git a/frontend/src/lib/components/reports/__tests__/report_card.ux.test.js b/frontend/src/lib/components/reports/__tests__/report_card.ux.test.js index e79bd6f..6f0e0f0 100644 --- a/frontend/src/lib/components/reports/__tests__/report_card.ux.test.js +++ b/frontend/src/lib/components/reports/__tests__/report_card.ux.test.js @@ -2,7 +2,7 @@ * @vitest-environment jsdom */ // [DEF:frontend.src.lib.components.reports.__tests__.report_card.ux:Module] -// @TIER: STANDARD +// @TIER: CRITICAL // @SEMANTICS: reports, ux-tests, card, states, recovery // @PURPOSE: Test UX states and transitions for ReportCard component // @LAYER: UI @@ -70,6 +70,33 @@ describe('ReportCard UX Contract', () => { // Check fallback type (the profile itself returns 'reports.unknown_type' string which doesn't get translated by $t in the mock if it's returning the key) expect(screen.getByText('reports.unknown_type')).toBeDefined(); }); + + // @TEST_FIXTURE valid_report_card + it('should render valid_report_card correctly', () => { + const validReportCard = { + task_type: "migration", + status: "success", + summary: "Test Summary", + updated_at: "2024-01-01" + }; + render(ReportCard, { report: validReportCard }); + + expect(screen.getByText('Test Summary')).toBeDefined(); + expect(screen.getByText('Success')).toBeDefined(); + }); + + // @TEST_EDGE empty_report_object + it('should handle completely empty report object gracefully', () => { + render(ReportCard, { report: {} }); + const placeholders = screen.getAllByText('Not provided'); + expect(placeholders.length).toBeGreaterThan(0); + }); + + // @TEST_EDGE random_status + it('should render random status directly if no translation matches', () => { + render(ReportCard, { report: { status: "unknown_status_code" } }); + expect(screen.getByText('unknown_status_code')).toBeDefined(); + }); }); // [/DEF:frontend.src.lib.components.reports.__tests__.report_card.ux:Module] \ No newline at end of file diff --git a/specs/023-clean-repo-enterprise/plan.md b/specs/023-clean-repo-enterprise/plan.md index f0a4d3c..d12e411 100644 --- a/specs/023-clean-repo-enterprise/plan.md +++ b/specs/023-clean-repo-enterprise/plan.md @@ -159,7 +159,19 @@ frontend/ **Gate Result (post-design)**: PASS -**Operational Note**: в tooling обнаружено предупреждение о конфликте numeric prefix `020` между [`specs/023-clean-repo-enterprise`](./) и [`specs/020-task-reports-design`](../020-task-reports-design). Это не блокирует текущий план-файл, но требует governance-решения для устранения неоднозначности при запуске стандартных `speckit`-скриптов. +**Operational Note (resolved)**: конфликт numeric prefix `020` устранён governance-решением: enterprise clean feature закреплён под уникальным префиксом `023` (ветка и feature-directory: `023-clean-repo-enterprise`). Проверка prereqs и speckit-поток выполняются с `FEATURE_DIR=specs/023-clean-repo-enterprise` без неоднозначности с [`specs/020-task-reports-design`](../020-task-reports-design). + +## Implementation Traceability & Final Notes + +- Статус реализации: Phase 1–7 завершены (T001–T043). +- Ключевые подтверждения polish-фазы: + - T039: smoke TUI сценария зафиксирован в [`quickstart.md`](./quickstart.md). + - T040: контрактная проверка API подтверждена тестом [`backend/tests/api/routes/test_clean_release_api.py`](../../backend/tests/api/routes/test_clean_release_api.py). + - T041: создан чеклист evidence package [`checklists/release-readiness.md`](./checklists/release-readiness.md). + - T042: governance conflict по префиксу закрыт и задокументирован. + - T043: добавлена итоговая traceability-нотация в текущем плане. + +Итог: feature готова к финальному релизному циклу с обязательным CI gate (`COMPLIANT` only) и операционной доказательной базой для аудита. ## Complexity Tracking diff --git a/specs/023-clean-repo-enterprise/quickstart.md b/specs/023-clean-repo-enterprise/quickstart.md index 2d31625..1dbabd5 100644 --- a/specs/023-clean-repo-enterprise/quickstart.md +++ b/specs/023-clean-repo-enterprise/quickstart.md @@ -85,6 +85,14 @@ cd /home/busya/dev/ss-tools - [ ] Отчёт выгружен и приложен к релизному пакету - [ ] Candidate отмечен как готовый к выпуску +## Troubleshooting Matrix + +| Category | Symptom | Blocking Rule | Remediation | +|---|---|---|---| +| `data-purity` | В составе кандидата есть test/demo/load-test артефакты | Любой найденный запрещённый артефакт => `BLOCKED` | Удалить запрещённые артефакты, повторить проверку | +| `external-source` | Обнаружен внешний endpoint (не из реестра внутренних хостов) | Любой внешний источник => `BLOCKED` | Заменить источник на внутренний сервер из allowlist, подтвердить конфиг | +| `operational-risk` | Внутренний источник недоступен во время проверки | Недоступность обязательного внутреннего ресурса => `BLOCKED` | Восстановить доступность внутреннего сервера и перезапустить проверку | + ## Troubleshooting ### Сценарий: Internal source unavailable @@ -105,4 +113,19 @@ cd /home/busya/dev/ss-tools Действия: 1. Удалить/заменить внешний источник на внутренний сервер из реестра. 2. Подтвердить изменение конфигурации. -3. Перезапустить проверку. \ No newline at end of file +3. Перезапустить проверку. + +## Smoke Validation Record + +Дата: 2026-03-03 +Среда: `backend/.venv`, локальный запуск из корня репозитория. + +- TUI smoke command: + - `cd backend && .venv/bin/python3 -m src.scripts.clean_release_tui` + - Результат: `PASS` (exit code 0, состояние `READY`, панель внутренних источников отображается). +- API contract smoke command: + - `cd backend && .venv/bin/python3 -m pytest tests/api/routes/test_clean_release_api.py -q` + - Результат: `PASS` (`2 passed`), shape контрактов `/api/clean-release/checks*` и `/api/clean-release/reports/{id}` подтверждён. + +Примечание: +- В тестовом прогоне есть существующие проектные предупреждения (Pydantic/FastAPI deprecations), но блокирующих ошибок для feature smoke-проверки не обнаружено. \ No newline at end of file diff --git a/specs/023-clean-repo-enterprise/tasks.md b/specs/023-clean-repo-enterprise/tasks.md index a58bad1..02ed530 100644 --- a/specs/023-clean-repo-enterprise/tasks.md +++ b/specs/023-clean-repo-enterprise/tasks.md @@ -15,11 +15,11 @@ **Purpose**: Подготовка каркаса clean-release подсистемы и мест хранения артефактов. -- [ ] T001 Create feature package skeleton for clean release modules in `backend/src/services/clean_release/__init__.py` -- [ ] T002 [P] Create clean release domain models module in `backend/src/models/clean_release.py` -- [ ] T003 [P] Create clean release API route module placeholder in `backend/src/api/routes/clean_release.py` -- [ ] T004 [P] Create TUI script entrypoint placeholder in `backend/src/scripts/clean_release_tui.py` -- [ ] T005 Register clean release router export in `backend/src/api/routes/__init__.py` +- [X] T001 Create feature package skeleton for clean release modules in `backend/src/services/clean_release/__init__.py` +- [X] T002 [P] Create clean release domain models module in `backend/src/models/clean_release.py` +- [X] T003 [P] Create clean release API route module placeholder in `backend/src/api/routes/clean_release.py` +- [X] T004 [P] Create TUI script entrypoint placeholder in `backend/src/scripts/clean_release_tui.py` +- [X] T005 Register clean release router export in `backend/src/api/routes/__init__.py` --- @@ -29,12 +29,12 @@ **⚠️ CRITICAL**: No user story work can begin until this phase is complete. -- [ ] T006 Implement core enums and lifecycle models (`ReleaseCandidate`, `CleanProfilePolicy`, `ResourceSourceRegistry`, `DistributionManifest`, `ComplianceCheckRun`, `ComplianceViolation`, `ComplianceReport`) in `backend/src/models/clean_release.py` -- [ ] T007 [P] Implement persistence adapter for clean release entities in `backend/src/services/clean_release/repository.py` -- [ ] T008 [P] Implement compliance stage constants and run state machine helpers in `backend/src/services/clean_release/stages.py` -- [ ] T009 Wire clean release dependencies provider in `backend/src/dependencies.py` -- [ ] T010 Add API router include for clean release endpoints in `backend/src/app.py` -- [ ] T011 Add baseline fixtures for clean release policy/candidate/report payloads in `backend/tests/fixtures/clean_release/fixtures_clean_release.json` +- [X] T006 Implement core enums and lifecycle models (`ReleaseCandidate`, `CleanProfilePolicy`, `ResourceSourceRegistry`, `DistributionManifest`, `ComplianceCheckRun`, `ComplianceViolation`, `ComplianceReport`) in `backend/src/models/clean_release.py` +- [X] T007 [P] Implement persistence adapter for clean release entities in `backend/src/services/clean_release/repository.py` +- [X] T008 [P] Implement compliance stage constants and run state machine helpers in `backend/src/services/clean_release/stages.py` +- [X] T009 Wire clean release dependencies provider in `backend/src/dependencies.py` +- [X] T010 Add API router include for clean release endpoints in `backend/src/app.py` +- [X] T011 Add baseline fixtures for clean release policy/candidate/report payloads in `backend/tests/fixtures/clean_release/fixtures_clean_release.json` **Checkpoint**: Foundation ready — user story implementation can now begin. @@ -48,16 +48,16 @@ ### Tests for User Story 1 -- [ ] T012 [P] [US1] Add unit tests for artifact classification and deterministic decisions in `backend/tests/services/clean_release/test_policy_engine.py` -- [ ] T013 [P] [US1] Add integration test for manifest generation consistency in `backend/tests/services/clean_release/test_manifest_builder.py` +- [X] T012 [P] [US1] Add unit tests for artifact classification and deterministic decisions in `backend/tests/services/clean_release/test_policy_engine.py` +- [X] T013 [P] [US1] Add integration test for manifest generation consistency in `backend/tests/services/clean_release/test_manifest_builder.py` ### Implementation for User Story 1 -- [ ] T014 [US1] Implement `CleanPolicyEngine` (CRITICAL: PRE: active policy + valid registry; POST: classification in [required-system|allowed|excluded-prohibited]; TESTS: fixture `policy_enterprise_clean`, edges `conflicting_rules`/`missing_registry`/`empty_prohibited_categories`) in `backend/src/services/clean_release/policy_engine.py` -- [ ] T015 [US1] Implement distribution manifest builder and deterministic hash logic in `backend/src/services/clean_release/manifest_builder.py` -- [ ] T016 [US1] Implement release candidate preparation service flow in `backend/src/services/clean_release/preparation_service.py` -- [ ] T017 [US1] Expose candidate preparation API handler in `backend/src/api/routes/clean_release.py` -- [ ] T018 [US1] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` +- [X] T014 [US1] Implement `CleanPolicyEngine` (CRITICAL: PRE: active policy + valid registry; POST: classification in [required-system|allowed|excluded-prohibited]; TESTS: fixture `policy_enterprise_clean`, edges `conflicting_rules`/`missing_registry`/`empty_prohibited_categories`) in `backend/src/services/clean_release/policy_engine.py` +- [X] T015 [US1] Implement distribution manifest builder and deterministic hash logic in `backend/src/services/clean_release/manifest_builder.py` +- [X] T016 [US1] Implement release candidate preparation service flow in `backend/src/services/clean_release/preparation_service.py` +- [X] T017 [US1] Expose candidate preparation API handler in `backend/src/api/routes/clean_release.py` +- [X] T018 [US1] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` **Checkpoint**: US1 independently functional and testable. @@ -71,16 +71,16 @@ ### Tests for User Story 2 -- [ ] T019 [P] [US2] Add unit tests for internal source registry validation in `backend/tests/services/clean_release/test_source_isolation.py` -- [ ] T020 [P] [US2] Add integration test for external endpoint blocking in `backend/tests/api/routes/test_clean_release_source_policy.py` +- [X] T019 [P] [US2] Add unit tests for internal source registry validation in `backend/tests/services/clean_release/test_source_isolation.py` +- [X] T020 [P] [US2] Add integration test for external endpoint blocking in `backend/tests/api/routes/test_clean_release_source_policy.py` ### Implementation for User Story 2 -- [ ] T021 [US2] Implement source isolation validator service in `backend/src/services/clean_release/source_isolation.py` -- [ ] T022 [US2] Extend `CleanPolicyEngine` with source registry checks for external endpoint detection in `backend/src/services/clean_release/policy_engine.py` -- [ ] T023 [US2] Add source registry API contract handling (`internal-only` validation errors) in `backend/src/api/routes/clean_release.py` -- [ ] T024 [US2] Update TUI view model to display Allowed Internal Sources panel and External Source blocking messages in `backend/src/scripts/clean_release_tui.py` -- [ ] T025 [US2] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` +- [X] T021 [US2] Implement source isolation validator service in `backend/src/services/clean_release/source_isolation.py` +- [X] T022 [US2] Extend `CleanPolicyEngine` with source registry checks for external endpoint detection in `backend/src/services/clean_release/policy_engine.py` +- [X] T023 [US2] Add source registry API contract handling (`internal-only` validation errors) in `backend/src/api/routes/clean_release.py` +- [X] T024 [US2] Update TUI view model to display Allowed Internal Sources panel and External Source blocking messages in `backend/src/scripts/clean_release_tui.py` +- [X] T025 [US2] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` **Checkpoint**: US2 independently functional and testable. @@ -94,17 +94,17 @@ ### Tests for User Story 3 -- [ ] T026 [P] [US3] Add orchestrator state machine tests for stage pass/fail transitions in `backend/tests/services/clean_release/test_compliance_orchestrator.py` -- [ ] T027 [P] [US3] Add report builder validation tests for counters and blocking violations in `backend/tests/services/clean_release/test_report_builder.py` -- [ ] T028 [P] [US3] Add API contract tests for `/api/clean-release/checks*` and `/api/clean-release/reports/{id}` in `backend/tests/api/routes/test_clean_release_api.py` +- [X] T026 [P] [US3] Add orchestrator state machine tests for stage pass/fail transitions in `backend/tests/services/clean_release/test_compliance_orchestrator.py` +- [X] T027 [P] [US3] Add report builder validation tests for counters and blocking violations in `backend/tests/services/clean_release/test_report_builder.py` +- [X] T028 [P] [US3] Add API contract tests for `/api/clean-release/checks*` and `/api/clean-release/reports/{id}` in `backend/tests/api/routes/test_clean_release_api.py` ### Implementation for User Story 3 -- [ ] T029 [US3] Implement `CleanComplianceOrchestrator` (CRITICAL: PRE: candidate exists + active policy; POST: final status COMPLIANT/BLOCKED/FAILED; TESTS: fixture `compliant_candidate`, edges `stage_failure_blocks_release`/`missing_stage_result`/`report_generation_error`) in `backend/src/services/clean_release/compliance_orchestrator.py` -- [ ] T030 [US3] Implement `ComplianceReportBuilder` (CRITICAL: PRE: terminal run state; POST: report counters consistent with violations; TESTS: fixture `blocked_with_two_violations`, edges `empty_violations_for_blocked`/`counter_mismatch`/`missing_operator_summary`) in `backend/src/services/clean_release/report_builder.py` -- [ ] T031 [US3] Implement clean release API endpoints from `contracts/api.yaml` in `backend/src/api/routes/clean_release.py` -- [ ] T032 [US3] Add audit logging hooks for preparation/check/report lifecycle in `backend/src/services/clean_release/audit_service.py` -- [ ] T033 [US3] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` +- [X] T029 [US3] Implement `CleanComplianceOrchestrator` (CRITICAL: PRE: candidate exists + active policy; POST: final status COMPLIANT/BLOCKED/FAILED; TESTS: fixture `compliant_candidate`, edges `stage_failure_blocks_release`/`missing_stage_result`/`report_generation_error`) in `backend/src/services/clean_release/compliance_orchestrator.py` +- [X] T030 [US3] Implement `ComplianceReportBuilder` (CRITICAL: PRE: terminal run state; POST: report counters consistent with violations; TESTS: fixture `blocked_with_two_violations`, edges `empty_violations_for_blocked`/`counter_mismatch`/`missing_operator_summary`) in `backend/src/services/clean_release/report_builder.py` +- [X] T031 [US3] Implement clean release API endpoints from `contracts/api.yaml` in `backend/src/api/routes/clean_release.py` +- [X] T032 [US3] Add audit logging hooks for preparation/check/report lifecycle in `backend/src/services/clean_release/audit_service.py` +- [X] T033 [US3] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` **Checkpoint**: US3 independently functional and testable. @@ -118,11 +118,11 @@ ### Implementation for User Story 4 -- [ ] T034 [US4] Update operator runbook with enterprise clean lifecycle and recovery actions in `docs/installation.md` -- [ ] T035 [US4] Add dedicated enterprise clean deployment section with internal-only source policy in `README.md` -- [ ] T036 [US4] Sync quick operational guidance with compliance statuses and report workflow in `specs/023-clean-repo-enterprise/quickstart.md` -- [ ] T037 [US4] Add troubleshooting matrix for blocked categories (`data-purity`, `external-source`, `operational-risk`) in `specs/023-clean-repo-enterprise/quickstart.md` -- [ ] T038 [US4] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` +- [X] T034 [US4] Update operator runbook with enterprise clean lifecycle and recovery actions in `docs/installation.md` +- [X] T035 [US4] Add dedicated enterprise clean deployment section with internal-only source policy in `README.md` +- [X] T036 [US4] Sync quick operational guidance with compliance statuses and report workflow in `specs/023-clean-repo-enterprise/quickstart.md` +- [X] T037 [US4] Add troubleshooting matrix for blocked categories (`data-purity`, `external-source`, `operational-risk`) in `specs/023-clean-repo-enterprise/quickstart.md` +- [X] T038 [US4] Verify implementation matches `ux_reference.md` (Happy Path & Errors) in `specs/023-clean-repo-enterprise/ux_reference.md` **Checkpoint**: US4 independently functional and testable. @@ -132,11 +132,11 @@ **Purpose**: Финализация, smoke-проверки и governance-замыкание. -- [ ] T039 [P] Run end-to-end smoke validation of TUI scenario from `quickstart.md` and record results in `specs/023-clean-repo-enterprise/quickstart.md` -- [ ] T040 [P] Validate OpenAPI contract consistency against implemented routes in `backend/tests/api/routes/test_clean_release_api.py` -- [ ] T041 Add release checklist artifact template for compliance evidence packaging in `specs/023-clean-repo-enterprise/checklists/release-readiness.md` -- [ ] T042 Resolve numeric-prefix governance conflict note (`020-*`) and document decision in `specs/023-clean-repo-enterprise/plan.md` -- [ ] T043 Update feature status traceability and final notes in `specs/023-clean-repo-enterprise/plan.md` +- [X] T039 [P] Run end-to-end smoke validation of TUI scenario from `quickstart.md` and record results in `specs/023-clean-repo-enterprise/quickstart.md` +- [X] T040 [P] Validate OpenAPI contract consistency against implemented routes in `backend/tests/api/routes/test_clean_release_api.py` +- [X] T041 Add release checklist artifact template for compliance evidence packaging in `specs/023-clean-repo-enterprise/checklists/release-readiness.md` +- [X] T042 Resolve numeric-prefix governance conflict note (`020-*`) and document decision in `specs/023-clean-repo-enterprise/plan.md` +- [X] T043 Update feature status traceability and final notes in `specs/023-clean-repo-enterprise/plan.md` ---