Sync execution
Definition: sync job A sync job is one immediate execution of the review-sync service for one stored review binding. It reads reviewed items, attempts publication, and stores a machine-readable result. See sync job.
The JobRequest/JobResult/JobSummary contract shape is owned by
sdk/semantic_evaluation and documented in
06-review-sync.md — Job execution.
This page describes what review_sync_service/executor.py actually does when
POST /v1/sync-jobs runs one job.
Execution flow
graph TD
A["POST /v1/sync-jobs"] --> B["1. Validate binding"]
B --> C["2. Resolve Langfuse credentials"]
C --> D["3. Read completed queue items"]
D --> E["4. Filter already-handled versions"]
E --> F["5. Sort pending records"]
F --> G["6. Evaluate each record"]
G --> H["7. Queue cleanup"]
H --> I["8. Advance checkpoint"]
I --> J["9. Persist artifacts"]
J --> K["10. Persist job record"]
K --> L["Return JobResult"]
style A fill:#e5f4fd,stroke:#0098eb,stroke-width:2px
style L fill:#e5f4fd,stroke:#0098eb,stroke-width:2px
For each POST /v1/sync-jobs request (JobRequest: binding_id, dry_run,
max_items, page_size, persist_artifacts):
- Validate the binding. The same semantic checks as
POST /v1/bindings/validate(see 01-bindings.md — Binding validation) run again before anything is read from Langfuse. A binding that fails validation here fails the job withstatus: failed. - Resolve Langfuse credentials. The executor looks up
binding.langfuse_profile_idin the Langfuse profile registry. An unregistered profile fails the job immediately with a message telling the caller to register it first. - Read completed queue items. The shared
LangfuseReviewArtifactReaderreads and normalizes completed items frombinding.queue_id, honoringrequest.page_size(falling back to the service'sREVIEW_SYNC_QUEUE_PAGE_SIZEdefault) andrequest.max_items. - Filter already-handled versions. Records whose
(queue_item_id, record_id, reviewed_at)identity is already present in this binding's checkpoint state are dropped before anything else runs — this is what makes repeated sync jobs safe to run without double-promoting. - Sort the remaining pending records by
(reviewed_at, queue_item_id, trace/observation identity)for a stable, deterministic processing order. - Evaluate each pending record in order:
- If the review policy rejects it, record a
policy_skippedoutcome and continue to the next record. - Otherwise, map it into a promoted record (see
Reviewer comments and score passthrough
below) and count it as
approved_as_isorexplicit_correctiondepending on whether the reviewer supplied a correction. - On a dry run, stop here — mark the record handled without publishing.
- Otherwise, publish it. A
skipped_unchangedoutcome is non-blocking and processing continues. Afailed_publishoutcome is blocking: the job stops immediately, and every later pending record in that job becomesdeferred(attempted again on the next sync job for this binding, since it was never marked handled). - Resolve queue cleanup (see 03-langfuse-profiles-and-cleanup.md — Queue cleanup).
- Advance the checkpoint if this was not a dry run and something actually changed (new handled records or a change in pending cleanup retries).
- Persist artifacts (see Artifacts below) if
persist_artifactsis true (the default). - Persist the job record to
REVIEW_SYNC_JOBS_DIR/{job_id}.json, regardless of outcome, soGET /v1/sync-jobs/{job_id}can return it later.
Promotion outcomes
| Outcome | Blocking? | Meaning |
|---|---|---|
published |
no | The record was written to the golden dataset for the first time (or with changed content) |
skipped_unchanged |
no | The record already exists in the golden dataset with identical content — see publish_promoted_records() |
policy_skipped |
no | The record did not meet the review policy |
failed_publish |
yes | The Langfuse write failed; the job stops and every later pending record becomes deferred |
| deferred | — | Not an outcome value itself — a pending record that was never reached because an earlier record in the same job failed to publish |
graph TD
A["pending record"] --> B{"review policy passes?"}
B -->|"no"| C["policy_skipped\nnon-blocking — continue"]
B -->|"yes"| D["map to promoted record"]
D --> E{"dry run?"}
E -->|"yes"| F["mark handled\nno publish — continue"]
E -->|"no"| G["publish to golden dataset"]
G --> H{"result"}
H -->|"write succeeded"| I["published\nnon-blocking — continue"]
H -->|"content unchanged"| J["skipped_unchanged\nnon-blocking — continue"]
H -->|"write failed"| K["failed_publish\nBLOCKING"]
K --> L["remaining records deferred\njob stops"]
style C fill:#e7eeec,stroke:#55aa77,stroke-width:2px
style F fill:#e7eeec,stroke:#55aa77,stroke-width:2px
style I fill:#e7eeec,stroke:#55aa77,stroke-width:2px
style J fill:#e7eeec,stroke:#55aa77,stroke-width:2px
style K fill:#fdecea,stroke:#cc4444,stroke-width:2px
style L fill:#fdecea,stroke:#cc4444,stroke-width:2px
policy_skipped and skipped_unchanged are reported as HandledRecordOutcome
entries; failed_publish is reported as a single BlockedRecordOutcome (at
most one per job, since it stops processing). See
06-review-sync.md — Outcome types
for the exact field shapes.
Reviewer comments and score passthrough
Reviewer comments and human review scores are direct passthrough, not binding-configurable projections:
- Reviewer comments copy
unchanged from the reviewed record's
reviewer_commentslist into the promoted record's metadata. - Human review scores copy
unchanged from the reviewed record's
review_scoresdict into the promoted record's metadata ashuman_review_scores.
Both live inside EvalCaseRecord.metadata, so they also participate in the
unchanged-content comparison publish_promoted_records() runs before writing
— if only a reviewer's comment changed between two reviews of the same
record, that is enough to make the record published again rather than
skipped_unchanged.
Checkpointing
Checkpoint state (contract shape documented
in
06-review-sync.md — Checkpointing)
is stored per binding by checkpoint_store.py as one JSON file per
binding_id. Two things are specific to how this service maintains it:
- Advancement is conditional. A completed job only writes a new
CheckpointStatewhen it is not a dry run and either it handled at least one record or its pending cleanup-retry set changed. A job that reads the queue and finds nothing new to do leaves the checkpoint untouched. - Cleanup retry state travels with the checkpoint.
pending_cleanup_itemscarries queue items whose deletion failed on the last attempt so a later job can retry them — see 03-langfuse-profiles-and-cleanup.md.
Artifacts
When persist_artifacts is true (the default), the executor writes JSON
artifacts under REVIEW_SYNC_ARTIFACTS_DIR/{binding_id}/{job_id}/. Each
artifact type is written twice per job: as a last_*.json file (always
overwritten, for inspecting the most recent run) and as a timestamped
*_{timestamp}.json file (for historical comparison). The full artifact
list — reviewed, pending, handled, blocked, deferred, and promoted records
plus cleanup candidates, cleanup outcomes, cleanup summary, sync summary, and
before/after checkpoint snapshots — is documented in
Pod-side helpers: persist_review_sync_artifacts().
Promoted-record artifacts include reviewer comments and human review scores
inside record metadata whenever the reviewed record carried them.
Reset local artifacts and state with:
uv run python scripts/reset_local_state.py