docs: add module documentation in AsciiDoc with PlantUML diagrams
Generated by per-group subagents covering all 52 modules across: core (18), infrastructure (10), apps (5), interfaces (3), plugins (7), testing (9) Documentation format: - AsciiDoc (.adoc) files in docs/modules/<group>/ - PlantUML (.puml) files in docs/diagrams/ - .adoc files reference diagrams via include:: directives Each doc covers: purpose, responsibilities, non-responsibilities, key types, event flow, integration points, invariants, PlantUML diagram, known issues, and open questions.
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
= core-approvals
|
||||
|
||||
== purpose
|
||||
|
||||
Evaluates whether a proposed operation should proceed by checking active grants and session approval mode, then returning an approval decision (auto-approved or pending human review). Tracks requests, decisions, and grants as event-sourced state.
|
||||
|
||||
== responsibilities
|
||||
|
||||
* Approving or rejecting operations based on tier, grants, and approval mode
|
||||
* Tracking pending approval requests and resolved decisions
|
||||
* Recording active grants (time-bounded permissions scoped to session/stage/project)
|
||||
* Rebuilding approval state from the event log
|
||||
* Providing a no-op engine for deterministic replay
|
||||
|
||||
== non-responsibilities
|
||||
|
||||
* Does not enforce policy — policy is absolute upstream
|
||||
* Does not emit events — all events are consumed from the event store
|
||||
* Does not validate whether the operation itself is valid (delegated to validation)
|
||||
* Does not determine execution tiers — tiers are declared by tools
|
||||
|
||||
== key types
|
||||
|
||||
=== ApprovalReducer
|
||||
* **kind**: interface
|
||||
* **purpose**: Transforms approval state given a stored event.
|
||||
|
||||
=== DefaultApprovalReducer
|
||||
* **kind**: class
|
||||
* **purpose**: Handles `ApprovalRequestedEvent`, `ApprovalDecisionResolvedEvent`, `ApprovalGrantCreatedEvent`, `ApprovalGrantExpiredEvent`.
|
||||
|
||||
=== ApprovalProjector
|
||||
* **kind**: class
|
||||
* **purpose**: Wraps `ApprovalReducer` as a `Projection<ApprovalState>`.
|
||||
|
||||
=== DefaultApprovalRepository
|
||||
* **kind**: class
|
||||
* **purpose**: Rebuilds `ApprovalState` for a session via `EventReplayer`.
|
||||
|
||||
=== ApprovalState
|
||||
* **kind**: data class
|
||||
* **purpose**: In-memory projection of all approval-state for a session.
|
||||
* **fields**: `requests`, `decisions`, `grants`
|
||||
|
||||
=== DomainApprovalRequest
|
||||
* **kind**: data class
|
||||
* **purpose**: A request for approval of a tiered operation.
|
||||
* **fields**: `id` (ApprovalRequestId), `tier`, `validationReportId`, `riskSummaryId`, `timestamp`, `causationId`, `correlationId`, `userSteering`, `toolName`, `preview`
|
||||
|
||||
=== ApprovalDecision
|
||||
* **kind**: data class
|
||||
* **purpose**: The outcome of an approval evaluation.
|
||||
* **fields**: `outcome` (ApprovalOutcome), `state` (ApprovalStatus), `tier`, `contextSnapshot`, `reason`, `userSteering`
|
||||
|
||||
=== ApprovalGrant
|
||||
* **kind**: data class
|
||||
* **purpose**: A time-bounded permission that bypasses approval for matching operations.
|
||||
* **fields**: `id` (GrantId), `scope` (GrantScope), `permittedTiers`, `reason`, `timestamp`, `expiresAt`
|
||||
|
||||
=== ApprovalContext
|
||||
* **kind**: data class
|
||||
* **purpose**: Snapshot of identity, mode, and causality chain at decision time.
|
||||
* **fields**: `identity` (ApprovalScopeIdentity), `mode` (ApprovalMode), `causationId`, `correlationId`
|
||||
|
||||
=== ApprovalScopeIdentity
|
||||
* **kind**: data class
|
||||
* **purpose**: Identifies the scope (session, stage, project) a decision applies to.
|
||||
|
||||
=== ApprovalEngine
|
||||
* **kind**: interface
|
||||
* **purpose**: Evaluates a request against grants and approval mode to produce a decision.
|
||||
|
||||
=== DefaultApprovalEngine
|
||||
* **kind**: class
|
||||
* **purpose**: Implements the tier-based approval logic. Checks grants first, then falls through to mode-based thresholds. Returns `PENDING` for requests above the approval threshold.
|
||||
|
||||
=== NoOpApprovalEngine
|
||||
* **kind**: class
|
||||
* **purpose**: Always returns `AUTO_APPROVED` with no logic. Used during replay to avoid live approval evaluation.
|
||||
|
||||
== event flow
|
||||
|
||||
*inbound:*
|
||||
* `ApprovalRequestedEvent` → creates a `DomainApprovalRequest` in state
|
||||
* `ApprovalDecisionResolvedEvent` → creates an `ApprovalDecision` in state
|
||||
* `ApprovalGrantCreatedEvent` → creates an `ApprovalGrant` in state
|
||||
* `ApprovalGrantExpiredEvent` → removes the grant from state
|
||||
|
||||
*outbound:*
|
||||
* None. This module is purely a projection — events are emitted by the orchestrator and consumed here.
|
||||
|
||||
== integration points
|
||||
|
||||
* `:core:events` — `StoredEvent`, event payload types, shared identity types (`SessionId`, `ApprovalRequestId`, etc.)
|
||||
* `:core:sessions` — `Projection`, `EventReplayer`, `ApprovalMode`
|
||||
|
||||
== invariants
|
||||
|
||||
* `ApprovalDecision.isFinal` is true only when `state != PENDING`
|
||||
* `ApprovalDecision.isApproved` is true only when outcome is `APPROVED` or `AUTO_APPROVED`
|
||||
* A grant with `expiresAt <= now` is never matched by `DefaultApprovalEngine`
|
||||
* `NoOpApprovalEngine` is used exclusively on the replay path — never during live execution
|
||||
* Request, decision, and grant IDs are globally unique; maps in `ApprovalState` are indexed by these IDs
|
||||
|
||||
== PlantUML diagram
|
||||
|
||||
[plantuml, core-approvals, "png"]
|
||||
----
|
||||
include::../../diagrams/core-approvals.puml[]
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
== known issues
|
||||
|
||||
* `ApprovalDecision.id` is nullable in the data class but `DefaultApprovalReducer` always assigns a non-null ID; `NoOpApprovalEngine` returns `null` for the ID, which could make downstream consumers handle both cases unnecessarily.
|
||||
* `ApprovalContext` construction in `DefaultApprovalReducer` hardcodes `ApprovalMode.PROMPT` and null stage/project IDs because the event does not carry this information. The full context is only available at evaluation time.
|
||||
|
||||
== open questions
|
||||
|
||||
* Should `ApprovalContext` be embedded in the event payload rather than reconstructed during reduction?
|
||||
* The `RiskSummaryId` on `DomainApprovalRequest` references data from the validation module — the two modules are separate and share no common risk model type.
|
||||
Reference in New Issue
Block a user