Documentation
¶
Overview ¶
Package runtimecommand is the seam between a Host-admitted public command and the UUID-keyed runtime command Harness has always dispatched.
Two identities meet here and must never be confused for one another.
The public CommandID is Host's retry-stable identity. It is an OPAQUE bounded UTF-8 string: Harness validates exactly what Core's canonical sessionwire/v1 CommandID validates — non-empty, at most MaxCommandIDBytes bytes, valid UTF-8 — and does nothing else with it. It is never parsed as a UUID, never truncated, and never substituted for the runtime id; a public id that happens to render a valid UUID is still just a string here.
The parity with Core is a CORRECTNESS requirement, not tidiness. The admission authority upstream accepts an id under those three rules; an applier that additionally rejected, say, a leading space or an embedded tab would refuse a command that was already durably admitted, forever. It could only sit pending to its apply deadline and become rejected, with the refusal invisible on the admission side. Do not add a rule here that Core does not have.
The RuntimeCommandID is the core/uuid.UUID Harness stamps on command headers and on the events those commands cause. Host allocates it ONCE, when it admits the command, and hands it back on every delivery. Harness does not allocate a replacement: a second UUID for the same admitted command would silently split one command's correlation across two identities, and the split would be invisible — the events would look perfectly well formed under either id.
Application is the durable bridge between them. It is written to the session's PRIVATE journal, under the lease epoch that authorizes the effect, BEFORE any runtime-visible effect begins. That ordering is the crash-safety property: a delivery that finds an existing prefix knows the command was already applied and replays the original disposition instead of applying it a second time.
Index ¶
- Constants
- type Admitted
- type Application
- type Applier
- type AttemptCloser
- type AttemptID
- type CapabilityUnavailableError
- type Closure
- type ClosureNotAuthorizedError
- type ClosureResult
- type CommandDisposition
- type CommandID
- type Disposition
- type DispositionKind
- type DispositionUnsupportedError
- type EffectScan
- type EnduringEffectError
- type Kind
- type LeaseLostError
- type MappingConflictError
- type Provider
- type StaleLeaseEpochError
- type ValidationError
Constants ¶
const MaxAttemptIDBytes = 256
MaxAttemptIDBytes bounds a dispatch-attempt identity. It is Core's MaxIDBytes, which is what the durable envelope's attempt_id field is bounded by; the two must not drift, for exactly the reason CommandID's bound must not — an attempt Host has already durably recorded must not become unwritable here.
const MaxCommandIDBytes = 256
MaxCommandIDBytes bounds a public CommandID. The id is opaque, so the only thing Harness can assert about it is that it is bounded: an unbounded id would enter a durable idempotency key and a journal record body. It is Core's MaxIDBytes; the two must not drift.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Admitted ¶
type Admitted struct {
// CommandID is Host's opaque public identity for this command.
CommandID CommandID
// RuntimeCommandID is the once-allocated UUID this command's runtime headers
// and events carry. Harness uses it verbatim.
RuntimeCommandID uuid.UUID
// Kind selects the runtime dispatch path.
Kind Kind
// LeaseEpoch is the session-lease epoch this command was admitted against. A
// record admitted under a superseded epoch is refused.
LeaseEpoch uint64
// Blocks is the input payload, required for KindInput and forbidden for every
// other kind (a payload a kind cannot carry would be silently dropped).
Blocks []content.Block
// AttemptID is Host's immutable identity for the ONE authorized dispatch
// attempt this delivery belongs to. It is OPTIONAL, and the option is the
// compatibility contract: a legacy admitted record carries none, and an
// applier handed one writes NO disposition, so a legacy session's journal is
// byte-for-byte what it was. A non-empty id is validated exactly as the
// durable boundary validates it.
AttemptID AttemptID
}
Admitted is one command Host has ALREADY admitted, handed to Harness for application. Harness re-validates it — an admitted record it cannot durably correlate is refused rather than applied on trust — but it never re-derives an identity from it.
func (Admitted) Application ¶
func (a Admitted) Application() Application
Application returns the durable correlation for this admitted record. It copies the identities rather than deriving new ones.
func (Admitted) DispositionFor ¶ added in v0.34.0
func (a Admitted) DispositionFor(kind DispositionKind, grantEpoch uint64) CommandDisposition
DispositionFor builds the durable disposition this admitted record's attempt resolved to, under grantEpoch — the journal grant the applier ACTUALLY held.
Both epochs come from that grant and neither is copied from Admitted.LeaseEpoch. The applier refuses an admitted record whose epoch is not the one it holds, so the two agree on every reachable path; taking the held grant rather than the record's claim is what keeps that true if the refusal is ever relaxed.
type Application ¶
type Application struct {
CommandID CommandID `json:"command_id"`
RuntimeCommandID uuid.UUID `json:"runtime_command_id"`
LeaseEpoch uint64 `json:"lease_epoch"`
// Kind is the admitted command's kind. It is part of the correlation because
// the released SessionStore reader correlates on it: a prefix whose kind
// disagrees with the inbox record's resolves CONFLICTED, not applied. Omitting
// it would make every application unmatchable by the counterparty.
Kind Kind `json:"command_kind"`
}
Application is the private durable correlation an applier writes BEFORE the effect: the public id, the one runtime id it maps to, and the lease epoch the application ran under. It is the whole content of the application prefix — no payload, no user content — because its only job is to answer "was this public command already applied, and under which runtime identity?".
Known gap, recorded so it is a decision rather than an oversight: it carries no loop id. KindInput dispatches to the session's ACTIVE loop, and which loop that was is not recoverable from this record — neither recovery nor an audit can say where an admitted command landed. Nothing in the current contract needs it (the runtime id correlates the events, and the events carry the loop), but a per-loop-addressed admitted command would need this field, and adding it later changes the persisted body and therefore every existing record's fingerprint.
func (Application) Validate ¶
func (a Application) Validate() error
Validate fails closed on a correlation that cannot have been produced by a valid admitted record. It is the decode-side guard for a prefix read back from storage.
type Applier ¶
type Applier interface {
// ApplyRuntimeCommand durably records the application prefix and then applies
// the command, returning the disposition. A duplicate delivery returns the
// original disposition and applies nothing.
//
// A non-nil error does not license a retry. The prefix is written BEFORE the
// effect, so an error raised by the effect leaves a durable prefix behind and
// every later delivery deduplicates against it. Re-deliver and read
// Disposition.Duplicate to learn what happened; do not assume an error means
// nothing was recorded.
//
// A returned Disposition also does not mean the application will SETTLE. The
// durable settlement correlation resolves an application by finding the public
// event adjacent to its prefix, and adjacency is not guaranteed for any command
// kind: another writer's record in that slot leaves the application unresolved
// forever. Unresolved never licenses a rejection, so nothing is applied twice and
// nothing is settled over — but a caller that BLOCKS on an application settling
// blocks indefinitely. Treat the Disposition as the answer, not as a promise that
// a later durable query will agree.
ApplyRuntimeCommand(context.Context, Admitted) (Disposition, error)
}
Applier is the SEGREGATED runtime-command capability. It is deliberately not a method on session.Session: almost every Session implementation — every test double, every adapter, the TUI's view — will never apply an admitted command, and widening the base contract would force all of them to grow a method they cannot honor. A caller obtains one through Provider.
type AttemptCloser ¶ added in v0.34.0
type AttemptCloser interface {
// CloseAttempt durably records not_applied for the named attempt, under THIS
// runtime's own grant, which must be strictly later than the attempt's.
//
// It refuses rather than closing when the runtime holds no live grant, when its
// grant is not strictly later, when the journal's own prefix binds the command
// to another mapping, and — the guard that matters most — when the journal holds
// an enduring event caused by that runtime command ANYWHERE IN THE JOURNAL,
// because a tombstone over a committed effect is the one error this protocol
// cannot recover from. It also refuses a journal it cannot fully read.
//
// "Anywhere" is deliberate and is not a looser restatement of "after the
// prefix". An event caused by that runtime id cannot exist unless the command
// was dispatched, so its POSITION proves nothing extra; and a guard that ignored
// an event before the prefix would be choosing, in the one journal shape nobody
// can explain, to tombstone rather than to refuse. Refusing an odd journal costs
// liveness; tombstoning a committed effect is unrecoverable.
CloseAttempt(context.Context, Closure) (ClosureResult, error)
}
AttemptCloser is the SEGREGATED recovery-closure capability: it writes the not_applied tombstone for an attempt a previous runtime never finished.
It is a separate interface from Applier, discovered by assertion, exactly as session.LeaseEpochReporter is. Folding CloseAttempt into Applier would break every existing implementer of that interface — the composed Host adapter, the test doubles — for a capability only a recovery path uses. A caller obtains one with
closer, ok := applier.(runtimecommand.AttemptCloser)
and an implementation that cannot honor it refuses at the call with a *CapabilityUnavailableError rather than advertising a closure it cannot write.
type AttemptID ¶ added in v0.34.0
type AttemptID string
AttemptID is Host's immutable identity for ONE authorized dispatch attempt. Like CommandID it is an OPAQUE bounded UTF-8 string: Harness never parses it, never derives anything from its shape, and validates exactly what the durable boundary validates.
It is the correlation key for settlement. A disposition names the attempt rather than the command because a command may be attempted more than once — a successor closing a predecessor's attempt writes about THAT attempt — and evidence about one attempt is not evidence about another.
type CapabilityUnavailableError ¶
CapabilityUnavailableError reports that ApplyRuntimeCommand was called on a session that does not advertise the capability. A caller that went through Provider.RuntimeCommands never sees it; it exists so a caller that reached the method by a bare type assertion fails loudly instead of silently applying a command with no durable correlation.
func (*CapabilityUnavailableError) Error ¶
func (e *CapabilityUnavailableError) Error() string
type Closure ¶ added in v0.34.0
type Closure struct {
CommandID CommandID
RuntimeCommandID uuid.UUID
Kind Kind
AttemptID AttemptID
AttemptJournalEpoch uint64
}
Closure is a successor runtime's request to close an unfinished attempt.
It carries NO author grant. The closer stamps that from the live lease it holds, which is the whole point of the capability: a caller-supplied author epoch would be a caller-authored proof, and the one thing a tombstone must not be is something a caller can assert.
type ClosureNotAuthorizedError ¶ added in v0.34.0
type ClosureNotAuthorizedError struct {
AttemptID AttemptID
AttemptJournalEpoch uint64
Current uint64
Held bool
}
ClosureNotAuthorizedError reports a closure offered without a strictly later grant. Held distinguishes "this runtime holds no live grant at all" from "its grant is not later than the attempt's": the first is a lost or released lease, the second is a runtime trying to close its OWN attempt, and conflating them would send an operator looking for the wrong failure.
func (*ClosureNotAuthorizedError) Error ¶ added in v0.34.0
func (e *ClosureNotAuthorizedError) Error() string
type ClosureResult ¶ added in v0.34.0
ClosureResult reports where the recovery closure landed. Appended=false means an identical closure was already durable — a redelivered recovery, not a second tombstone — and Sequence is the ORIGINAL append's.
type CommandDisposition ¶ added in v0.34.0
type CommandDisposition struct {
CommandID CommandID `json:"command_id"`
RuntimeCommandID uuid.UUID `json:"runtime_command_id"`
Kind Kind `json:"command_kind"`
LeaseEpoch uint64 `json:"lease_epoch"`
AttemptID AttemptID `json:"attempt_id"`
AttemptJournalEpoch uint64 `json:"attempt_journal_epoch"`
Disposition DispositionKind `json:"disposition_kind"`
}
CommandDisposition is the runtime's durable, bodiless statement about ONE attempt.
LeaseEpoch is the grant the AUTHOR actually held when it wrote the record, and getting it right matters more than it looks. Harness bypasses the durable store's own JournalWriter — it encodes the envelope and appends the bytes itself — so no field here is store-stamped and the reader treats this value as a CLAIM. It cross-checks it against the nearest preceding opening fence and fails closed when the two disagree, which reads exactly like a forged epoch. A wrong LeaseEpoch does not degrade settlement; it stops it.
AttemptJournalEpoch is the grant the ATTEMPT was authorized under, which the settlement verifier compares against its own immutable attempt record. For the three kinds authored by the attempt's grant the two epochs are EQUAL; for a recovery closure LeaseEpoch is strictly greater.
The struct tags are the JSON body this package's codec produces for fingerprinting and replay. They are NOT the durable frame: the frame is the store's binary envelope, whose field tags are its own.
func (CommandDisposition) Validate ¶ added in v0.34.0
func (d CommandDisposition) Validate() error
Validate fails closed on a disposition the durable boundary or the settlement verifier would refuse. It deliberately enforces the AUTHOR-GRANT rule as well as the per-field shape: the envelope codec cannot check it (it never sees the attempt record), so a record with the wrong grant relationship would encode and append cleanly and then be refused at settlement, where the refusal reads as an attack rather than as a writer bug.
type CommandID ¶
type CommandID string
CommandID is the public, retry-stable command identity Host owns. It is opaque to Harness; see the package doc for why it is never parsed as a UUID.
func (CommandID) Validate ¶
Validate reports whether id is a well-formed opaque public identity: non-empty, at most MaxCommandIDBytes bytes, and valid UTF-8. That is the WHOLE rule, and it is deliberately Core's rule byte for byte — see the package doc for why an extra rule here strands an already-admitted command. Everything else about the id — format, meaning, structure, whitespace, control characters — belongs to Host.
Nothing downstream needs a stricter id in the CHARACTER dimension: the durable idempotency key namespaces the public id rather than filtering it (journal.CommandApplicationRecord), and the record body is JSON-encoded, so no byte value is unsafe anywhere on the path.
The LENGTH dimension is enforced by the SAME authority, not by a second one, and that is structural rather than test-enforced. The application prefix is persisted as the released SessionStore's EnvelopeKindApplicationPrefix, whose identity field carries the RAW public id and is validated by Core's own sessionwire.CommandID.Validate. No prefix, namespace, or derived record id consumes any of the 256-byte budget, so Harness's acceptance and the durable boundary's acceptance cannot drift: they are one rule applied twice.
type Disposition ¶
type Disposition struct {
// CommandID echoes the public id this disposition answers for.
CommandID CommandID
// RuntimeCommandID is the durable runtime identity of the application.
RuntimeCommandID uuid.UUID
// PrefixSequence is the journal sequence of the application prefix. For a
// duplicate it is the ORIGINAL append's sequence, never a new one.
PrefixSequence uint64
// Duplicate reports that this delivery applied nothing because the command was
// already applied.
Duplicate bool
// Interrupted reports, for KindInterrupt, whether a running turn was cancelled.
// It is the TRANSIENT outcome of an application, not part of the durable prefix,
// so a duplicate delivery always reports false: the prefix records the
// correlation, never the outcome. Read it only alongside Duplicate.
Interrupted bool
}
Disposition is what an application resolved to. A duplicate delivery reports the ORIGINAL disposition — the same runtime id and the same prefix sequence the first delivery reported — with Duplicate set, so a caller can distinguish "applied by this call" from "already applied" without being able to confuse the two.
type DispositionKind ¶ added in v0.34.0
type DispositionKind string
DispositionKind is the closed vocabulary of durable runtime dispositions. The four spellings are DURABLE BYTES, not labels: they are written verbatim into the journal frame and compared verbatim by the settlement verifier, so renaming one is a wire change and not a refactor.
const ( // DispositionApplied is the narrow statement in this file's header doc: under // the attempt's grant, the runtime durably recorded that it accepted the command // into its execution path. DispositionApplied DispositionKind = "applied" // DispositionNoOp is an explicit SUCCESSFUL application with no effect — an // interrupt of an idle session. It settles applied. Reject-before-dispatch and // an applied no-op are different outcomes and must never be merged. DispositionNoOp DispositionKind = "no_op" // DispositionRefused is this runtime's own statement, under the attempt's OWN // grant, that it did not accept the command. It exists for LIVENESS: a command // whose effect failed after its prefix under a still-live lease has no other // terminal arm, because not_applied requires a strictly later grant and a // healthy Host never turns its lease over. Without it such a command sits // applying forever. DispositionRefused DispositionKind = "refused" // DispositionNotApplied is a SUCCESSOR's recovery closure, authored by a // strictly later grant. It is the tombstone late dispatch must consult. It is // never a substitute for a refusal and nothing may derive one from the other: a // refusal is a live runtime's own answer, a closure is a successor's conclusion // about a runtime that is gone. DispositionNotApplied DispositionKind = "not_applied" )
func (DispositionKind) Valid ¶ added in v0.34.0
func (k DispositionKind) Valid() bool
Valid reports whether k is one of the four known kinds.
type DispositionUnsupportedError ¶ added in v0.34.0
DispositionUnsupportedError reports that an attempt-bearing command reached a session whose durable log cannot record a disposition. It is raised BEFORE any durable write: a command applied with no evidence sits applying forever, settleable by nobody, which is strictly worse than a refusal Host can retry elsewhere.
IT LIVES HERE, BESIDE CapabilityUnavailableError, BECAUSE HOST IS TOLD TO ACT ON IT. It was originally declared in the unexported runtime package, which made that instruction unfollowable: a consumer outside this module could only recognise the refusal by matching its message text, and a message is not an API. A typed refusal a caller cannot name is a refusal a caller cannot distinguish from a transport failure, and the difference matters — nothing durable was written, so the command may be re-offered elsewhere.
func (*DispositionUnsupportedError) Error ¶ added in v0.34.0
func (e *DispositionUnsupportedError) Error() string
type EffectScan ¶ added in v0.34.0
type EffectScan struct {
PrefixSeq uint64
DurableRuntimeID uuid.UUID
DurableKind Kind
EffectFound bool
EffectSeq uint64
}
EffectScan is what a privileged journal scan found about one command identity. It is the evidence the closer's second guard rests on, so every member is a fact about durable records and none is a conclusion.
PrefixSeq is zero when the journal holds NO application prefix for the command, which is the ordinary shape of an attempt that never reached its effect. DurableRuntimeID and DurableKind are the mapping that prefix holds, so a closure offered against a different mapping can be refused rather than tombstoning somebody else's command.
type EnduringEffectError ¶ added in v0.34.0
type EnduringEffectError struct {
AttemptID AttemptID
CommandID CommandID
RuntimeCommandID uuid.UUID
PrefixSeq uint64
EffectSeq uint64
}
EnduringEffectError reports that the journal holds an enduring event caused by the attempt's runtime command, so the predecessor's EFFECT committed even though its disposition did not.
PrefixSeq and EffectSeq are WHERE THE SCAN FOUND THINGS, not an ordering claim. PrefixSeq is zero when the journal holds no application prefix for the command, and EffectSeq is NOT guaranteed to be greater than it — the scan refuses on an event caused by that runtime id at any sequence, deliberately. Read each as a locator; do not read a relationship between the two.
This is the case the idempotency guard cannot catch. A successor's not_applied collides with a predecessor's durable APPLIED disposition and fails closed for free, because both key on the attempt id — but if the predecessor's effect committed and its disposition append then failed, there is no colliding record and nothing but this scan stands between a successor and a tombstone over a real effect.
func (*EnduringEffectError) Error ¶ added in v0.34.0
func (e *EnduringEffectError) Error() string
type Kind ¶
type Kind string
Kind is the bounded set of admitted commands Harness can apply through this seam. It is deliberately small: every kind here must have an existing runtime dispatch path, so a new kind is a new implementation, never a new default.
type LeaseLostError ¶
LeaseLostError reports that the applier's session lease is no longer held, so no effect may be applied under it.
func (*LeaseLostError) Error ¶
func (e *LeaseLostError) Error() string
type MappingConflictError ¶
type MappingConflictError struct {
CommandID CommandID
// RuntimeCommandID is the id the offered delivery carried.
RuntimeCommandID uuid.UUID
// DurableRuntimeID is the id the durable application prefix holds. It is zero
// when the prefix could not be read; see Cause.
DurableRuntimeID uuid.UUID
// Kind is the kind the offered delivery carried, and DurableKind the kind the
// durable prefix holds. They are compared because the RELEASED reader compares
// them: a prefix whose kind disagrees with the inbox record's resolves
// CONFLICTED. Ignoring the kind here would have Harness report already-applied
// for a shape the counterparty refuses.
Kind Kind
DurableKind Kind
// Sequence is the journal sequence of the durable prefix.
Sequence uint64
Cause error
}
MappingConflictError reports that the public CommandID is already durable under a mapping this delivery may not be applied against. It covers two situations that must both fail closed, and the two are distinguishable — do not collapse them.
A CONFLICT: the durable prefix was read and binds this public id to a DIFFERENT RuntimeCommandID. That is never a legitimate retry, because a retry of an admitted command carries the mapping Host allocated once. DurableRuntimeID is set.
An UNREADABLE prefix: the durable frame at Sequence could not be read at all, so the mapping is UNKNOWN. DurableRuntimeID is then zero and Cause is the read failure. Reporting this as a duplicate would tell Host "already applied" about a command that may never have been applied.
Error() distinguishes them. The unreadable arm must not name a mapping: printing an all-zero DurableRuntimeID as though it had been read is a message that contradicts its own Cause, and it sends a reader hunting for a runtime command that does not exist.
func (*MappingConflictError) Error ¶
func (e *MappingConflictError) Error() string
func (*MappingConflictError) Unwrap ¶
func (e *MappingConflictError) Unwrap() error
type Provider ¶
Provider is implemented by a session that MAY be able to apply admitted runtime commands. RuntimeCommands reports the capability: ok is false — with a nil Applier — for a session with no durable, deduplicating application-prefix log, which includes a headless/no-persistence session.
The two-result form is the point, exactly as it is for the committed-public-event capability: a single-result form would hand back an applier that fails only after Host has already acknowledged the command as accepted.
type StaleLeaseEpochError ¶
StaleLeaseEpochError reports an admitted record whose lease epoch is not the epoch the applier currently holds. Applying it would let a superseded owner's admission take effect under a lease it no longer holds.
func (*StaleLeaseEpochError) Error ¶
func (e *StaleLeaseEpochError) Error() string
type ValidationError ¶
ValidationError reports a malformed admitted record or correlation.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string