agentsclimarketplace

Rubbing ocr review

Skill Lx050/rubbing-to-knowledge/skills/rubbing-ocr-review

书生国智科探挑战赛 · 赛道六 AI for Social Science · 古代碑帖与拓片 — 可复现证据链科研 Skill 系统:12 个离线、确定性、纯标准库 Skill(哑舍小分队 · 西北大学)

Install
npx -y skills add Lx050/rubbing-to-knowledge --skill rubbing-ocr-review

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

3 things to look at

  • 12 days oldThe repository was created 12 days ago. New is not bad, but a brand new repository carrying a familiar-sounding name is the shape a typosquat arrives in, and there has been no time for anyone else to find a problem with it.
  • no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Run auditable OCR baselines on registered rubbing, stele, copybook, and inscription images, preserving empty or failed results, and create append-only, actor-declared review decisions and pending human-review handoffs. Use when comparing OCR across an original and deterministic derivatives, binding readings to image hashes and pixel regions, retaining uncertain or rejected readings, separating AI prelabels from declared-human review, or handing OCR evidence to text-structure workflows.

SKILL.md

22.1 KB, ~5.1k tokens by cl100k_base, as published. Nobody here has run it

Rubbing OCR Review

Create traceable machine candidates and immutable actor-declared review history. Never present OCR output as ground truth, recovered writing, proven history, or completed human review. The current self-attested identity mode can report declared-human coverage, but it can never satisfy the authenticated human gate.

The authenticated human gate is unreachable by construction

Read this before promising anyone that a review will "pass the human gate".

review-verify reports two different things under human_review_gate, and they are routinely confused:

  • declared_human_coverage — does a declaration claiming actor_type: human have a current decision for every reviewable item? This can reach status: complete / satisfies: true. It is what REVIEWER_GUIDE asks a reviewer to aim at, and it is a real, useful result.
  • eligible and satisfies — the authenticated gate. These are hard-coded Python literals ("eligible": False, "satisfies": False in verify_review_path). They are not a queue waiting for a person and not a flag a sufficiently qualified reviewer can flip. No human review, however expert, careful, or well documented, can ever make them true.

The gate is unreachable because the only supported identity assurance is self-attested-not-authenticated: software has no way to check that the person behind a declaration is who they claim to be. Reaching the gate would require an external trusted-identity verifier plus a contract change — not a better reviewer and not more review. Until that exists, --require-human-gate is a correct fail-closed publication stop, and identity-unverified with reason code self_attested_identity_not_authenticated is the expected terminal state.

Do not "fix" this by making the gate satisfiable. A gate that a self-attested actor can pass is worse than no gate: it converts an honest "nobody has verified who reviewed this" into a false claim of verified human review. See HUMAN-REVIEW-HANDOFF-002/REVIEWER_GUIDE.md and the recorded dry run under docs/research/records/runs/OCR-EXIT-FORWARD-001/dry-run/, which shows coverage complete and the gate still blocked in the same command output.

Preconditions

  1. Register and verify the source with research-provenance-ledger.
  2. Pass untrusted images through the project's quarantine and image safety gate.
  3. For a DER input, verify its rubbing-image-preparation manifest.
  4. Choose an OCR adapter whose code, weights, data, and license passed the OCR candidate registry. Do not download or install adapters from this Skill.
  5. Wrap material runs with research-run-ledger when command-level transcripts are required.

Read output-contract.md before integrating an OCR result. Read review-contract.md before creating any review set. Read comparison-contract.md before comparing OCR results. Read contested-export-contract.md before handing contested readings to another Skill. Read human-review-handoff-contract.md before packaging evidence for a real reviewer. Read apple-vision-adapter.md before using the included local baseline.

Build and preflight the local baseline

Compile only the bundled reviewed Swift helper, as an explicit step:

python3 scripts/ocr_review.py compile-apple-vision \
  --output-binary /new/run/apple-vision-adapter \
  --output-manifest /new/run/apple-vision-build.json

