ID-145 — FORM-EXTRACTION / FILL Spike (empirical, measured)
ID-145 — FORM-EXTRACTION / FILL Spike
Section titled “ID-145 — FORM-EXTRACTION / FILL Spike”Type: Empirical spike feeding
{145.3}TECH. Every number below is MEASURED against the owner’s real forms on 12/07/2026 — not estimated. Does NOT edit the ledger and does NOT author TECH.Environment. Throwaway venv, system Python 3.14.2,
PIP_USER=0. Pinned reader deps (pdfplumber==0.11.9,openpyxl==3.1.5,python-docx>=1.1.2,pydantic==2.12.5) + the vision stack (commonforms==0.2.1,langextract==1.6.0,pypdf==6.14.2,torch==2.13.0,rfdetr==1.8.3,onnxruntime,transformers). Systempandocpresent at/opt/homebrew/bin/pandoc(Charnwood track-changes resolved via it). Deterministic readers recovered from5494b527(=c84be63b^) into a temp restore,FormMetadatastubbed to bypass theanthropic/cocoindeximport chain inextraction.py(the stub is field-identical; it does not touch field extraction). Temp restore removed after the run — repo tree clean.
Verdict in five sentences
Section titled “Verdict in five sentences”- The recovered id-52 deterministic readers reproduce the
ACCEPTANCE.mdnumbers EXACTLY on the three fixtures that were in the id-52 corpus (Charnwood 58, EFA 19, CSP 45) — recovery is a zero-cost, zero-regressiongit show. - But those numbers are archetype-coupled: on two previously-unseen real forms
(British Council
annex_2docx response form,annex_3rate-card xlsx) the readers emit ZERO fields, and the Charnwood “58” is 51 prose-regex placeholder hits + one table — 7 of 8 tables surface zero fields, CONFIRMED as a design limit, not a bounded bug. - Every real UK procurement PDF measured is FLAT (0 AcroForm widgets on 5/5 PDFs) so AcroForm parsing is a dead end and the FFDetr render→detect→write path is mandatory — commonforms delivers it (Croydon SQ 23 pp → 120 fields in 3.8 s CPU), corroborating ARCH-REVIEW’s 198-field/57-pp run.
- langextract does semantic, char-offset-grounded entity extraction (text spans only — no coordinates, no widget types, no fill status); it is NOT a fillable-field detector and adds no value over the live Claude question path for v1.
- A
/goalML “commonforms-for-docx/xlsx” build is NOT warranted — docx/xlsx fillable structure is native to OOXML (readable by python-docx/openpyxl), so the right move is to resurrect-and-generalise the deterministic readers (bounded engineering, seeded by these examples), not to train a detector.
1. Deterministic id-52 readers on real .docx / .xlsx
Section titled “1. Deterministic id-52 readers on real .docx / .xlsx”Command (per file, via a standalone harness that stubs FormMetadata and calls
docx.extract / xlsx.extract directly):
# recover the 6 deleted files from gitfor f in __init__ docx xlsx pdf shared orchestrator; do git show 5494b527:scripts/cocoindex_pipeline/form_extractors/$f.py > <tmp>/$f.pydone# venv (PIP_USER=0 to defeat a global --user pip config)python3 -m venv id145venvPIP_USER=0 id145venv/bin/pip install pdfplumber==0.11.9 openpyxl==3.1.5 \ 'python-docx>=1.1.2' pydantic==2.12.5# run: PATH includes /opt/homebrew/bin so docx_utils finds system pandocPATH=/opt/homebrew/bin:$PATH id145venv/bin/python run_det_readers.py1.1 Measured field counts vs ACCEPTANCE.md
Section titled “1.1 Measured field counts vs ACCEPTANCE.md”| Set | Format | Measured | ACCEPTANCE | Verdict |
|---|---|---|---|---|
Charnwood ITT Services.docx | docx | 58 | 58 | ✓ exact |
British Council annex_2_supplier_response.docx | docx | 0 | (not in id-52 corpus) | NEW form → ZERO |
EFA evaluation-matrix-itt-vol8.xlsx | xlsx | 19 | 19 | ✓ exact (dedup, not 2N) |
British Council annex_3_pricing_approach.xlsx | xlsx | 0 | (not in id-52 corpus) | NEW form → ZERO |
CSP Cloud Security Principles Checklist V5_3.xlsx (owner) | xlsx | 45 | 45 | ✓ exact |
Per-facet (measured):
| Set | total | mandatory | word_lim | ref_urls | placeholder | full_coords | sections |
|---|---|---|---|---|---|---|---|
| Charnwood | 58 | 0 | 0 | 0 | 58 | 7 | 3 |
| EFA | 19 | 0 | 0 | 0 | 0 | 19 | 6 |
| CSP | 45 | 0 | 0 | 14 | 42 | 45 | 25 |
The owner CSP V5_3.xlsx returns the same 45 as the id-52 CSP fixture — the
readers are faithful on in-corpus shapes.
1.2 Charnwood “7/8 tables zero fields” — CONFIRMED, and it is a DESIGN LIMIT
Section titled “1.2 Charnwood “7/8 tables zero fields” — CONFIRMED, and it is a DESIGN LIMIT”Command: open via open_document_safe, classify each table’s header row with
_classify_header and test _is_placeholder_grid.
Charnwood has 8 tables; only table[3] emits. full_coords=7 = those 7 grid
cells; the other 51 of the “58” are _INLINE_PLACEHOLDER regex hits in paragraph
prose (fields-per-table = {3: 7}).
| tbl | header (truncated) | classify | grid? | emitted | what it really is |
|---|---|---|---|---|---|
| 0 | [Name] / [Tel] | None,None | no | 0 | contact block — fillable, [Name]-style ≠ insert-pattern |
| 1 | Stage / Date(s) and time(s) | None | no | 0 | tender timetable — fillable dates |
| 2 | SCHEDULE HEADING / COMPLETED? | None | no | 0 | submission checklist — fillable Yes/No |
| 3 | Insert question ti… / Insert % | None | yes | 7 | the one archetype it models |
| 4 | 0-3 / Completely unsatis… | None | no | 0 | scoring rubric (reference — arguably correct) |
| 5 | Service component / Costs (£) | None | no | 0 | pricing table — fillable £ cells |
| 6 | I DECLARE THAT… | None | no | 0 | declaration (prose) |
| 7 | SIGNED for and on… | None | no | 0 | signature block — fillable |
The reader recognises exactly two shapes: (a) a table whose header cell
_classify_headers to question/answer, and (b) a first-column-uniform
insert-style placeholder grid. Real fillable areas that fit neither
(contact/timetable/checklist/pricing/signature tables) are silently dropped. This
is architectural — the engine is corpus-shape-coupled, not a generic
fillable-field detector.
British Council annex_2 (7 tables — Social Value 10 %, Quality 20 %,
Methodology, Commercial 40 %, Submission Checklist, Supplier details, Table of
Information — a genuine supplier RESPONSE form) confirms it in the extreme: every
table classifies None, none is an insert-grid, 0 fields. annex_3
(Rate Card & Resources: Role | Day Rate (£GBP) | Description, plus numbered
Assumptions & Exclusions) matches neither the EFA scoring-matrix nor CSP
letter-keyed archetype → NO_ARCHETYPE_REASON → 0 fields (graceful, logged,
but empty).
Fidelity verdict: the readers are correct-but-overfit. Zero recovery cost and no regression on in-corpus shapes, but they generalise poorly — 0 fields on 2 of 4 unseen real forms and 7/8 Charnwood tables missed. Resurrecting them as-is is insufficient for the owner’s real corpus; they need generalisation (see §5).
2. commonforms on new PDFs
Section titled “2. commonforms on new PDFs”Command:
python - <<'PY' # AcroForm statusfrom pypdf import PdfReaderfor p in [...]: r=PdfReader(p); f=r.get_fields() or {} print(len(r.pages), len(f), 'ACROFORM' if f else 'FLAT')PY# FFDetr detect→write (weights auto-download to ~/.cache/huggingface on first run)from commonforms import prepare_formprepare_form(src, out, model_or_path='FFDetr', device='cpu', fast=True, confidence=0.4)2.1 AcroForm status — ALL FLAT
Section titled “2.1 AcroForm status — ALL FLAT”| pages | AcroForm fields | class | |
|---|---|---|---|
Croydon TMES CHS Salix MW SQ v1 (002).pdf | 23 | 0 | FLAT |
Croydon …Bidders response document v1.pdf | 29 | 0 | FLAT |
Lot Appendix A1.1 - TMES Company Details.pdf | 1 | 0 | FLAT |
Lot Appendix A responses.pdf | 2 | 0 | FLAT |
repo fixture standard-selection-questionnaire-ppn-03-24.pdf | 57 | 0 | FLAT |
5/5 FLAT. AcroForm parsing yields nothing on real UK procurement PDFs — the detection path is not optional.
2.2 FFDetr detection (fast=True / FFDNet-S, CPU, conf=0.4) — MEASURED
Section titled “2.2 FFDetr detection (fast=True / FFDNet-S, CPU, conf=0.4) — MEASURED”| pages | fields written | /Tx (text) | /Btn (checkbox) | time (CPU) | |
|---|---|---|---|---|---|
| Croydon SQ v1 (002) | 23 | 120 | 97 | 23 | 3.8 s |
| Lot A1.1 Company Details | 1 | 3 | 3 | 0 | 1.3 s |
Per-page detection is genuinely spatial (SQ page 15: 27 fields, page 11: 12, pages 21-22: 0). Times exclude the one-time weights download. Corroborates ARCH-REVIEW’s 198-field/57-pp/35.9-s run: FFDetr is deployable on the existing IONOS worker without a GPU.
Gap the run exposes (unchanged from ARCH-REVIEW): detection yields
coordinates + widget type ONLY — no label text. Auto-map matches lexically on
field.question_text, so the PDF lane MUST pair each detected box with nearby
label words (pdfplumber words left/above the box; the recovered pdf.py already
extracts positioned words) or PDF auto-map is a structural no-op.
Licence status unchanged: commonforms code carries NO licence (GitHub
license: null); only the FFDetr weights are stated Apache. Treat as
viable-pending-licence — either confirm upstream, or replicate the ~200-line
render→detect→write wrapper against the Apache FFDetr weights (pypdf + onnxruntime)
using commonforms only as a dev-side reference.
3. langextract for non-PDF questions/entities
Section titled “3. langextract for non-PDF questions/entities”Command: import + introspect langextract.data.Extraction, the provider
registry, and the extract factory (no ANTHROPIC_API_KEY/OPENAI_API_KEY in env,
so a live extraction could not be run here — assessment is architectural + API-level).
- Output shape (
Extraction):extraction_class,extraction_text,char_interval{start_pos,end_pos},alignment_status,attributes. That is text spans with character offsets — there is NO row/col/table coordinate, NO widget type, NO fill_status. langextract does not detect fillable-field structure; it is complementary to (not a substitute for) a field detector. - Backends (measured registry):
^gemini(Gemini), local Ollama family (llama/mistral/mixtral/phi/qwen/deepseek/gpt-oss/…), and OpenAI (^gpt-4/^gpt-5). Non-Gemini is real. No Anthropic builtin; a Claude backend requires the communitylangextract-anthropicplugin. - Text-only: PDFs/docx/xlsx must be textified first (the deterministic readers or python-docx/openpyxl can supply that text).
- Value over live Claude extraction: its one differentiator is char-offset span grounding (extractions map to exact source spans → aligns with the citation span model, D-S330-1). But it still needs an LLM (no cost saving), adds a dependency + a community Anthropic plugin as a prod dep, and the live Claude path already produces sectioned questions with word limits/weights across PDF/DOCX. v1 verdict: not adopted; track as a second-generation upgrade when span-grounded question provenance is wanted (matches ARCH-REVIEW R-A).
4. Per-format recommendation {PDF, .docx, .xlsx, .html}
Section titled “4. Per-format recommendation {PDF, .docx, .xlsx, .html}”| Format | Real-world class | Extraction (plane 2 structure) | Fill | Notes |
|---|---|---|---|---|
| FLAT (5/5 measured) | FFDetr render→detect→write (commonforms approach; measured 120 / SQ, 3 / A1.1). AcroForm parse = dead end. | pypdf AcroForm value writes into the detected widgets | MUST add label-pairing (pdfplumber words near boxes) or auto-map no-ops. Licence-gate the code dep. | |
| .docx | structured OOXML | resurrect id-52 reader but generalise beyond the 2 archetypes (measured 0 on annex_2; 7/8 Charnwood tables missed) | fill_template.py / docxtpl (formatting-preserving) | structure is native — no ML detector needed; generic “labelled cell + empty/placeholder response cell”, content-controls, highlighted runs |
| .xlsx | structured SpreadsheetML | resurrect id-52 reader but generalise beyond EFA/CSP archetypes (measured 0 on annex_3 rate-card) | openpyxl cell writes at recorded coords | same: detect empty answer cells adjacent to labelled cells generically |
| .html | none (no fixture) | net-new lane | — | defer until a real sample exists |
Legacy .doc/.xls are still in the owner corpus (British Council .doc RFPs;
Charnwood ITT Evaluation Matrix.xls) — convert-on-upload (LibreOffice
headless on the worker) or reject-with-guidance is a TECH decision.
Partial-fill-as-success (BI-22) is the default contract for every format: a
completion with gaps is completed + fields_skipped>0 (never fill_failed,
reserved for I/O/engine errors); gaps stay skipped/pending and re-fill passes
process only gaps.
5. /goal build verdict — NOT warranted (generalise the deterministic readers instead)
Section titled “5. /goal build verdict — NOT warranted (generalise the deterministic readers instead)”The owner idea is a one-time Claude-Code-/goal side-project to BUILD a
.docx/.xlsx commonforms-equivalent field detector, seeded with these examples.
Verdict: a /goal ML detector build is NOT warranted for docx/xlsx — it is a
category error. commonforms needs vision because a PDF is a flat raster with no
structural record of its fields; a .docx/.xlsx is the opposite — the fillable
structure (tables, cells, merged cells, content controls, highlighting) is native
to OOXML/SpreadsheetML and directly readable by python-docx/openpyxl. The
deterministic readers already prove this: they reproduce the acceptance counts
exactly at zero dependency cost. Their only defect is narrow heuristics (two
hardcoded shapes), not a missing detector.
Recommendation: resurrect-and-generalise the deterministic readers. Rough plan (bounded, days not weeks; do NOT build now):
- Replace archetype-matching with generic structural detection. DOCX: any
table row where a labelled/prose cell is followed by an empty-or-placeholder
cell → a field (drop the
_classify_headergate as the only path); also emit Word content-controls (w:sdt) and highlighted runs. XLSX: any labelled cell with an adjacent empty answer cell → a field (drop the EFA/CSP-only gate); keep dedup. - Keep the two archetypes as high-precision fast-paths layered over the generic detector (so EFA 19 / CSP 45 / Charnwood grid stay stable).
- Regression-gate on this exact corpus: Charnwood ≥ its 8 real fillable tables (not 1), annex_2 > 0, annex_3 rate-card rows detected, EFA 19, CSP 45.
- Use
/goalonly as an authoring accelerator for that generalised reader against the eval harness — the deliverable is deterministic code + a fixture suite, NOT a trained model.
This reuses the id-52 investment (readers, tests, fixtures, the analyse_form
queue-worker shape) and directly closes the measured gap, at a fraction of the
cost and risk of a from-scratch ML build.
Top-3 TECH decisions this spike forces
Section titled “Top-3 TECH decisions this spike forces”- PDF = FFDetr detection, never AcroForm parse. 5/5 real PDFs are FLAT;
detection is mandatory and measured-deployable on CPU (120 fields / 3.8 s).
Gate the code dependency on licence (replicate the ~200-line wrapper on
Apache FFDetr weights if unresolved), and MUST pair detected boxes with
pdfplumberlabel words or PDF auto-map is a no-op. - .docx/.xlsx = generalise the recovered deterministic readers, do NOT
/goalan ML detector. The measured design limit (0 fields on annex_2/annex_3; 7/8 Charnwood tables missed) is narrow heuristics over a native-structured format — fix with generic cell/placeholder/content-control detection, regression-gated on this corpus. - langextract deferred; live Claude path stays plane-1. langextract emits text
spans only (no field structure), needs an LLM anyway, and adds a community
Anthropic plugin as a prod dep — track it as the span-grounded-provenance
upgrade, not a v1 dependency. Legacy
.doc/.xlsneed a convert-on-upload decision;.htmldefers (no fixture).