Skip to content

Batch 3D: textord/ccstruct shapes onto V3 SoA facets + OCR arc board entries#657

Merged
AdaWorldAPI merged 24 commits into
mainfrom
claude/happy-hamilton-0azlw4
Jul 7, 2026
Merged

Batch 3D: textord/ccstruct shapes onto V3 SoA facets + OCR arc board entries#657
AdaWorldAPI merged 24 commits into
mainfrom
claude/happy-hamilton-0azlw4

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Core change

lance-graph-contract/src/textord_facet.rs — Batch 3D of the tesseract-rs OCR arc: the 7 textord data shapes (TBOX, BLOBNBOX, ROW, TO_ROW, BLOCK, TO_BLOCK, POLY_BLOCK) sunk onto facet::FacetCascade per the network-sink precedent (E-OCR-NETWORK-SINK-1):

  • classid = compose_classid(<minted 0x0805..0x0807 canon>, <shape ordinal custom-low>) — reuses existing mints (textline/blob/page_layout), no new mints.
  • Payload = the 6×8:8 rail carving with a documented field→rail table per shape (i16 box coords full precision; floats rounded / fixed-point with stated loss, e.g. TO_ROW baseline slope ×10000).
  • Field inventory comes from the banked ruff walk_tu manifest (239 classes, 14259 triples — no vtable hierarchy, so the sink is field inventory, not a ClassView); header line citations per field (rect.h / blobbox.h / ocrrow.h / ocrblock.h / polyblk.h).
  • Intrusive ELIST links + pointers routed to EdgeBlock per doctrine; list content stays content-store.
  • 10 new tests (round-trip per shape, classid composition, distinctness); example textord_facet_dump as the future oracle seam. Full crate: 849 lib tests green, fmt + clippy -D warnings clean.

Board (append-only EPIPHANIES entries for the OCR arc landed this branch)

E-OCR-INPUT-LAYER-1 (P2 pixconv+Otsu 3/3+3/3), E-OCR-PAGE-APPROX-1 (P3-alt page E2E + Batch-3A harvests), E-OCR-CONNCOMP-1/E-OCR-TSV-1 (3B 6/6 + P4a), E-OCR-TEXTORD-FACET-1/E-OCR-SCANNED-PDF-1 (3D + D5.2), E-OCR-SEARCHABLE-PDF-1 (D4.5 round-trip; P4+P5 complete; 3E cut).

Companion consumer PR: AdaWorldAPI/tesseract-rs#11.

🤖 Generated with Claude Code

https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added OCR layout/facet support for text blocks, lines, pages, and related shape types.
    • Added broader dictionary/graph loading and inspection support, plus new OCR concept entries.
    • Introduced example tools for inspecting graph and layout data from files.
  • Documentation
    • Added a GitHub access runbook with practical guidance for clone, push, pull request, and release workflows.
  • Chores
    • Updated internal planning notes and status logs for the OCR pipeline rollout.

claude added 22 commits July 7, 2026 05:16
…arity green

The network loader + full composed forward (tesseract-ocr B1) reproduces
libtesseract's net->Forward bit-for-bit on the REAL eng.lstm across 8/8
synthetic image widths (softmax f32 output; num_weights 385807 self-check).
The recognizer now spans image-grid -> softmax logits; with Leaf 7 already
spanning logits -> text, only B2 (LSTMRecognizer load) + A6/B3 (leptonica
Input + RecognizeLine) remain to close image -> text.

Core-First: B1 consumes the Core's proven NetworkHeader (E-OCR-NETWORK-SINK-1);
no new Core type. This board entry is the durable record per Core-First iron
rule 3 (board hygiene lands in lance-graph; tesseract-rs carries the consumer
wiring, commit eb99e84/39c78bd).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…byte-parity green

