mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-22 11:45:52 +00:00
Extract the transport-independent action lifecycle service
Planning, approval decisions, and execution for typed resource actions move out of the HTTP handlers in internal/api/actions.go into a new internal/actionlifecycle.Service owned by api-contracts. The REST handlers become thin decode/actor/error-mapping adapters over the one shared service, and ResourceHandlers.ActionLifecycle() exposes the same service for in-process consumers, so a future Patrol action broker inherits identical resource lookup, availability checks, plan hashing, audit persistence, remediation locks, plan-drift revalidation, execution, and terminal publication instead of loopback HTTP or a parallel lifecycle. Behavior is preserved: same status codes, error codes, and audit/ lifecycle persistence ordering, backed by the existing api contract tests plus new fail-closed proofs for the service itself (unknown resource/capability, availability refusal, unapproved execution, remediation lock, plan drift, missing executor, missing store). Contract text in api-contracts, agent-lifecycle, and storage-recovery now names the service alongside actions.go and planner.go; the subsystem registry owns internal/actionlifecycle/ under api-contracts with a dedicated path policy; the code-standards and contract source pins follow the moved invariants; and the subsystem_lookup line-number pin shifts with the api-contracts canonical-files list insertion. This is the first slice of making the typed action lifecycle the only autonomous execution route for Patrol, Assistant, and MCP.
This commit is contained in:
@@ -639,16 +639,28 @@ func TestActionExecutionContractStaysAPIOwned(t *testing.T) {
|
||||
filepath.Join(".", "types.go"): {
|
||||
"ActionReadiness []ResourceActionReadiness `json:\"actionReadiness,omitempty\"`",
|
||||
},
|
||||
filepath.Join("..", "api", "actions.go"): {
|
||||
"type ActionExecutor interface",
|
||||
"type ActionAvailabilityChecker interface",
|
||||
filepath.Join("..", "actionlifecycle", "service.go"): {
|
||||
// The transport-independent lifecycle service is the only
|
||||
// sanctioned path from a typed action request to execution.
|
||||
// REST handlers and in-process brokers must both route
|
||||
// through it; pin its execution-boundary invariants here.
|
||||
"type Executor interface",
|
||||
"type AvailabilityChecker interface",
|
||||
"CheckActionAvailable(ctx context.Context, req unified.ActionRequest, resource unified.Resource) unified.ResourceActionReadiness",
|
||||
"func (h *ResourceHandlers) HandleExecuteAction(w http.ResponseWriter, r *http.Request)",
|
||||
"func (h *ResourceHandlers) validateActionPlanFresh(orgID string, record unified.ActionAuditRecord) error",
|
||||
"func recordRefusedActionExecution(store unified.ResourceStore, record unified.ActionAuditRecord",
|
||||
"func (h *ResourceHandlers) publishActionCompleted(record unified.ActionAuditRecord)",
|
||||
"func (s *Service) ValidatePlanFresh(orgID string, record unified.ActionAuditRecord) error",
|
||||
"func RecordRefusedExecution(store Store, record unified.ActionAuditRecord",
|
||||
"func (s *Service) publishCompleted(record unified.ActionAuditRecord)",
|
||||
"store.RecordActionExecutionStart(started, startEvent)",
|
||||
"store.RecordActionExecutionResult(completed, doneEvent)",
|
||||
},
|
||||
filepath.Join("..", "api", "actions.go"): {
|
||||
// The REST layer is a thin adapter over the shared lifecycle
|
||||
// service; it owns only decode, actor resolution, and error
|
||||
// code mapping.
|
||||
"type ActionExecutor = actionlifecycle.Executor",
|
||||
"type ActionAvailabilityChecker = actionlifecycle.AvailabilityChecker",
|
||||
"func (h *ResourceHandlers) ActionLifecycle() *actionlifecycle.Service",
|
||||
"func (h *ResourceHandlers) HandleExecuteAction(w http.ResponseWriter, r *http.Request)",
|
||||
"agentcapabilities.AgentErrCodeActionExecutionUnavailable",
|
||||
"agentcapabilities.AgentErrCodeActionPlanDrift",
|
||||
"agentcapabilities.AgentErrCodeActionExecutorUnavailable",
|
||||
|
||||
Reference in New Issue
Block a user