Page results and provenance¶
On-disk location and naming: project-on-disk.md. Export projection of these fields: notebook-export.md. Multipass orchestration: ocr-multipass.md. Preference ledger: ocr-preference.md.
File identity¶
Path:
results/<page_id>.jsonformatmust be"transcribe.page-result"schema_versionmust be1Payload
page_idmust match the filename stemAdditive fields (
preferred_attempt_id,comparison, attemptattempt_kind/pass_id/source_attempt_ids) are optional on older files; readers treat missing as defaults
Attempts¶
OCR generations are append-only attempt records (capped retention in implementation; default cap 40)
Each attempt has a status in
{running, succeeded, failed, cancelled, interrupted}attempt_kind:vision(default) |compositepass_id(optional) ties attempts from one multipass runComposite attempts carry
source_attempt_ids(vision attempt ids used as merge inputs) and optionalcomposite_noteactive_attempt_idselects the attempt that owns current derived status / raw textWhitespace-only provider output is failed with
error.code=empty_output(notsucceeded). Cleanup may still recordskipped_empty_sourceon that failed attempt. Failed / running / empty writes do not replace a prior succeeded-with-textactive_attempt_id. A first-ever attempt on a page may still become active whenactivateis true (the page then shows Failed OCR).Historical empty
succeededattempts (legacy DeepSeek-OCR + long faithful prompt) are repaired on Review: rewritten tofailed/empty_output, andactive_attempt_idrestored to the latest succeeded-with-text reading when one exists.preferred_attempt_id(optional) records user preference; may differ from active underprefer_onlymodeInterrupted reconciliation: when the job lock is free,
runningattempts becomeinterruptedRetention must never drop
active,preferred, the latest succeeded attempt per(model_name, digest), or the latest composite for the currentpass_idwhen possible within the cap
Effective text¶
edited_textis user-ownedIf
edited_textis notnull, effective text is the editOtherwise effective text is the active attempt’s
raw_textRe-running OCR must not clear a user edit
Promoting / preferring an attempt must not clear
edited_textunless the user explicitly chooses adopt-new underprefer_promote_with_edit_gate
Optional additive page-level provenance (not per-token):
effective_text_origin:ocr_attempt|composite|human_selected|human_corrected— how the current effective text was established. Absent on legacy files.reviewed_text_fingerprint/reviewed_evidence_fingerprint— identity of effective text and OCR evidence (merge-input vision attempt ids + current composite id) when the page was marked reviewed. Required for a validreviewedstatus onPageIndex.review_status.source_disagreement_count/agreement_ratio— optional cached source-only alignment signals (raw vision attempts; never counting composite or the editor as votes)
A stored reviewed is valid only while both fingerprints match. Central invalidation on write (and a load-time repair) moves reviewed → needs_attention when effective text, active_attempt_id, current merged draft, or newly available source OCR evidence changes. Skip is sticky; reviewed is not.
Composite / merged draft¶
Composite attempts (attempt_kind=composite) are an LLM reconciliation of independent vision attempts, not another OCR vote. source_attempt_ids lists the vision attempts consumed. A composite is current iff that set equals the latest succeeded vision attempt per (model_name, digest) that would feed a new merge; otherwise it is stale. Stale composites are retained (append-only). At most one current composite is the live merged draft. User-facing name: Merged draft. Rank lists never include composites.
Prefer / promote¶
Prefer modes (workspace default + per-notebook OCR override). User-facing guide: runtime/ocr.md.
Mode |
Review UI label |
Prefer behaviour |
|---|---|---|
|
Notebook default = current text |
Sets |
|
Notebook default only (stats / fine-tune) |
Sets |
|
Notebook default + current, with edit gate |
Sets preferred and active; if |
Promote (set_active_attempt) always sets active_attempt_id to a succeeded attempt and does not clear edits.
When auto_activate_composite is true (default), multipass sets active (and preferred under prefer_is_promote) from a succeeded merged draft and seeds Transcription — see ocr-multipass.md activation phase.
Comparison record¶
Optional comparison on the page (last successful multipass rank):
pass_id,ranked_attempt_ids(vision only, best-first), optional per-entry score/rationaleRanker model / prompt provenance
Composite attempts must never appear in
ranked_attempt_ids
Fingerprints (persisted)¶
Successful attempts store input_fingerprint and a canonical fingerprint_payload including provider, model name, model digest, model identity verification flag, input image hash (after preprocess), prompt hash, preprocess profile/version, and generation options.
Vision generate always sends a num_predict cap (default 4096). The default cap is omitted from hashed generation_options so skip/resume stays compatible with attempts that only stored temperature. A non-default num_predict is included and changes the fingerprint.
When optional post-OCR cleanup is enabled for the job, the fingerprint also includes a cleanup object: mode, cleanup model name/digest/verified flag, cleanup prompt id/version/sha256, and cleanup_validator_policy_id / cleanup_validator_policy_version. When cleanup is disabled, the cleanup key is omitted so fingerprints remain compatible with pre-cleanup attempts.
Some vision tags use a model recipe (name match → short prompt such as free_ocr) frozen into the plan; that prompt hash participates like any other prompt_sha256. Custom notebook prompts always win over a recipe. See runtime/ocr_model_recipes.md.
Skip/resume policy for single-model jobs (runtime): a page may be skipped only when the frozen job plan has verified model identity and the recomputed fingerprint matches a succeeded active attempt. Unverified model identity is non-cacheable for skip.
Skip/resume for multipass vision phases: skip when verified identity matches any succeeded vision attempt fingerprint on the page (not only active), so rematching models stay cacheable while accumulating candidates.
Job execution freezes plan fields at start; see ARCHITECTURE.md for shape. Generation writes may pass activate=false so multipass does not flip active until promotion / auto-composite policy.
Provenance¶
Successful attempts carry provenance suitable for audit/export: model identity, prompt text/ids/hashes, preprocess, application version, Ollama host, render id, request id, and allowlisted provider timing/token metadata.
Optional cleanup record¶
When a job plan enables cleanup, each succeeded attempt may include a cleanup object. Cleanup never changes OCRAttempt.status, attempt timestamps’ OCR meaning, or page success/failure: vision OCR success still yields status=succeeded even if cleanup fails or is rejected.
Fields:
execution_status:disabled|skipped_empty_source|provider_ok|provider_failedacceptance_status:not_applicable|applied|unchanged|validator_rejectedmode,model_name,model_digest, cleanupprompt_*,note(stable machine code)pre_cleanup_text: set only whenacceptance_status=applied(immutable audit of vision OCR); otherwise nullBounded diagnostics on reject/fail:
original_length,candidate_length,length_ratio— rejected candidate text is not persistedcleanup_validator_policy_id/cleanup_validator_policy_version
Whitespace-only vision OCR skips the cleanup model (execution_status=skipped_empty_source) and the attempt is failed (empty_output) — not a succeeded blank page. Plan-time misconfiguration (invalid mode, missing/unsuitable cleanup model, unverified digest) rejects job start. Runtime model disappearance or digest drift is page-level fail-soft (provider_failed) and keeps vision raw_text.
raw_text and cleanup for a succeeded attempt are committed in one write. Interrupted running attempts must not leave cleaned raw_text without a complete cleanup record.