The command uses a fixed argv vector with shell: false, refuses existing outputs, and records source, compiler, and binary hashes. It does not fetch code or models.

Confirm the exact revision and language priority:

python3 scripts/ocr_review.py apple-preflight \
  --adapter-binary /new/run/apple-vision-adapter \
  --build-manifest /new/run/apple-vision-build.json \
  --revision 3 \
  --level accurate \
  --language zh-Hant \
  --language zh-Hans \
  --no-language-correction \
  --output-report /new/run/apple-vision-preflight.json

Stop if preflight fails. Never substitute Vision's current default revision.

Run OCR

Run on an original registered image:

python3 scripts/ocr_review.py run \
  --adapter-binary /new/run/apple-vision-adapter \
  --build-manifest /new/run/apple-vision-build.json \
  --input /immutable/source.png \
  --asset-id MAT-RUBBING-001 \
  --image-role OBS \
  --revision 3 \
  --level accurate \
  --language zh-Hant \
  --language zh-Hans \
  --no-language-correction \
  --output /new/run/ocr-original.json

For a derivative, use --image-role DER --der-manifest /prepared/run/manifest.json. The command requires an exact output path and hash match in that manifest and records the parent SHA-256.

Run the same fixed OCR configuration on every justified image variant. Preserve completed, empty, and failed outputs. Do not select a preferred variant from confidence alone.

OCR on a region crop declares two frames, never one

rubbing-image-preparation derive --region X,Y,W,H writes a schema 1.1 derivation manifest carrying a region block. Its pixels are numbered from the region, not from the parent asset, so a box the adapter reports inside the crop is not a parent coordinate. The OCR result therefore ladders too:

OCR schema_versioninputpixel rectangles per box
1.0whole image (OBS, or a DER of a whole image)raw_image_top_left_pixels — the raw pixel frame of the OCR'd file
1.1region crop (a schema 1.1 derivation manifest)region_local_top_left_pixels and parent_raw_top_left_pixels

The rung is a function of the input alone, including for empty and failed results. A whole-image run still writes 1.0, byte for byte as before.

Both frames are recorded and both are named. 1.1 deliberately shares no rectangle name with 1.0: raw_image_top_left_pixels is absent from a region document. A consumer that looks that key up gets nothing instead of a confidently wrong whole-image coordinate — which is the only acceptable outcome, because the value of a coordinate here is that it points at the right pixels.

  • region_local_top_left_pixels — inside the OCR'd crop. Use this to crop the crop, e.g. for a review image.
  • parent_raw_top_left_pixels — in the parent asset's raw sensor frame. Use this to compare against a whole-image observation, another region, or the parent asset's own pixels.
  • coordinate_frame.frame_idregion-crop-raw-pixels, naming the frame the crop-local rectangles live in.
  • coordinate_frame.region_projection — the full mapping: region_offset, region_dimensions, parent_derived_dimensions, parent_raw_dimensions, parent_exif_orientation, the two box field names, and composition_order.
  • input.derivation.region and input.derivation.parent_coordinate_transform — the same facts as read from the manifest. verify re-reads the manifest and requires the two copies to agree.

The composition order is load-bearing. rubbing-image-preparation declares the region offset in the parent's derived frame — the frame that exists only after the parent's EXIF orientation has been applied. So:

