Skip to documentation
Documentation navigation

Documentation navigation

Documentation / guides

Context and compaction events

Observe context measurements, pressure, and compaction outcomes.

developer

Context events make model-request sizing and pressure observable. Compaction events record an attempt and its authoritative result. The shared basis ties both to a loop-local committed context revision and the exact event through which that context was built.

Context contracts

type ContextRevision uint64

type ContextBasis struct {
	Revision ContextRevision `json:"revision"`
	ThroughEventID uuid.UUID `json:"through_event_id"`
}

type ContextMeasurement struct {
	Basis ContextBasis `json:"basis"`
	Model model.ModelKey `json:"model"`
	RequestFingerprint [32]byte `json:"request_fingerprint"`
	InputTokens content.TokenCount `json:"input_tokens"`
	InputLimit content.TokenCount `json:"input_limit"`
	Quality contextcount.CountQuality `json:"quality"`
}

type ContextMeasured struct {
	enduring
	loopScoped
	Header
	Measurement ContextMeasurement `json:"measurement"`
}

type ContextPressure struct {
	ephemeral
	loopScoped
	Header
	Measurement ContextMeasurement `json:"measurement"`
	Occupancy BasisPoints `json:"occupancy"`
	Previous PressureLevel `json:"previous"`
	Current PressureLevel `json:"current"`
}

ContextMeasured is the latest authoritative complete-request measurement and is durable. ContextPressure is a Public Ephemeral level-change signal. The pressure value is BasisPoints from 0 through FullScaleBasisPoints (10,000), and PressureLevel transitions from PressureNormal, PressureCompact, or PressureHardLimit; PressureUnknown is allowed only as the previous value. The current level must be valid and must differ from the previous level.

ContextMeasurement.Validate requires a non-zero revision, through-event ID, valid model key, non-zero request fingerprint, non-zero input limit, and one of the exact count quality values. A measured input count above the model limit is not malformed; it is retained as audit evidence for pressure handling.

Compaction contracts

type CompactionStarted struct {
	ephemeral
	loopScoped
	Header
	AttemptID CompactAttemptID `json:"attempt_id"`
	Reason CompactionReason `json:"reason"`
	Basis ContextBasis `json:"basis"`
}

type CompactionCommitted struct {
	enduring
	loopScoped
	Header
	AttemptID CompactAttemptID `json:"attempt_id"`
	WaiterCommandIDs []uuid.UUID `json:"waiter_command_ids"`
	Reason CompactionReason `json:"reason"`
	Basis ContextBasis `json:"basis"`
	Summary *content.UserMessage `json:"summary"`
	PostContext ContextMeasurement `json:"post_context"`
	Duration time.Duration `json:"duration,omitzero"`
}

type CompactionRejected struct {
	enduring
	loopScoped
	Header
	AttemptID CompactAttemptID `json:"attempt_id"`
	WaiterCommandIDs []uuid.UUID `json:"waiter_command_ids"`
	Reason CompactionReason `json:"reason"`
	Basis ContextBasis `json:"basis"`
	RejectReason CompactRejectReason `json:"reject_reason"`
	Duration time.Duration `json:"duration,omitzero"`
}

type CompactWaiterResolved struct {
	enduring
	loopScoped
	Header
	AttemptID CompactAttemptID `json:"attempt_id"`
	CommittedEventID uuid.UUID `json:"committed_event_id"`
}

type CompactWaiterRejected struct {
	enduring
	loopScoped
	Header
	AttemptID CompactAttemptID `json:"attempt_id"`
	Reason CompactRejectReason `json:"reason"`
}

CompactionStarted is live progress and is not a durable record. The committed and rejected values are the authoritative outcome. Waiter values are Enduring Replies: their Header.Cause.CommandID names the caller’s compaction command, and CompactWaiterReplyID(attempt, commandID, resolved) derives a deterministic event ID for idempotent per-command resolution.

EventClassDurableLifecycle
ContextMeasuredEnduringYesLatest complete-request measurement
ContextPressureEphemeralNoPressure level change for a live consumer
CompactionStartedEphemeralNoAttempt accepted and running
CompactionCommittedEnduringYesSummary and post-compaction context become authoritative
CompactionRejectedEnduringYesAttempt ended without changing context
CompactWaiterResolvedEnduringYesOne command resolves to a committed event
CompactWaiterRejectedEnduringYesOne command resolves with a typed reject reason

Attempt sequence

%%{init: {"theme":"dark"}}%%
sequenceDiagram
    participant App as Application
    participant S as session.Session
    participant L as Loop actor
    participant H as Event hub
    participant J as Journal

    App->>S: Compact(ctx)
    S-->>App: command ID
    L->>H: CompactionStarted (live only)
    alt summary and recount succeed
        L->>J: CompactionCommitted
        J-->>H: journal sequence
        H-->>App: committed Delivery
        L->>J: CompactWaiterResolved per waiter
    else attempt cannot commit
        L->>J: CompactionRejected
        J-->>H: journal sequence
        H-->>App: rejected Delivery
        L->>J: CompactWaiterRejected per waiter
    end

CompactionCommitted.Summary must be a non-nil user-role message with exactly one non-blank text block. WaiterCommandIDs must be non-empty and contain no zero or duplicate IDs. PostContext must pass the same structural measurement validation as ContextMeasured. Negative durations and unknown enum values are invalid.

The rejection vocabulary is closed. It includes control-lane full, shutting-down, interrupted, canceled, stale basis, progress publication, unavailable, execution failed, invalid summary, context-count failed, summary too large, internal, and context-limit unknown. The zero CompactRejectUnspecified is a sentinel and is never a valid rejection.

Observe pressure and durable outcomes

func watchCompaction(ctx context.Context, live session.Session) error {
	sub, err := live.SubscribeEvents(event.EventFilter{
		Ephemeral: event.LoopScope{All: true},
		Enduring: event.LoopScope{All: true},
	})
	if err != nil {
		return err
	}
	defer sub.Close()

	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case delivery, ok := <-sub.Events():
			if !ok {
				return sub.Err()
			}
			switch e := delivery.Event.(type) {
			case event.ContextPressure:
				log.Printf("loop %s context pressure %d/%d", e.LoopID, e.Occupancy, event.FullScaleBasisPoints)
			case event.CompactionCommitted:
				log.Printf("compaction %s committed at %d", e.AttemptID, delivery.JournalSeq)
			case event.CompactionRejected:
				log.Printf("compaction %s rejected: %d", e.AttemptID, e.RejectReason)
			case event.CompactWaiterResolved, event.CompactWaiterRejected:
				log.Printf("compaction command %s resolved as %T", e.EventHeader().Cause.CommandID, e)
			}
		}
	}
}

The code intentionally handles the pressure signal as optional. A reconnecting consumer should derive the current durable context from ContextMeasured and the committed compaction record, not from the last pressure notification it happened to receive. An Enduring delivery has a non-zero journal sequence; an Ephemeral pressure or start signal always has zero.

Restore and typed errors

Replay uses ContextBasis.ThroughEventID and the revision to prove which committed context a measurement describes. A stale basis is a typed CompactRejectStaleBasis outcome, not a silently applied summary. Invalid records return *event.InvalidEventError with fields such as AttemptID, WaiterCommandIDs, Summary, PostContext, Reason, or RejectReason. The event codec also rejects an Ephemeral value with *event.EphemeralNotPersistableError; do not try to journal CompactionStarted as a progress record.

Source and proofs

Compaction belongs inside a turn and Step stream; use the event envelope when persisting or correlating its replies.

← back to documentation