The full recognizer now loads from disk: LstmRecognizer::from_components parses
the lstm component's 81-byte trailing fields (byte-identical vs a public-API
libtesseract oracle) and assembles the already-proven network (B1) + charset
(E-CPP-PARITY-1..6) + recoder (E-CPP-PARITY-7) with null_char=110. Only the
leptonica front-end (A6 Input + B3 RecognizeLine) remains to close image->text;
A6 is the one unproven leaf and the one architectural fork (leptonica dep vs
pure-Rust decode) to raise to the operator.

Core-First: B2 is assembly in the tesseract-ocr consumer; no new Core type.
This board entry is the durable record per iron rule 3 (tesseract-rs commit
de0d60a).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…id) byte-parity green

The pixel -> int8 grid step ships (from_grey_pix): ComputeBlackWhite middle-row
extrema -> STATS(0,255) -> black/white percentiles -> SetPixel x128 quant, all
transcoded exactly and byte-identical vs a public-API FromPix oracle across 8/8
image widths. The x128 (INT8_MAX+1) quant constant is distinct from
write_time_step's x127 — a documented gotcha. Only A6b (leptonica image
decode+pixScale, pure-Rust per the founding directive) remains to close
image->text; everything else is proven byte-parity.

Core-First: A6a is compute-tier work in tesseract-recognizer; no new Core type.
Durable record per iron rule 3 (tesseract-rs commit a58e9f0).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…parity green

The recognizer produces text end-to-end: recognize_grid (network.forward B1 ->
beam decode -> extract -> ids_to_text) is byte-identical to a public-API
libtesseract oracle (composed from the proven B1-forward + 7b-beam + charset
oracles) across 5/5 grid widths. Proves the B1-logits->beam seam. With A6a +
B3-core proven, from_grey_pix -> recognize_grid composes pre-scaled grey-image
-> text; only A6b (leptonica-free image decode+scale) remains for full
image->text.

Core-First: B3-core is glue in the tesseract-ocr consumer; no new Core type.
Durable record per iron rule 3 (tesseract-rs 18d11f2 + parity-record commit).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…nscode COMPLETE

An image file on disk -> text is byte-parity green (6/6 widths), pure-Rust, zero
leptonica at runtime — the Tesseract->Rust recognizer is a complete byte-parity
transcode of RecognizeLine for model-height line images. recognize_image_file
seeds via the real SetRandomSeed (Convolve noise depends on it -> bit-matches
actual RecognizeLine). Only accuracy layers (dict C1, CJK C3, word/box B3-full)
and a byte-exact general-height pixScale remain as enhancements.

Core-First: A6b is the tesseract-ocr consumer's image front-end; no new Core
type. Durable record per iron rule 3 (tesseract-rs ffb55b0 + 4d8efda + docs).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…st leaf proven

Corrects E-OCR-IMAGE-TEXT-1's "leptonica headers-only, can't transcode" overstatement:
leptonica is open source; 1.82.0 source (matching the installed lib) fetches fine.
pixScale was deferred, not blocked. Method is ruff-driven: extended ruff_cpp_spo with
walk_free_functions (C-library free-function + call-graph harvest arm, ruff 096689c
local), harvested scale1.c -> the dispatch manifest classifying scaleGrayLILow etc. as
LEAF kernels, ported the first leaf (scale_gray_li) byte-parity green vs pixScaleGrayLI
(6/6 factors). walk_free_functions is reusable for any C-library transcode.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…image->text byte-exact at any height

The general-height pixScale is fully transcoded (LI + area-map + area-map2 +
unsharp + dispatch), byte-parity vs the real leptonica pixScale (12/12 + 4/4
exact 2^-n), and wired into prescale_grey_to_height. image file -> text is now
byte-identical to libtesseract at ANY line-image height (f>=0.02), pure-Rust,
zero leptonica at runtime -- closing the E-OCR-IMAGE-TEXT-1 model-height-only
boundary. All leaves ruff-driven (walk_free_functions harvested scale1.c +
enhance.c). Key finding: the area-map LR-corner coords are f64 in C (1.0 double
literal), not f32 -- a per-subexpression precision audit is mandatory.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
Plan index entry for the PDF->text batch plan; flags D0.5 (ogar_codebook mirror
+ COUNT_FUSE 79->84) as queued behind the OGAR mint merge per the two-sided
drift fuse.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
… OGAR #172)

