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-v006";}They must implement all stores required by the current contract and return a valid preparation result from prepare():
return { providerId: "my-adapter", storageContractRevision: "storage-v006", 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. Only one runner process may execute jobs; additional replicas must setREVIEWPHIN_JOB_RUNNER_ENABLED=false. ReviewPhin ships a reusable single-worker queue helper on top of the generic entity store that you can reuse when declaring this topology.
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-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.