Class ProposalStore
Scope: single-process only, like the plan store. Tokens are valid only
for the lifetime of this server process; if the MCP host restarts the server, outstanding
proposals are gone and must be re-proposed. Entries expire after ttl() (default
10 minutes) and the store keeps at most capacity() live proposals. Minting beyond
capacity evicts the oldest live proposal; mint(ChangeType, String, String, String, Instant, String) returns what it evicted so the caller of
fabric_propose_change is told, and a later confirm of an evicted token resolves to
ProposalStore.Outcome.EVICTED.
Single use, consumed on attempt: consume(String) removes a live
proposal on the first attempt, before the caller executes anything - a failed
execution does not resurrect the token. Recently consumed and recently expired tokens are
remembered (for one further TTL window) so a second confirm gets a precise
ProposalStore.Outcome.REPLAYED or ProposalStore.Outcome.EXPIRED answer instead of a generic
ProposalStore.Outcome.UNKNOWN.
Thread-safe; all methods synchronize on the store. The clock is injectable for deterministic expiry tests.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordThe result of one consume attempt.static final recordThe result ofmint(ChangeType, String, String, String, Instant, String).static enumHow aconsume(String)attempt resolved. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final intDefault maximum number of concurrently pending proposals.static final DurationDefault time-to-live for a pending proposal. -
Constructor Summary
ConstructorsConstructorDescriptionCreates a store with the default TTL (10 minutes) and capacity (20 proposals).ProposalStore(Clock clock, Duration ttl, int capacity) Creates a store with explicit clock, TTL, and capacity. -
Method Summary
Modifier and TypeMethodDescriptionageOf(PendingChange change) The time elapsed since a proposal was minted, measured on this store's clock.intcapacity()Consumes a confirm token.booleanisExpired(PendingChange change) Whether a proposal's TTL has elapsed on this store's clock.mint(ChangeType changeType, String canonicalSpec, String specSha256, String requestSha256, Instant compiledAt, String priceSummary) Mints a proposal: a fresh unguessable token bound to the SHA-256 of the compiled request, expiring one TTL from now.now()intsize()ttl()
-
Field Details
-
DEFAULT_TTL
Default time-to-live for a pending proposal. -
DEFAULT_CAPACITY
public static final int DEFAULT_CAPACITYDefault maximum number of concurrently pending proposals.- See Also:
-
-
Constructor Details
-
ProposalStore
public ProposalStore()Creates a store with the default TTL (10 minutes) and capacity (20 proposals). -
ProposalStore
-
-
Method Details
-
now
- Returns:
- the current instant on this store's clock (the reference time a proposal is compiled at)
-
mint
public ProposalStore.Minted mint(ChangeType changeType, String canonicalSpec, String specSha256, String requestSha256, Instant compiledAt, String priceSummary) Mints a proposal: a fresh unguessable token bound to the SHA-256 of the compiled request, expiring one TTL from now. When the store is at capacity the oldest live proposal is evicted first and returned inProposalStore.Minted.evicted().- Parameters:
changeType- the kind of create the proposal performscanonicalSpec- the canonical-form spec JSON the dry run validatedspecSha256- the SHA-256 (hex) ofcanonicalSpecrequestSha256- the SHA-256 (hex) of the compiled request the confirm phase will sendcompiledAt- the reference instant the request was compiled at; the confirm phase recompiles at the same instantpriceSummary- the one-line price context shown with the proposal; may benull- Returns:
- the pending change and any proposals evicted to make room for it
-
isExpired
Whether a proposal's TTL has elapsed on this store's clock. The confirm phase asks again after the human prompt returns, because the prompt may have been open past the expiry.- Parameters:
change- a proposal minted by this store- Returns:
truewhen the proposal is no longer within its TTL
-
consume
Consumes a confirm token. A live token is removed on this attempt and returned withProposalStore.Outcome.CONSUMED; every other outcome carries no change.- Parameters:
token- the confirm token presented byfabric_confirm_change- Returns:
- the consumption result - exactly one of consumed, expired, replayed, or unknown
-
ageOf
The time elapsed since a proposal was minted, measured on this store's clock.- Parameters:
change- a proposal minted by this store- Returns:
- the age; zero when the clock reads earlier than the mint instant
-
size
public int size()- Returns:
- the number of live (confirmable) proposals currently pending
-
ttl
- Returns:
- this store's time-to-live per proposal
-
capacity
public int capacity()- Returns:
- this store's maximum number of concurrently pending proposals
-