Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ one Activation at a time.
## Status

The repository contains the PostgreSQL-authoritative SandboxAttempt reconcile
slice: atomic resource/change/outbox persistence, at-least-once Hatchet
dispatch, tenant-scoped workers, and OpenSandbox adoption after ambiguous
provider outcomes. The broader Cloud Agent API remains under development.
slice and the CloudSession commit consistency boundary: atomic
resource/change/outbox persistence, at-least-once Hatchet dispatch,
tenant-scoped workers, OpenSandbox adoption after ambiguous provider outcomes,
Activation admission, Session lease fencing, immutable BundleRevision
metadata, and atomic commit/publication visibility. The broader Cloud Agent API
and execution orchestration remain under development.

## Layout

Expand All @@ -40,6 +43,8 @@ The first Maka Graph implementation plan is in
The PostgreSQL/Hatchet reconciliation boundary and downstream package ownership
are specified in
[`docs/reconcile-workflow-contract.md`](docs/reconcile-workflow-contract.md).
The implemented Session consistency boundary is described in
[`docs/session-commit-protocol.md`](docs/session-commit-protocol.md).

## Run

Expand Down
85 changes: 85 additions & 0 deletions docs/session-commit-protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Session commit protocol

This slice implements the PostgreSQL consistency boundary for one Cloud
Session turn. It does not run Maka or move Session Bundle bytes.

## Resource relationship

```text
CloudSession
+-- Activation (one admitted stimulus)
| +-- SandboxAttempt (one replaceable execution attempt)
+-- SessionLease (one current execution fence)
+-- BundleRevision (immutable committed checkpoint metadata)
```

An Agent is referenced configuration, not a process. Different Sessions that
reference the same Agent may execute concurrently. Activations within one
Session are serialized.

## Server-owned admission state

Callers provide Session identity, Agent identity, and desired lifecycle, but
cannot choose initial metadata or status. The repository assigns generation 1,
resource version 1, and either `Ready` or `Suspended`.

Activation admission likewise rejects caller-owned status, resource version,
generation, finalizers, and Agent generation. It snapshots the Session's Agent
generation and creates a `Pending` Activation. The admission key is
`(tenant, cloud_session_id, activation_id)`.

Idempotency hashes canonicalize JSON object ordering with `json.Number`, so
integers beyond IEEE-754 precision are not collapsed. An exact admission replay
returns the existing Activation; reusing the key with different input is a
non-retryable duplicate.

## Claiming and fencing

The oldest pending Activation is the only one allowed to claim an idle
Session. `AcquireSessionLease` records the holder, Activation, monotonically
increasing epoch, and expiry. It also snapshots the Session head into the
Activation and marks both resources active in the same transaction.

The database clock is authoritative for expiry. A live lease cannot be stolen.
After expiry, the same claimed Activation may be recovered by another worker
with a higher epoch. Lease renewal and commit validate the holder, Activation,
epoch, resource version, and expiry.

## Commit transaction

The candidate Bundle bytes must already exist in object storage. A successful
`CommitActivation` performs one metadata transaction:

1. lock the CloudSession, Activation, and SessionLease rows;
2. verify the expected Session head and lease epoch;
3. insert immutable BundleRevision metadata for `head + 1`;
4. advance the Session head and clear its active Activation;
5. record the committed Activation outcome;
6. insert the reply into `publication_outbox`;
7. expire the lease;
8. append ordered resource changes for the Session, Activation, and revision.

No reply becomes visible without its committed Session head. An exact commit
replay returns the existing revision and outbox identity. A replay that changes
the expected parent, bundle, outcome, publication, or lease epoch is rejected.

Resource-change allocation takes a tenant-scoped transaction advisory lock.
This prevents a watcher from advancing past a lower change ID that commits
later.

## Migrations

Migrations are embedded, ordered by numeric version, and recorded in
`maka_cloud_schema_migrations` under one transaction advisory lock. The runner
recognizes a complete database created by the original version-1 runner,
records that baseline, and applies version 2. A partial legacy schema is
rejected instead of being guessed into a valid state.