crop-local box
  -> add region_offset            (still in the parent's DERIVED frame)
  -> inverse EXIF orientation     (into the parent's RAW frame)
  = parent_raw_top_left_pixels

composition_order records this as add-region-offset-in-parent-derived-frame-then-inverse-exif-to-parent-raw. Reversing the two steps agrees for EXIF orientation 1 and disagrees for all seven rotated or mirrored orientations, usually landing on a plausible but wrong rectangle inside the parent. Do not reimplement the mapping; read region_projection or use parent_raw_top_left_pixels.

A worked round trip on a real asset — a region derived from the Met 36445 leaf, OCR'd through this command, mapped back onto a whole-image observation of 首 — is recorded under docs/research/records/runs/REGION-COMPOSITION-001/.

What still refuses a region result

compare, review-init, and review-handoff-create refuse a schema 1.1 OCR result with an explicit error. Their own contracts copy the rectangle under the 1.0 name and have no field in which to say which frame it belongs to, so consuming one would strip the region frame and republish a region coordinate as a whole-image coordinate. Until those contracts grow their own region rung the refusal is the correct answer, not a limitation to work around by relabelling the document.

Verify before review or handoff

python3 scripts/ocr_review.py verify \
  --ocr-result /new/run/ocr-original.json \
  --output-report /new/run/ocr-original.verify.json

Require status: pass. Verification checks schema semantics, coordinate transforms, current input bytes, adapter binary/build hashes, and any DER parent chain.

Compare machine outputs without choosing a winner

Compare an original and one or more deterministic derivatives:

python3 scripts/ocr_review.py compare \
  --ocr-result /new/run/ocr-original.json \
  --ocr-result /new/run/ocr-grayscale.json \
  --ocr-result /new/run/ocr-binary.json \
  --comparison-id COMPARE-RUBBING-001 \
  --output-report /new/run/ocr-comparison.json

The command is read-only over OCR inputs and always verifies their current files, build artifacts, and complete DER chains. It rejects parameter objects that differ under exact canonical JSON comparison—including provider extensions and JSON types—or different evidence-family roots. It preserves completed, empty, and failed, and reports every observation, all candidates, rank 1, Unicode code points, confidence, per-observation character-box coverage, failures, complete runtime engine identity, and exact machine-output differences.

Do not use the comparison report as accuracy, CER, transcription truth, or an automatic image-variant ranking. Observation IDs are local to each run and are not automatic physical-glyph alignments. Verification reloads every bound OCR source and reconstructs the full comparison inputs, summary, and fixed machine-output difference set. The SHA-256 returned by current-file verification must equal the bytes reloaded for reconstruction, closing the verification/read snapshot race. Injected difference kinds—including any accuracy or CER claim—are rejected even when their counts are edited to look consistent.

Hand a verified comparison to other Skills

A comparison report is evidence, but on its own it leaves this Skill only as prose — which is why every formal record under REAL-RUBBING-SLICE-002/formal-records/ was hand-authored. Two commands give it typed exits. Both reload and reconstruct the comparison from its own OCR sources first, so an edited report cannot become a downstream record.

Emit the disputed glyphs, their machine candidates, their code points, and their raw-image regions:

python3 scripts/ocr_review.py export-contested-observations \
  --comparison-report /frozen/ocr-comparison.json \
  --export-id EXPORT-RUBBING-001 \
  --repo-root /path/to/repo \
  --output-export /new/run/contested-observations.json

Observations from different runs are grouped when their boxes overlap in the shared raw frame. That grouping is a machine geometry hypothesis: every item records grouping_method, pins physical_glyph_identity to not-established, and leaves adjudication null. Observation IDs are local to each run and are never an automatic physical-glyph alignment. An item is contested when variants disagree on the top-1 reading, when they offer several candidate readings, or when some variants report nothing at that region at all — absence is disagreement, and a failed run is excluded because it could not testify.

Turn that export into ResearchCase records:

python3 scripts/ocr_review.py export-case-records \
  --contested-export /new/run/contested-observations.json \
  --repo-root /path/to/repo \
  --output-dir /new/run/case-records \
  --material-id MAT-RUBBING-CONTESTED-EXPORT \
  --evidence-id-prefix EVD-RUBBING-CONTESTED \
  --independence-group one-engine-one-source-family \
  --source-url local://run/contested-observations.json \
  --publisher "your team" \
  --license PROJECT-RECORD \
  --rights-status restricted

The output is one DER material record plus one machine evidence record per contested item, and a manifest giving the order to apply them with orchestrate.py record. Order is load-bearing: an evidence record naming a material the case does not yet contain is rejected, so the material goes first. Evidence is always written machine-checked, never human-checked. The command re-derives the export from its comparison and refuses a hand-edited one. It never invents licensing or rights: those are required arguments.

Review without changing machine output

Create one actor declaration per review set. Keep the subject ID non-personal; actor_type, actor_id, and role_id are separate fields. A human declaration must self-attest direct visual review and independence while explicitly recording that software did not authenticate the person's real-world identity.

Initialize a review set:

python3 scripts/ocr_review.py review-init \
  --ocr-result /new/run/ocr-original.json \
  --review-set-id REVIEW-RUBBING-001 \
  --actor-declaration /new/run/actor-declaration.json \
  --output-review /new/run/review-v001.json

Prepare one decision JSON with exactly decision_id, known item_id, status, rationale, and timezone-aware created_at, plus corrected_text or supersedes_decision_id only when applicable. The CLI injects actor, source OCR, and coordinate-region hashes from the immutable review set; a decision file may not claim its own actor.

Append it as a new version and event:

python3 scripts/ocr_review.py review-append \
  --review /new/run/review-v001.json \
  --decision /new/run/decision-001.json \
  --output-review /new/run/review-v002.json \
  --output-event /new/run/review-event-001.json

python3 scripts/ocr_review.py review-verify \
  --review /new/run/review-v002.json

Never edit an older OCR result, review state, or event. Use accepted, corrected, uncertain, or rejected; preserve uncertainty and rejection as research outcomes.

review-verify returns integrity-pass for a valid chain and separately reports declared_human_coverage inside human_review_gate. A complete coverage result means only that a declaration claiming actor_type: human has a current decision for every reviewable item. Under the only supported identity assurance, self-attested-not-authenticated, human_review_gate.satisfies is always false and a fully covered chain remains identity-unverified. Use --require-human-gate as a fail-closed stopping or publication gate; it remains blocked until a future trusted external identity verifier is implemented. A legacy v1.0 chain remains verifiable but is always ineligible and read-only, even if its object name contains human or its role starts with role-.

Correct an actor misclassification

Do not edit an old mislabelled chain. Create a machine-readable correction index. Schema 1.1 requires the original mislabelled ocr-human-review-summary as an authoritative discovery index. It binds the summary bytes, its ordered latest review tips, and its complete decision-ID inventory, then binds every external decision file and any superseded artifact by current SHA-256:

python3 scripts/ocr_review.py review-correction-create \
  --legacy-discovery-index /frozen/mislabelled-summary.original.json \
  --legacy-review /frozen/review-a-v002.json \
  --decision-file /frozen/decision-a.json \
  --superseded-artifact /frozen/mislabelled-summary.json \
  --correction-id CORRECTION-RUBBING-001 \
  --actual-actor-type ai-agent \
  --actual-actor-id agent-ocr-previewer \
  --reason "AI pre-review was incorrectly labelled as human review." \
  --output-index /new/run/actor-correction-index.json

The correction command can only downgrade legacy semantics to ai-agent or software; it can never promote old evidence to human. The ordered --legacy-review inputs must exactly equal the discovery summary's authoritative latest tips, and the external decision IDs must exactly equal the summary's observation decision IDs. Passing older ancestors, omitting a decision, or adding an unindexed decision is rejected. Verification reloads the discovery source and all bound files. Verify the result with review-correction-verify.

Prepare, but do not perform, human review

Create a blocked handoff from the exact OCR inputs, comparison, correction index, ResearchCase, and preregistration:

python3 scripts/ocr_review.py review-handoff-create \
  --ocr-result /frozen/ocr-original.json \
  --ocr-result /frozen/ocr-derivative.json \
  --comparison-report /frozen/ocr-comparison.json \
  --actor-correction-index /new/run/actor-correction-index.json \
  --research-case /frozen/case-final.json \
  --preregistration /frozen/preregistration.md \
  --repo-root /path/to/repo \
  --handoff-id HANDOFF-RUBBING-001 \
  --output-handoff /new/run/handoff.json \
  --output-actor-template /new/run/actor-declaration.template.json \
  --output-decision-template /new/run/decision.template.json

Schema 1.3 packets are portable. Every path is POSIX-relative to --repo-root, and path_encoding.handoff_dir records where the document sits so verification recovers the root from the document's own location — no marker files, no guessing, and a wrong root fails closed instead of resolving to something plausible. Schemas 1.0–1.2 wrote absolute paths and could only be verified on the machine that produced them; those artifacts stay readable exactly as written and are never rewritten.

Acquisition images and image-preparation manifests usually live under a gitignored data/ tree. A portable packet binds them by SHA-256 and marks them location: outside-repository with a null path, because a repo-relative path to a file the repository does not carry is a promise it cannot keep. Nothing is weakened: those bytes are still read and hash-checked through the OCR chain on every verification.

Each task also carries a reference_lookup slot. Empty is a complete, valid state, so a later automated pass can attach literature cross-references without reissuing the packet. Only non-human actors may populate it, every entry pins satisfies_human_gate: false, and a lookup is never a reading or a verdict — a reviewer's judgement goes through the decision template.

The handoff always remains pending-human-review, preserves empty and failed OCR outcomes, does not assign physical regions automatically, and emits only invalid blank templates. It never creates a human declaration or decision. Its comparison is accepted only after full reconstruction from the current OCR sources, including the fixed difference set; arbitrary accuracy/CER differences cannot enter the package. Run review-handoff-verify before delivery. Record material correction, handoff, and future human commands with research-run-ledger.

Every materialization path must be new. Never overwrite or reuse an earlier correction index, handoff, template, verification report, or command ledger; when a contract changes, retain the old artifact as superseded evidence and write a new versioned output.

Gates

  • Reject symlinks, non-regular files, path traversal, unsupported image magic, byte/pixel-limit violations, changed hashes, and existing outputs.
  • Keep the original or specific DER hash and oriented coordinate frame attached to every observation.
  • Keep raw Vision bottom-left rectangles; never synthesize missing per-character boxes.
  • Compare only exact parameter matches within one source-image hash family; preserve empty and failed outcomes instead of filtering them.
  • Bind actor type, a non-personal subject/agent/software ID, and role ID separately; a role string is never proof of a human actor.
  • Treat self-attestation as a structured claim, not authentication of biological identity, expertise, or independence.
  • Report complete declared-human coverage separately from the authenticated human gate; self-attestation never makes that gate pass. Treat eligible/satisfies as unreachable by construction and say so plainly instead of implying a reviewer could satisfy them.
  • Keep the contested-observation export free of adjudications and physical glyph identities; a machine grouping is a hypothesis, not an alignment.
  • Emit exported evidence as machine-checked only; an export never claims a human checked anything.
  • Write portable packets with repo-relative paths; bind gitignored inputs by hash rather than publishing a machine-local path.
  • Treat Pillow as an optional dependency for context-image rendering only; every other command must keep working without it.
  • Require a new v1.1 set for real human review; never append to a legacy v1.0 chain to upgrade its semantics.
  • Keep AI advisories outside the human gate and require a schema 1.1 correction index bound to the original legacy discovery summary whenever a handoff includes mislabelled legacy chains.
  • Recompute comparison inputs, summary, and fixed difference kinds from the current OCR sources; reject injected accuracy/CER claims.
  • Preserve old materialized evidence and use new exclusive output paths for every corrected or superseding artifact.
  • Treat Apple Vision as a Darwin-only proprietary baseline, not the SCP portable default and not evidence of ancient-rubbing accuracy.
  • Add future PaddleOCR or Tesseract adapters behind the provider-neutral contract only after their registry and license gates pass.

What ships with it: 12 files

530.1 KB alongside SKILL.md, 2 of them executable

agents/

scripts/

tests/

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.