The lance-graph half of the two-sided drift fuse: ogar_codebook::CODEBOOK +5
rows (textline/blob/page_layout/page_image/ocr_renderer) mirroring OGAR PR #172,
+ the lance-graph-ogar doc-count 79->84. MERGE ORDER: OGAR #172 first (or same
timeframe) — lance-graph-ogar pulls ogar-* as git deps on OGAR main, so its
COUNT_FUSE compile-assert only balances once both sides carry 84. 829/829
contract tests green.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…red)

Measured-in-production runbook (2026-07-07 OCR arc: ruff#53/OGAR#172/lance-graph#655/tesseract-rs#10): token hygiene, the access matrix, clone/push (incl. force-with-lease-against-URL), direct no-proxy REST for PR create/patch/inspect + release create/asset upload, the plateau fallback, and the paired-fuse merge rule. Canonical copies in lance-graph + ruff + OGAR .claude/prompts/.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…on 3/3 real dawgs

Recognizer C1 leaf 1: the Tesseract dictionary DAWG binary format loader, the
gate for the dictionary beam (P1B). Transcodes Dawg::init (bit-mask derivation
via ceil(log2(unicharset_size+1)) in f64) + SquishedDawg::read_squished_dawg
(dawg.cpp:313-352) — wire is i16 magic=42, i32 unicharset_size, i32 num_edges,
num_edges * u64 LE EDGE_RECORD; accessors: letter = rec & letter_mask, next_node
= rec >> next_node_start_bit, flags = (rec & flags_mask) >> flag_start_bit.
Placed in lance-graph-contract next to unicharcompress (content-tier, walked
read-only).

Byte-parity GREEN on all 3 real eng.lstm dawgs vs a public-API-only oracle
(SquishedDawg + Load(TFile*), the tesseract dawg2wordlist.cpp pattern — no
private access, no ABI-skew risk): word (461,848 edges, 3.69 MB), punc (539),
number (591), each byte-identical hdr + per-edge letter/next_node/word_end. All
letters bounded < 113 = unicharset_size + 1 (eng's 112 + null-char slot). +5
unit tests (hand-computable bit-math + magic/size edge cases). fmt clean;
clippy -D warnings clean; 5/5 dawg tests green.

Enables D1.2 (Dict-lite: default_dawgs + def_letter_is_okay + IsSpaceDelimited)
via the ContinueDawg surface (recodebeam.cpp:1026-1123), then D1.3 wires into
the dormant beam dict arms proven in E-OCR-RECODEBEAM-1. No full Dict port
needed.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…(D1.2a)

The beam-step substrate on the proven D1.1 loader: edge_char_of (binary search
over node-0 forward edges via the given_greater_than_edge_rec comparator
dawg.h:243-282; linear scan elsewhere, dawg.cpp:198-228), edge_occupied /
last_edge, num_forward_edges_in_node0 computed at load (dawg.cpp
num_forward_edges(0) walk), NodeRef/NO_EDGE sentinel, and the DawgType +
PermuterType enums.

SHAPES ARE RUFF-HARVEST-VERIFIED (per the no-hand-rolled-shapes directive): the
new ruff_cpp_spo walk_enums arm harvested dawg.h/dict.h/ratngs.h — PermuterType
0..12, DawgType 0..3, DawgPosition{dawg_ref,punc_ref,dawg_index:i8,
punc_index:i8,back_to_punc}, DawgArgs{active,updated,permuter,valid_end} —
manifest banked at tesseract-rs/.claude/harvest/tesseract-dict-shapes.txt; the
Rust enums match the manifest ordinals exactly (sentinel counts deliberately
unmirrored).

10/10 dawg tests green (binary-search hit/miss, linear scan, three node-0
layouts, enum round-trips, real punc-dawg spot check); fmt + clippy -D warnings
clean.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…ords), ruff-shape-verified

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…(7/7 + regression 5/5)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…h-3A harvest manifests

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
Sinks the 7 textord data shapes (TBOX, BLOBNBOX, ROW, TO_ROW, BLOCK,
TO_BLOCK, POLY_BLOCK) onto facet::FacetCascade per the network-sink
precedent: classid = compose_classid(<minted 0x0805..0x0807 canon>,
<shape ordinal custom-low>), payload = the 6x8:8 rail carving with a
documented field->rail table per shape (i16 box coords full-precision,
floats rounded/fixed-point with stated loss, TO_ROW slope x10000).
Field inventory from the banked ruff manifest (239 classes, no vtable
-- data containers, so the sink is field inventory, not ClassView) +
header line citations (rect.h/blobbox.h/ocrrow.h/ocrblock.h/polyblk.h).
Intrusive list links and pointers routed to EdgeBlock per doctrine;
list content stays content-store. 10 new tests (round-trip, classid
composition, distinctness); 849 lib tests green; fmt+clippy clean.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
…urfaces complete; 3E cut

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_214e8a05-c97c-472f-888a-996089ae1859)

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AdaWorldAPI, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 39dc3ad7-1b10-4526-9ee3-b18d8656e52b

