Rubbing ocr review
书生国智科探挑战赛 · 赛道六 AI for Social Science · 古代碑帖与拓片 — 可复现证据链科研 Skill 系统:12 个离线、确定性、纯标准库 Skill(哑舍小分队 · 西北大学)
npx -y skills add Lx050/rubbing-to-knowledge --skill rubbing-ocr-reviewAssembled 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 claimingactor_type: humanhave a current decision for every reviewable item? This can reachstatus: complete/satisfies: true. It is what REVIEWER_GUIDE asks a reviewer to aim at, and it is a real, useful result.eligibleandsatisfies— the authenticated gate. These are hard-coded Python literals ("eligible": False, "satisfies": Falseinverify_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
- Register and verify the source with
research-provenance-ledger. - Pass untrusted images through the project's quarantine and image safety gate.
- For a
DERinput, verify itsrubbing-image-preparationmanifest. - Choose an OCR adapter whose code, weights, data, and license passed the OCR candidate registry. Do not download or install adapters from this Skill.
- Wrap material runs with
research-run-ledgerwhen 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_version | input | pixel rectangles per box |
|---|---|---|
1.0 | whole image (OBS, or a DER of a whole image) | raw_image_top_left_pixels — the raw pixel frame of the OCR'd file |
1.1 | region 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_id—region-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, andcomposition_order.input.derivation.regionandinput.derivation.parent_coordinate_transform— the same facts as read from the manifest.verifyre-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
DERhash 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/satisfiesas 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-checkedonly; 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/
- openai.yaml451 B
references/
- apple-vision-adapter.md2.4 KB
- comparison-contract.md3.3 KB
- contested-export-contract.md4.2 KB
- human-review-handoff-contract.md10.8 KB
- ocr-result.schema.json22.6 KB
- output-contract.md7.6 KB
- review-contract.md7.9 KB
scripts/
- apple_vision_adapter.swift13.8 KB
- ocr_review.pyruns326.6 KB
tests/
- test_ocr_review.pyruns129.4 KB
- requirements.txt1.0 KB