## Deliberate boundaries

- Bundle bytes remain an object-storage concern.
- Provider, object-storage, Maka, and messaging calls never run inside the
metadata transaction.
- The repository does not choose queue or workflow technology.
- Publication delivery, Activation state-machine orchestration, cancellation,
and garbage collection remain follow-up slices.
138 changes: 138 additions & 0 deletions internal/resource/session.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package resource

import (
"encoding/json"
"time"
)

type CloudSessionLifecycle string

const (
CloudSessionLifecycleActive CloudSessionLifecycle = "Active"
CloudSessionLifecycleSuspended CloudSessionLifecycle = "Suspended"
)

type CloudSessionPhase string

const (
CloudSessionReady CloudSessionPhase = "Ready"
CloudSessionBusy CloudSessionPhase = "Busy"
CloudSessionSuspended CloudSessionPhase = "Suspended"
CloudSessionTerminating CloudSessionPhase = "Terminating"
)

type CloudSessionSpec struct {
AgentRef string
AgentGeneration int64
Lifecycle CloudSessionLifecycle
}

type CloudSessionStatus struct {
ObservedGeneration int64
HeadRevision int64
LastCommittedActivationID string
ActiveActivationID string
Phase CloudSessionPhase
}

type CloudSession struct {
Metadata Metadata
Spec CloudSessionSpec
Status CloudSessionStatus
}

type ActivationStimulusType string

const (
ActivationStimulusMessage ActivationStimulusType = "message"
ActivationStimulusSchedule ActivationStimulusType = "schedule"
ActivationStimulusSystem ActivationStimulusType = "system"
)

type ActivationStimulus struct {
Type ActivationStimulusType
Payload json.RawMessage
}

type ActivationSpec struct {
CloudSessionID string
ActivationID string
AgentGeneration int64
Stimulus ActivationStimulus
Deadline *time.Time
}

type ActivationPhase string

const (
ActivationPending ActivationPhase = "Pending"
ActivationClaimed ActivationPhase = "Claimed"
ActivationScheduled ActivationPhase = "Scheduled"
ActivationHydrating ActivationPhase = "Hydrating"
ActivationRunning ActivationPhase = "Running"
ActivationPacking ActivationPhase = "Packing"
ActivationCommitting ActivationPhase = "Committing"
ActivationPublishing ActivationPhase = "Publishing"
ActivationSucceeded ActivationPhase = "Succeeded"
ActivationRetrying ActivationPhase = "RetryPending"
ActivationBlocked ActivationPhase = "Blocked"
ActivationFailed ActivationPhase = "Failed"
ActivationCancelled ActivationPhase = "Cancelled"
)

type ActivationOutcomeStatus string

const (
ActivationOutcomeCompleted ActivationOutcomeStatus = "completed"
ActivationOutcomeBlocked ActivationOutcomeStatus = "blocked"
ActivationOutcomeRetryableFailure ActivationOutcomeStatus = "retryable_failure"
ActivationOutcomeFatalFailure ActivationOutcomeStatus = "fatal_failure"
)

type ActivationOutcome struct {
Status ActivationOutcomeStatus
Response json.RawMessage
Reason string
}

type ActivationStatus struct {
ObservedGeneration int64
Phase ActivationPhase
Attempt int
BaseRevision *int64
SandboxAttemptID string
LeaseEpoch *int64
CommittedRevision *int64
Outcome *ActivationOutcome
}

type Activation struct {
Metadata Metadata
Spec ActivationSpec
Status ActivationStatus
}

type SessionLease struct {
TenantID string
CloudSessionID string
HolderID string
ActivationID string
Epoch int64
ResourceVersion string
AcquiredAt time.Time
RenewedAt time.Time
ExpiresAt time.Time
}

type BundleRevision struct {
TenantID string
CloudSessionID string
Revision int64
ParentRevision int64
BundleRef string
TransportDigest string
PayloadDigest string
SizeBytes int64
CreatedByActivationID string
CreatedAt time.Time
}
Loading