📥 Commits

Reviewing files that changed from the base of the PR and between e12013f and 5a9d845.

📒 Files selected for processing (3)
  • .claude/board/EPIPHANIES.md
  • crates/lance-graph-contract/src/dawg.rs
  • crates/lance-graph-contract/src/lib.rs
📝 Walkthrough

Walkthrough

This PR adds a new dawg module for parsing Tesseract SquishedDawg binaries with accessors and edge lookup, a new textord_facet module for encoding layout shapes into FacetCascade tiers, five new codebook entries, two example CLIs, a Cargo.toml comment update, and documentation/board/runbook files.

Changes

DAWG and textord facet contracts

Layer / File(s) Summary
SquishedDawg parsing and accessors
crates/lance-graph-contract/src/dawg.rs, crates/lance-graph-contract/src/lib.rs
New module defines NodeRef, DawgType, PermuterType, SquishedDawg with header validation, edge decoding, accessors, edge_char_of lookup, DawgError, and unit tests; wired into crate root.
DAWG dump example
crates/lance-graph-contract/examples/dawg_dump.rs
CLI loads a SquishedDawg file and prints header/edge data or exits with an error.
Textord facet shapes and packing
crates/lance-graph-contract/src/textord_facet.rs, crates/lance-graph-contract/src/lib.rs
New module defines canon slot constants, BlobShape/TextlineShape/PageShape enums, quantization helpers, and tbox_facet/blobnbox_facet/row_facet/to_row_facet/block_facet/to_block_facet/poly_block_facet constructors with tests; wired into crate root.
Textord facet dump example
crates/lance-graph-contract/examples/textord_facet_dump.rs
CLI constructs synthetic facet shapes and hex-dumps each with its classid.
Codebook entries and version fuse
crates/lance-graph-contract/src/ogar_codebook.rs, crates/lance-graph-ogar/Cargo.toml
Adds five OCR-domain CODEBOOK entries (textline, blob, page_layout, page_image, ocr_renderer) and updates the ALL count comment from 79 to 84.
Board logs, integration plan, and runbook
.claude/board/EPIPHANIES.md, .claude/board/INTEGRATION_PLANS.md, .claude/prompts/github-access-runbook.md
Adds timestamped OCR pipeline milestone log entries, a new integration plan record for pdf-to-text-ocr v1, and a GitHub access runbook documenting proxy/token/PR/release procedures.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related PRs

Poem

