Custom storage adapters
Custom storage adapters let ReviewPhin persist to PostgreSQL, MySQL, a cloud key-value store, or internal storage. They are loaded with STORAGE_PROVIDER_MODULE.
STORAGE_PROVIDER_MODULE=@my-org/reviewphin-postgresRequired contract
Section titled “Required contract”Adapters must report the current storage contract revision:
getSupportedStorageContract(): string { return "storage-v007";}They must implement all stores required by the current contract and return a valid preparation result from prepare():
return { providerId: "my-adapter", storageContractRevision: "storage-v007", appliedMigrationIds: [],};Each entity store implements get, getMany, find, list, upsert, upsertMany, replace, replaceMany, update, updateMany, patch, patchMany, delete, and deleteMany.
Use src/storage/adapters/README.md and the SQLite adapter as implementation references.
Claim-aware interaction jobs
Section titled “Claim-aware interaction jobs”storage-v005 makes the interaction-job store claim-aware. Beyond the standard store operations, it must expose a claimMode and claim-scoped operations that fence work by claim token:
claimModeis"atomic"or"single-worker".claimNextrecovers expired leases, preserves single active execution, selects an eligible job (status = queued,enqueuedAt >= queuedAfter,availableAt <= now) ordered byavailableAt, thenenqueuedAt, then id, and claims it with a fresh token.renewClaim,transitionClaim, and the*ForClaimrun/finding/metric/snapshot/mapping operations returnfalseornullwhen the token no longer owns the job. Callers treat that as lease loss and stop writing for that attempt.expireQueuedandreconcileOrphanedInteractionRunsprovide bounded maintenance.- Ordinary
EntityStoremutations of an existingin_progressjob must be rejected so they cannot bypass fencing.
Pick a claim mode by what your backend can guarantee:
atomic— the backend can select-and-claim in one atomic step (like SQLite’sBEGIN IMMEDIATE). Global single-review execution holds even with many runner processes.single-worker— the backend has no cross-request compare-and-set. The built-in helper puts batch updates and job claims in a queue inside the adapter and completes one before starting the next. Run only one copy of ReviewPhin receiving comment webhooks and executing jobs when using this helper. Disabling job execution in a second copy does not make its batch writes safe. A custom adapter needs shared coordination to support multiple copies.
The claim token fences an abandoned attempt after its lease expires. Because a third-party provider request cannot be made transactional with the storage lease, an in-flight external call may still finish after lease loss; exactly-once external side effects remain outside the fencing guarantee. Project-memory writes are also outside claim fencing by design.
Contract revision notes
Section titled “Contract revision notes”-
storage-v007(breaking) addsinteractionRequests, nullablebatchKindon jobs, nullablerepliesJsonon runs, and nullable routing model and effort on profiles. ImplementadmitInteractionTrigger,setInteractionJobHeadForClaim, andsaveInteractionRunRepliesForClaim. Admission must deduplicate by tenant and provider event, append only to unclaimed collecting jobs, and serialize membership/deadline changes with claims. SQLite uses a transaction. The reusable single-worker helper stages requests and recovers interrupted remote writes before admission or claim; its queue protects only one running copy of ReviewPhin, as described above.repliesJsonis a versioned batch checkpoint containing routing decisions, saved review/reply output, phase completion, and reply publication markers.resultJsonremains a review result. Backfill one request for every legacy job without rewriting its payload or opening it for collection. -
storage-v006(breaking) changes interaction-run metrics from one record per run to one record per harness session. Adapters identify a session by interaction run, harness, and harness-native session key.sessionTypeandusageUnitare open strings, not enumerations.usageUnitandusageAmountmust either both be present or both be absent. Per-model usage JSON uses the same unit. Existing premium-request rows must keep their counters, receive deterministic legacy session identity, and allocate their usage to theunknownmodel. Preserve provider-managed timestamps where the backend permits it; Flotiq retainscreatedAtbut advancesupdatedAtwhen the row is backfilled, and cross-adapter migration may assign both timestamps again. Filters add inclusivegteand exclusiveltrange operators; adapters must return the same boundary behavior and should translate supported ranges to backend filters before pagination. -
storage-v003added provider-owned interaction trigger identity throughInteractionJobRecord.triggerJsonand madecommentIdnullable. Built-in migrations preserve existing GitLab jobs and synthesize trigger JSON from the existing comment id. -
storage-v004addedProjectMemoryRecordand theprojectMemoriesstore. There is at most one project memory record per tenant, with the record id equal to the tenant id. Built-in adapters delete that row during tenant deletion and include it in tenant deletion summaries. -
storage-v005(breaking) added claim-aware interaction-job operations, the terminal"expired"status, and job fieldsavailableAt,claimToken,claimedBy,claimExpiresAt, andlatestInteractionRunId. It also added nullablereviewReasoningEffortandtextGenerationReasoningEffortto model profiles and interaction runs, aninteractionJobClaimTokensnapshot on runs, and nullableinteractionRunIdon code-review snapshots. Migrations preserve existing rows, backfillavailableAtfromenqueuedAt, and leave new nullable fields empty. Migrate withavailableAtoptional first, backfill legacy rows, then require it. Stop all v004 processes before migrating so a recovered job row cannot keep running under an old worker.
Verify a new adapter
Section titled “Verify a new adapter”Point STORAGE_PROVIDER_MODULE at the adapter and use the CLI against it: tenant add, tenant list, then a real review. storage migrate can seed it from an existing SQLite database — see migrating between adapters.