chore: add GRACE semantic contracts across agent + backend + build scripts

- Add @RELATION/@RATIONALE/@REJECTED headers to agent modules
- Add GRACE contracts to backend services, models, schemas
- Update build.sh with single-image build commands (build:backend|frontend|agent)
- Update docker entrypoint: openssl rsa → openssl pkey (alg-agnostic)
- Add @RATIONALE to backend core modules (cot_logger, config_manager, ssl)
This commit is contained in:
2026-07-08 11:10:59 +03:00
parent b6b1e05567
commit f22b0cf41c
34 changed files with 452 additions and 263 deletions

View File

@@ -12,6 +12,13 @@
# @POST User identity verified and session tokens issued according to role scopes.
# @SIDE_EFFECT Writes last login timestamps and JIT-provisions external users.
# @DATA_CONTRACT [Credentials | ADFSClaims] -> [UserEntity | SessionToken]
# @RATIONALE Thin service layer between API routes and AuthRepository — encapsulates credential
# verification (password hashing), ADFS JIT provisioning, and session creation into
# a single orchestration class. This keeps route handlers free of auth business logic.
# @REJECTED Putting credential verification logic directly in API route handlers was rejected —
# it would be duplicated across local login, ADFS callback, and API-key auth paths.
# Authentication was kept separate from authorization (permission checks in middleware)
# to allow independent testing of each concern.
from datetime import UTC, datetime
from typing import Any

View File

@@ -6,6 +6,14 @@
# @INVARIANT Artifact catalog must produce deterministic CandidateArtifact entries with required identity and checksum fields.
# @SIDE_EFFECT Reads JSON file from filesystem
# @DATA_CONTRACT FilePath -> CandidateArtifact[]
# @RATIONALE ArtifactCatalogLoader provides a standalone entry point for loading release
# candidate manifests from JSON files — used by the TUI and API to bootstrap
# clean-release flows without requiring an already-configured release pipeline.
# The JSON artifact catalog is human-editable for manual overrides.
# @REJECTED Embedding artifact loading in the compliance orchestrator was rejected — the
# loader must work independently for TUI-based workflows where no orchestrator
# exists. A DB-backed artifact store was rejected for the bootstrap flow —
# the release process starts before the database may be fully configured.
from __future__ import annotations

View File

@@ -9,6 +9,15 @@
# @PRE PolicyRepository is accessible
# @POST PolicyDecision returned with approval status
# @SIDE_EFFECT Read-only policy evaluation; no state changes
# @RATIONALE Policy evaluation is a pure function from Candidate -> PolicyDecision, enabling
# deterministic unit tests without database mocking. The "enterprise-clean" policy
# rejects dashboards referencing non-registry datasources — this prevents deploying
# dashboards that depend on ad-hoc tables or views that won't exist in production.
# @REJECTED Executing policy checks inside the deployment pipeline was rejected — policy failures
# should be surfaced during release preparation, not after deployment begins. An
# allow-list approach (listing permitted sources) was rejected as too brittle — the
# registry-based approach (any source not in the registry is a violation) automatically
# catches new unregistered sources without policy updates.
from __future__ import annotations

View File

@@ -10,6 +10,14 @@
# @PRE PolicyRepository and Manifest are available
# @POST ResolutionResult with matched policies
# @SIDE_EFFECT Read-only policy evaluation; logs resolution decisions
# @RATIONALE PolicyResolutionService decouples snapshot resolution from policy evaluation — it
# resolves the active policy and registry snapshots from ConfigManager's environment
# configuration, then passes them to PolicyEngine for evaluation. This separation
# allows testing resolution logic without evaluating policies, and vice versa.
# @REJECTED Merging resolution and evaluation into one service was rejected — resolution depends
# on ConfigManager (which environment is target), while evaluation is a pure function
# over snapshots. Combining them would require mocking ConfigManager even for unit
# testing simple policy rules.
from __future__ import annotations

View File

@@ -7,6 +7,15 @@
# @INVARIANT Stage execution is deterministic for equal input context.
# @SIDE_EFFECT None (deterministic execution)
# @DATA_CONTRACT Context -> StageResult
# @RATIONALE Pluggable stage architecture isolates each compliance check (manifest integrity,
# internal-only sources, data purity, etc.) into a self-contained Protocol-based stage.
# Each stage receives a shared ComplianceContext and produces a StageResult — the
# orchestrator aggregates results without knowing stage internals. This allows adding
# new compliance checks without modifying the execution pipeline.
# @REJECTED Monolithic compliance check was rejected — a single function checking all rules would
# exceed INV_7 (400 lines) and require coordinated changes for every new policy. Using
# inheritance (base class with abstract methods) was rejected — Protocol-based structural
# subtyping is more testable with simple dataclass contracts instead of class hierarchies.
from __future__ import annotations

View File

@@ -14,6 +14,12 @@
# @PRE session is active and valid
# @POST Returns Report with generated summary
# @SIDE_EFFECT Read-only database operations; logs report generation
# @RATIONALE Module-level contract aggregates task report normalization and query services
# into a single namespace consumed by route handlers. Normalization logic is extracted
# as a pure function (normalize_task_report) to enable unit testing without DB state.
# @REJECTED Splitting normalization and query into separate modules was rejected — they always
# change together (when a new task type is added, both the normalizer and query filter
# must be updated), making separation artificial overhead.
from datetime import UTC, datetime
@@ -81,6 +87,14 @@ def _filter_tasks_by_rbac(tasks: list[Task], current_user) -> list[Task]:
# @SIDE_EFFECT Reads task history and optional clean-release repository state without mutating source records.
# @DATA_CONTRACT Input[TaskManager, Optional[CleanReleaseRepository], ReportQuery|report_id] -> Output[ReportCollection|ReportDetailView|None]
# @INVARIANT Service methods are read-only over task history source.
# @RATIONALE ReportService provides a read-only query layer over TaskManager + CleanReleaseRepository
# — separate from the task execution pipeline — so that report UIs don't need access to
# task creation or lifecycle APIs. Service methods normalize task records into a consistent
# ReportRow shape regardless of the originating plugin (migration, translate, etc.).
# @REJECTED Embedding report queries in TaskManager was rejected — it would mix read-only analytics
# with task execution concerns and expose execution internals to report consumers. A
# dedicated reporting database was rejected — the operational task store is the SSOT and
# syncing to a reporting DB would add latency and consistency risk.
#
# @TEST_CONTRACT ReportsServiceModel ->
# {