A dawg of bytes, a facet's tier,
I hopped through bits both far and near,
Edges parsed and boxes packed,
Byte-for-byte, the parity's exact,
Thump-thump — another leaf lands clear! 🐇📦

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: sinking textord/ccstruct shapes onto V3 SoA facets and adding OCR arc board entries.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/board/EPIPHANIES.md:
- Line 186: The markdown fence in EPIPHANIES.md is missing a language tag, which
triggers the lint warning. Update that fenced block to use a language identifier
such as text (or the most appropriate language) so the documentation remains
markdownlint-clean. Refer to the bare fence near the epiphany entry and keep the
surrounding content unchanged.
- Line 159: The inline `eeSSSS      ` example in EPIPHANIES.md is triggering the
MD038 whitespace lint because the trailing padding is inside inline code. Move
that sample into a fenced code block or another preformatted example so the
intentional spaces remain visible, and keep the surrounding narrative unchanged.

In @.claude/prompts/github-access-runbook.md:
- Around line 31-125: The GitHub access examples leak the token via argv and
token-in-URL patterns in the clone/push/PR/release flows. Update the runbook
snippets around the direct REST helpers and git commands to use an askpass or
temporary credential-helper approach so the token is never passed as a
command-line argument or embedded in remote URLs. Keep the guidance consistent
across the clone, push, create_pull_request, and release sections, and reference
the existing curl/python/git examples in this document so they can be replaced
in place.

In `@crates/lance-graph-contract/src/dawg.rs`:
- Around line 284-287: In the DAWG deserialization path in read_dawg (the block
that builds edges), preflight the declared edge payload before allocating or
pushing into the Vec. Before the loop over num_edges, validate that the
remaining bytes are enough for num_edges u64 values using the reader position
and total input length, and return an error early if not. This should be done in
the edge-reading logic that uses r.pos(), read_u64(), and the edges buffer so
malformed headers do not cause unnecessary memory growth.

In `@crates/lance-graph-contract/src/textord_facet.rs`:
- Around line 329-349: The facet packing constructors are only using
debug_assert! for enum ordinals, so release builds can still serialize invalid
values and alias them to the wrong bytes. Update the public constructors in
textord_facet.rs, including the facet-packing helpers that build FacetCascade,
to validate region_type, left_tab_type, and right_tab_type with runtime checks
(assert! or Result-based validation) before packing. After validation, pack the
already-checked values directly so invalid ordinals are rejected consistently in
all builds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 88553495-81fd-4d01-b9c2-9c5235d366f3

📥 Commits

Reviewing files that changed from the base of the PR and between 6bfc719 and e12013f.

📒 Files selected for processing (10)
  • .claude/board/EPIPHANIES.md
  • .claude/board/INTEGRATION_PLANS.md
  • .claude/prompts/github-access-runbook.md
  • crates/lance-graph-contract/examples/dawg_dump.rs
  • crates/lance-graph-contract/examples/textord_facet_dump.rs
  • crates/lance-graph-contract/src/dawg.rs
  • crates/lance-graph-contract/src/lib.rs
  • crates/lance-graph-contract/src/ogar_codebook.rs
  • crates/lance-graph-contract/src/textord_facet.rs
  • crates/lance-graph-ogar/Cargo.toml


The grid → text glue ships — the composition that makes the recognizer produce text. `tesseract_ocr::LstmRecognizer::recognize_grid` is the A6b-independent core of `LSTMRecognizer::RecognizeLine` (`lstmrecognizer.cpp:247-291`): it threads `network.forward` (B1, `E-OCR-NETWORK-FORWARD-1`) → the softmax logits → `RecodeBeamSearch::decode` (`E-OCR-RECODEBEAM-1`) → `extract_best_path_as_unichar_ids` (C2, `E-OCR-UNICHAR-EXTRACT-1`) → `ids_to_text` (`E-CPP-PARITY-1`). It composes ONLY already-byte-parity-proven pieces plus the thin logits→beam adapter (`outputs.f(t)` rows), so its value is proving the **one seam the per-leaf oracles never covered: B1's actual forward output feeding the beam.**

