InteractionStore
@forge/monorepo / backend/src / InteractionStore
Interface: InteractionStore
Defined in: backend/src/persistence/index.ts:348
Durable human-in-the-loop interactions (docs/04 → Questions & Approvals). Pending questions and
approvals survive restart/deploy. answer/decide are idempotent — the first call resolves the
interaction and reports alreadyResolved: false; a duplicate reports true and changes nothing,
so a continuation is queued exactly once.
Methods
answerQuestion()
answerQuestion(
input):Promise<{alreadyResolved:boolean;question:PendingQuestion; }>
Defined in: backend/src/persistence/index.ts:358
Record answers.
A value may be a string or an array of strings (#155): a multi-select question has several answers, and
QuestionSpec.multiple exists to say so. Widened rather than encoded — comma-joining would make an answer
containing a comma indistinguishable from two answers.
Parameters
input
TenantScope & object
Returns
Promise<{ alreadyResolved: boolean; question: PendingQuestion; }>
claimApproval()
claimApproval(
input):Promise<{approval:PendingApproval;claimed:boolean; }>
Defined in: backend/src/persistence/index.ts:401
Claim the single execution a decided approval authorizes — the mechanism behind allow-once.
A compare-and-set, not a read-then-write: two workers racing a resumed run must see exactly one
claimed: true, because the loser executing anyway is a duplicate publish. claimed is false
for an approval that is already claimed and for one nobody has decided, so an undecided
interaction can never be turned into permission.
Parameters
input
TenantScope & object
Returns
Promise<{ approval: PendingApproval; claimed: boolean; }>
createApproval()
createApproval(
input):Promise<void>
Defined in: backend/src/persistence/index.ts:379
Parameters
input
TenantScope & object
Returns
Promise<void>
createQuestion()
createQuestion(
input):Promise<void>
Defined in: backend/src/persistence/index.ts:349
Parameters
input
TenantScope & object
Returns
Promise<void>
decideApproval()
decideApproval(
input):Promise<{alreadyResolved:boolean;approval:PendingApproval; }>
Defined in: backend/src/persistence/index.ts:381
Parameters
input
TenantScope & object
Returns
Promise<{ alreadyResolved: boolean; approval: PendingApproval; }>
findAnsweredQuestion()
findAnsweredQuestion(
input):Promise<PendingQuestion|null>
Defined in: backend/src/persistence/index.ts:377
The run's answered question — what a resumed run must tell the model — #163.
The approval side has had findDecidedApproval from the start; questions had nothing, so a resumed run
could not learn that its question had been answered. The observable result was a loop: the person picked
an option, the run resumed, the model had no idea and asked the same question again.
No claim, unlike an approval. A claim exists to make an external write happen exactly once; this produces a line of history, which is idempotent — and scoping to the run already bounds it, since the next turn is a different run. Adding a claim would mean a recovered run rebuilt its history without the answer, which is the bug again.
Parameters
input
TenantScope & object
Returns
Promise<PendingQuestion | null>
findApproval()
findApproval(
input):Promise<PendingApproval|null>
Defined in: backend/src/persistence/index.ts:386
One approval by id — how a one-time authorization is verified against what was stored.
Parameters
input
TenantScope & object
Returns
Promise<PendingApproval | null>
findDecidedApproval()
findDecidedApproval(
input):Promise<PendingApproval|null>
Defined in: backend/src/persistence/index.ts:392
The run's decided-but-unclaimed approval: what a resumed run must execute. Decided and claimed are separate states because the decision is the human's and the claim is the runtime's — without the second, nothing distinguishes "approved, waiting to run" from "approved, already run".
Parameters
input
TenantScope & object
Returns
Promise<PendingApproval | null>
findPendingApproval()
findPendingApproval(
input):Promise<PendingApproval|null>
Defined in: backend/src/persistence/index.ts:380
Parameters
input
TenantScope & object
Returns
Promise<PendingApproval | null>
findPendingQuestion()
findPendingQuestion(
input):Promise<PendingQuestion|null>
Defined in: backend/src/persistence/index.ts:350
Parameters
input
TenantScope & object
Returns
Promise<PendingQuestion | null>