Byte-parity **GREEN** on **5/5** synthetic grid widths (8, 16, 24, 40, 64): the best-path `unichar_ids` + assembled `text` byte-identical vs a public-API oracle that is a **mechanical composition of the proven B1-forward + 7b-beam + charset oracles** — `Network::CreateFromFile` → `net->Forward` → `RecodeBeamSearch(recoder, 110, true, nullptr).Decode(outputs, 1.0, 0.0, 0.0, &charset, 0)` → `ExtractBestPathAsUnicharIds` → `id_to_unichar`. Varied outputs (`:`, `eEE.`, `aiiiiff`, `eeSSSS `, …) all matched. No private-member access → no ABI skew. The oracle's stderr self-checks (`nw=385807`, charset 112, recoder code_range 111, `oshape width=8 int_mode=0`) confirm the composition seam is sound before the beam runs.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Preserve the visible padding in a fenced example.

Line 159's eeSSSS literal is what trips MD038. If the spacing is intentional, move this sample to a fenced block so the whitespace stays explicit without the inline-code lint hit.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 159-159: Spaces inside code span elements

(MD038, no-space-in-code)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/board/EPIPHANIES.md at line 159, The inline `eeSSSS      ` example
in EPIPHANIES.md is triggering the MD038 whitespace lint because the trailing
padding is inside inline code. Move that sample into a fenced code block or
another preformatted example so the intentional spaces remain visible, and keep
the surrounding narrative unchanged.

Source: Linters/SAST tools

The recognizer-load leaf ships — the assembly of the whole pipeline from disk. `tesseract_ocr::LstmRecognizer::from_components(lstm, unicharset_text, recoder)` transcodes `LSTMRecognizer::DeSerialize` (`lstmrecognizer.cpp:133-177`) for the **`include_charsets == false`** path — how `combine_tessdata -u` split `/tmp/eng.lstm` out of `eng.traineddata`: the unicharset + recoder are SEPARATE traineddata components, so they are NOT inline in the lstm blob. After the B1 network (`Network::from_le_bytes`, `E-OCR-NETWORK-FORWARD-1`), the lstm component's **81-byte tail** is exactly `network_str_` (u32-len string) + **4×i32** (`training_flags_`, `training_iteration_`, `sample_iteration_`, `null_char_`) + **3×f32** (`adam_beta_`, `learning_rate_`, `momentum_`); the unicharset (TEXT → `UniCharSet::load_from_str`, `E-CPP-PARITY-1..6`) + recoder (binary → `UnicharCompress::from_le_bytes`, `E-CPP-PARITY-7`) load from their own component files. B2 is thus **assembly of already-proven pieces + one thin new parse** (the 8 trailing fields) — the natural shape of a "load the whole object" leaf.

Byte-parity **GREEN** on the real `/tmp/eng.lstm`: the 8 trailing-parse lines are **byte-identical** vs a **public-API-only** oracle (`Network::CreateFromFile` to advance the cursor past the network, then `TFile::DeSerialize` in the exact `lstmrecognizer.cpp:144-166` field order — no private-member access, so the 5.3.4-lib / 5.5.0-header ABI skew cannot bite):
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a language tag to the output fence.

Line 186's fence is bare, which is the markdownlint warning here. Mark it as text (or the appropriate language) so the docs stay lint-clean.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 186-186: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/board/EPIPHANIES.md at line 186, The markdown fence in EPIPHANIES.md
is missing a language tag, which triggers the lint warning. Update that fenced
block to use a language identifier such as text (or the most appropriate
language) so the documentation remains markdownlint-clean. Refer to the bare
fence near the epiphany entry and keep the surrounding content unchanged.

Source: Linters/SAST tools

Comment on lines +31 to +125
curl -sS --noproxy '*' -H "Authorization: Bearer $GHT" \
https://api.github.com/repos/AdaWorldAPI/<repo> | python3 -c "import json,sys; print(json.load(sys.stdin).get('permissions'))"
```

## 2. The measured access matrix

| Path | behaviour |
|---|---|
| local proxy remote `http://127.0.0.1:<port>/git/AdaWorldAPI/<repo>` | per-repo policy: some repos push ✅ (lance-graph, tesseract-rs), others 403 (ruff, OGAR) |
| git-over-HTTPS `https://x-access-token:$GHT@github.com/...` THROUGH proxy | sometimes works (ruff), sometimes 403 (OGAR) — still proxy policy |
| **git push with proxy env cleared** | ✅ works wherever the TOKEN has push (both ruff + OGAR) |
| REST `api.github.com` through proxy | ❌ always blocked |
| **REST direct** (`curl --noproxy '*'` / Python `ProxyHandler({})`) | ✅ full API: PR create/patch/merge-state, releases, checks |
| MCP `mcp__github__*` | PR-create works only where the GitHub App has `pulls:write` (lance-graph, tesseract-rs); ruff ❌, OGAR not in scope |

## 3. Clone

```sh
git clone --depth 30 "https://x-access-token:${GHT}@github.com/AdaWorldAPI/<repo>.git" /tmp/<repo>-gh
```
(Reads generally work even through the proxy; the token-URL clone is the
reliable universal form. `--depth` to taste; `git fetch --unshallow` if needed.)

## 4. Push

```sh
# 1st try: the configured remote (proxy). If 403:
env -u HTTPS_PROXY -u https_proxy -u HTTP_PROXY -u http_proxy \
git push -u "https://x-access-token:${GHT}@github.com/AdaWorldAPI/<repo>.git" <branch>
```

**force-with-lease against a URL** has no tracking ref — pass the expected tip
explicitly (after a fresh `git fetch origin <branch>`):
```sh
OLD=$(git rev-parse origin/<branch>)
env -u HTTPS_PROXY ... git push --force-with-lease=refs/heads/<branch>:$OLD "<token-url>" <branch>
```
Only force-push a session branch whose extra history is ALREADY MERGED upstream
(the merged-PR rule); never rewrite other people's merge commits — if a stop
hook flags "unverified" commits that are the repo's own main history after a
`checkout -B <branch> origin/main` sync, the fix is this pointer fast-forward,
NOT an amend/rebase.

## 5. Pull request — create / fix / inspect

Try MCP `mcp__github__create_pull_request` first (works for lance-graph,
tesseract-rs). Where it 403s, go direct REST. **Write the body to a FILE via a
QUOTED heredoc first** — an unquoted heredoc executes backticks inside the body
and mangles it (bit us on OGAR#172; fixed via PATCH):

```sh
cat > /tmp/pr_body.md <<'EOF'
...body with `backticks` safe here...
EOF
python3 - "$GHT" <<'PY'
import json, sys, urllib.request
data = json.dumps({"title": "...", "head": "claude/<slug>", "base": "main",
"body": open('/tmp/pr_body.md').read()}).encode()
req = urllib.request.Request("https://api.github.com/repos/AdaWorldAPI/<repo>/pulls",
data=data, method="POST", headers={"Authorization": f"Bearer {sys.argv[1]}",
"Accept": "application/vnd.github+json", "User-Agent": "claude-code"})
opener = urllib.request.build_opener(urllib.request.ProxyHandler({})) # ← bypasses the proxy
print(json.load(opener.open(req, timeout=30))["html_url"])
PY
```

- Fix a body afterwards: same, `method="PATCH"`, URL `.../pulls/<n>`, payload `{"body": ...}`.
- Inspect: GET `.../pulls/<n>` → `merged`, `mergeable_state`; GET
`.../commits/<head-sha>/check-runs` → CI status. ("state: closed" ≠ rejected —
check `merged`/`merged_at`.)

## 6. Release — create + upload assets (same direct-REST pattern)

```sh
python3 - "$GHT" <<'PY'
import json, sys, urllib.request
opener = urllib.request.build_opener(urllib.request.ProxyHandler({}))
H = {"Authorization": f"Bearer {sys.argv[1]}", "Accept": "application/vnd.github+json",
"User-Agent": "claude-code"}
# 1) create the release (tag is created on target_commitish if it doesn't exist)
data = json.dumps({"tag_name": "v0.1.0", "target_commitish": "main",
"name": "v0.1.0 — <title>", "body": open('/tmp/rel_body.md').read(),
"draft": False, "prerelease": False}).encode()
req = urllib.request.Request("https://api.github.com/repos/AdaWorldAPI/<repo>/releases",
data=data, method="POST", headers=H)
rel = json.load(opener.open(req, timeout=30))
print("release:", rel["html_url"], "id:", rel["id"])
# 2) upload each asset — NOTE the DIFFERENT host uploads.github.com + ?name=
blob = open("/tmp/asset.tar.gz","rb").read()
up = urllib.request.Request(
f"https://uploads.github.com/repos/AdaWorldAPI/<repo>/releases/{rel['id']}/assets?name=asset.tar.gz",
data=blob, method="POST",
headers={**H, "Content-Type": "application/octet-stream"})
print("asset:", json.load(opener.open(up, timeout=300))["browser_download_url"])
PY

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Don't expose the GitHub token in argv or remote URLs.

Lines 31-125 pass the token on the command line (curl -H ..., python3 - "$GHT") and in git URLs. That can leak via shell history and process listings. Use an askpass or temporary credential-helper flow so the token never appears in argv, and apply it consistently across clone/push/PR/release examples.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~42-~42: The official name of this software platform is spelled with a capital “H”.
Context: ...EN has push (both ruff + OGAR) | | REST api.github.com through proxy | ❌ always block...

(GITHUB)


[uncategorized] ~44-~44: The official name of this software platform is spelled with a capital “H”.
Context: ...h/merge-state, releases, checks | | MCP mcp__github__* | PR-create works only where th...

(GITHUB)


[uncategorized] ~76-~76: The official name of this software platform is spelled with a capital “H”.
Context: ...quest — create / fix / inspect Try MCP mcp__github__create_pull_request first (works for ...

(GITHUB)


[locale-violation] ~97-~97: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...=30))["html_url"]) PY ``` - Fix a body afterwards: same, method="PATCH", URL `.../pulls...

(AFTERWARDS_US)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/prompts/github-access-runbook.md around lines 31 - 125, The GitHub
access examples leak the token via argv and token-in-URL patterns in the
clone/push/PR/release flows. Update the runbook snippets around the direct REST
helpers and git commands to use an askpass or temporary credential-helper
approach so the token is never passed as a command-line argument or embedded in
remote URLs. Keep the guidance consistent across the clone, push,
create_pull_request, and release sections, and reference the existing
curl/python/git examples in this document so they can be replaced in place.

Comment thread crates/lance-graph-contract/src/dawg.rs
Comment thread crates/lance-graph-contract/src/textord_facet.rs Outdated
claude added 2 commits July 7, 2026 14:57
dawg.rs: preflight num_edges * 8 against the remaining buffer before
allocating/pushing, so a malformed header with a huge count fails fast
with UnexpectedEof instead of growing the Vec until the truncated read
errors.

textord_facet.rs: the enum-ordinal guards (region_type, left/right
tab_type, pitch_decision, poly-block type) become release-mode assert!
-- a pub contract constructor must not serialize out-of-range ordinals
that alias to a different value in release builds.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
Domain substrate does not live in the agnostic spine: the textord
facets belong to the ruff->OGAR producer path (ruff_spo_address::Facet
+ ogar-from-ruff mint/emit, concepts minted in ogar-vocab) or to the
tesseract-rs consumer -- all four repos compile into one binary, so
there is no linking excuse. The removed facets also misused the
classid low u16 as a shape ordinal where the canon-high flip defines
it as the app render prefix (0x0806_0001 reads as "blob rendered by
app 0x0001", a collision). Board: E-OCR-FACET-HOME-CORRECTION-1
(ruling appended; E-OCR-TEXTORD-FACET-1 Status -> SUPERSEDED).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016b33swuXE23hKtqxsHu9p1
@AdaWorldAPI AdaWorldAPI merged commit dc79f9c into main Jul 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants