okf-inventory
Build a court Inventory of Productions PDF for an OKF matter (whole-paragraph yellow highlighting of production source PDFs, optional verbatim web-page captures, court-formatted cover + hyperlinked index + page ranges + page numbers, deployed to the matter folder) and the related bundle families: adjudication appendix indexes (INDEX TO APPENDICES TO THE RESPONSE etc., with section bands and no pages column), arbitration indexes to supporting documents/authorities (IN THE MATTER OF THE ARBITRATION ACT 1996 heading, bracketed (pp. x - y) ranges), and papers for counsel / inventory of papers (numbered list with sections). Use when the user mentions an inventory of productions, '4th/fifth inventory', building/rebuilding a production PDF, adding highlighted extracts to an inventory, FOURTH_INVENTORY_OF_PRODUCTIONS, PAI references, an INDEX TO APPENDICES / index to supporting documents / papers for counsel bundle. Never modifies the production sources themselves.
Source: .opencode/skills/okf-inventory/SKILL.md — site rebuilt 2026-09-05.
OKF → Inventory of Productions & bundle indexes¶
Turn highlighted production extracts (court) or plain source PDFs (adjudication, arbitration, counsel papers) into a ready-to-file index/bundle PDF. Three stages, one config file:
[verbatim pages] -> [highlight extracts] -> [assemble inventory + deploy]
tools/okf_inventory_verbatim.py (optional; web captures)
tools/okf_inventory_highlight.py (stage 1 - court only)
tools/okf_inventory_build.py (stage 2)
One builder (okf_inventory_build.py) serves all four families — the index + sections config keys switch the layout:
| Family | Cover | Index columns | Pages | Example |
|---|---|---|---|---|
| Court inventory of productions | PAI ref, sheriffdom, parties | Doc # / Document name / Pages | a - b plain |
examples/okf_inventory_h1673_sample.json |
| Adjudication appendix index | IN THE MATTER OF AN ADJUDICATION BEFORE [adjudicator] | NO. / DOCUMENT DESCRIPTION | hidden | examples/okf_inventory_adjudication_sample.json |
| Arbitration index (supporting docs / authorities) | IN THE MATTER OF THE ARBITRATION ACT 1996 … BETWEEN Claimant - and - Respondent | NO. / DOCUMENT / PAGES | (pp. a - b) bracketed |
examples/okf_inventory_arbitration_sample.json |
| Papers for counsel / inventory of papers | PAPER(S) FOR COUNSEL + instance | NO. / PAPER / PAGES | plain or hidden | examples/okf_inventory_counsel_sample.json |
All example configs are self-contained templates with fictitious parties (McFadden v Millar; Greenway v Meridian before Mr Alistair Craig; Highland Build Products v Lochview Homes; Lyra Assurance petition) — copy them for real matters and swap in the true names.
When to run¶
- The user asks to build/rebuild the inventory of productions for a matter ("4th inventory", "add docs to the inventory").
- New highlighted extracts exist and need indexing, hyperlinking, page numbering and deployment to the matter folder.
- A web page needs a verbatim, citable production copy (e.g. CPCAB qualification pages).
- An adjudication pleading (Referral Notice, Response, Reply, Rejoinder…) needs its appendix index ("INDEX TO APPENDICES…") or an inventory of papers in that adjudication.
- An arbitration needs an "Index to Supporting Documents" or "Index to Authorities" bundle.
- Papers are being sent to counsel and an inventory/papers-for-counsel index is needed.
- Do NOT use for ordinary correspondence filing or evidence production under an Existing Productions order — those are okf-ocr/inventory matters; this skill only assembles the index PDF.
Tooling¶
One config JSON drives everything. Examples:
- examples/okf_inventory_h1673_sample.json — court Fourth Inventory as built 09-08-2026 (fictitious parties in this template copy).
- examples/okf_inventory_adjudication_sample.json — Greenway v Meridian style INDEX TO APPENDICES (no pages column, section bands).
- examples/okf_inventory_arbitration_sample.json — IN THE MATTER OF THE ARBITRATION ACT 1996 style, bracketed (pp. a - b) ranges.
- examples/okf_inventory_counsel_sample.json — Papers for Counsel with numbered sections.
{
"workspace": "C:\\...\\matter workspace (extract PDFs live here)",
"deploy_to": "H:\\My Drive\\<matter folder>",
"output": "FOURTH_INVENTORY_OF_PRODUCTIONS_09-08-2026.pdf",
"inventory_title": "FOURTH INVENTORY OF PRODUCTIONS FOR THE PURSUER",
"index_subtitle": "Fourth Inventory of Productions for the Pursuer",
"doc_start": 27,
"fetch_date": "9 August 2026",
"cover_lines": [ {"text": "PAI 43 - 26", "size": 16, "bold": true}, {"text": "", "size": 12} ],
"index": {
"heading": "INDEX TO APPENDICES TO RESPONSE",
"doc_label": "NO.",
"name_label": "DOCUMENT DESCRIPTION",
"pages_label": null,
"pages_format": "plain"
},
"sections": [ {"at": 16, "label": "DOCUMENTS ATTACHED TO REJOINDER"} ],
"highlights": [ {"name": "ef2018", "src": "...", "pins": ["..."]} ],
"verbatim_pages": [ {"name": "cpcab_lc_l4", "title": "...", "source_url": "...", "sections": [{"heading": "...", "paras": ["..."]}]} ],
"extracts": [ {"file": "ef2018_extract.pdf", "name": "Extract from ... - pages 1, 6, ..."} ]
}
Config fields beyond the court basics:
index(optional; court defaults shown):heading— index page title ('Index' for court; 'INDEX TO APPENDICES TO RESPONSE' for adjudication; 'INDEX TO RESPONDENT'S SUPPORTING DOCUMENTS' for arbitration).doc_label— number column header ('Doc #' court / 'NO.' adjudication / 'PAPER' counsel).name_label— description column header ('Document name' / 'DOCUMENT DESCRIPTION' / 'DOCUMENT' / 'PAPER').pages_label—nullhides the pages column entirely (adjudication indexes, VG Gigaclear style). Default 'Pages'.pages_format—'plain'(a - b, court) or'bracketed'((p. 3)/(pp. 4 - 5), arbitration). Use ASCII hyphen: en-dash does not survive the simple-font encoding.doc_numbers— optional explicit row numbers as strings (for A1/B1 style, 'Not used' gaps or sub-numbering like 6.1); defaults todoc_start+ index.sections— band rows drawn inside the index before the row at 1-based positionat(e.g. 'DOCUMENTS ATTACHED TO RESPONSE' / 'DOCUMENTS ATTACHED TO REJOINDER'; or 'GROUNDS OF APPEAL' / 'ANSWERS' / 'NOTES OF ARGUMENT' / 'MISC.' in papers for counsel). Bands take no doc number and move with their row across page breaks.extractsentries may omitfile— a placeholder row ('Not used') with a number and description but no pages and no hyperlink.cover_linesentries support"align": "center"and long lines are word-wrapped automatically (the Arbitration Act heading block is centred).
Run order:
python tools/okf_inventory_verbatim.py <config> # optional stage 0 (court web captures)
python tools/okf_inventory_highlight.py <config> # stage 1 (court only; or --only <job>)
python okf_inventory_build.py <config> # stage 2 (assembles, verifies, deploys)
okf_inventory_highlight.py exits 1 if any pin could not be highlighted (WARN) — never deploy with unresolved WARNs; fix the pin or the source first.
Workflow¶
- Collect the documents. Court: source PDFs (scans must be OCR'd first — run
okf-ocr), born-digital PDFs, or web pages. Adjudication/arbitration/counsel: the appendix or paper PDFs themselves (already OCR'd if scanned) — no highlighting needed. - Web pages → verbatim stage: pull the page text, keep it verbatim, record the real URL and fetch date. Never paraphrase inside a production; paraphrase belongs in research notes.
- Highlight pins (court only). Pin phrases must be verbatim from the source (verify first; never guess). Whole-paragraph highlighting is the standard: the pin matches a paragraph, the whole text-block rect is highlighted in yellow
(1.0, 0.92, 0.25). The extract = the source's own cover page + annotated pages. - QA the extracts. Zero WARNs; every extract opens; annots present on the annotated pages; page 1 of each extract is its cover.
- Assemble. Stage 2 builds cover → hyperlinked index → extracts, adds page numbers, writes
_pagedtwin, deploys todeploy_to. - Report. Output path, page count, doc range (doc_start..), annotation count, link count, deploy target.
Rules¶
- Never modify production sources. Highlighting writes
_highlighted.pdf/_extract.pdfcopies only; bundle builds append originals untouched. - Verbatim only. Pins and extract text must match the source exactly. Apostrophes: normalise U+2019/U+2018 to
'in BOTH pin and page text (PDFs often use curly quotes); also normalise U+00A0. A WARN means the pin did not match — investigate, never force it. - Whole paragraphs, not word-ranges. The extract must carry the source cover page so it is self-describing in the production list.
- Doc numbering continues from the previous inventory (
doc_start); page ranges are computed from actual page counts after the index is laid out (two-pass — single-page docs and index wrapping are the classic off-by-one traps). Adjudication/counsel indexes number from 1 unless continuing an earlier index. - Hyperlinks required: index row → extract start; cover → index; each extract cover → its index row. Use PyMuPDF's
pagekey forLINK_GOTO(newer PyMuPDF rejectsto). Placeholder rows (nofile) get no link — verify the link count equals the number of real extracts. - ASCII hyphens in page ranges. En-dashes (U+2013) do not survive the simple-font encoding (they render as a centre dot) — the court and Ipsum exemplars use plain hyphens anyway.
- Page numbers on every page, centred grey footer (10pt Palatino Linotype — house style; Arial only as a fallback if Palatino is missing).
- House style: cover/index/footer render in Palatino Linotype (12pt body equivalents; footer 10pt). All example configs carry fictitious parties so they can be copied safely.
- Deploy discipline. Deploy only when annotation and link counts verify; always refresh the
_pagedtwin; report the deployed byte size. - NotebookLM sync (if the matter has one): rebuilding extracts changes staging inputs — run
okf_nblm_build.py <matter> --checkand rebuild if it exits 1.
Traps (learned; see learnings.md)¶
- Edge/Chrome headless
--print-to-pdfcan hang on this machine — build verbatim web captures withokf_inventory_verbatim.pyinstead. - PowerShell
>redirection corrupts Python UTF-8 stdout — usecmd /c python ... > out.txtor write the file from inside Python. - lcl4-style two-column criteria pages split paragraphs across text blocks: paragraph merging needs strict x-overlap, and a vertical gap threshold of ~8pt (measured line-gap clusters: 0–7pt intra-paragraph, 12–28pt inter-paragraph).
- The index page range off-by-one: never assume one index page; count wraps first (see stage 2 pass 1).
- Section bands are 1-based row positions (
at= the extract's position in theextractslist, starting at 1) — they are NOT doc numbers; withdoc_start> 1 the band position still refers to list order. - Centred cover headings wrap automatically — keep the long "AND IN THE MATTER OF THE JCT…" line as one entry and let the builder wrap; do not hand-split lines.
- Check for leftover court styling — if an adjudication/arbitration/counsel build renders a 'Pages' column or 'Doc #'/'Index' headers, the
indexblock is missing from the config (defaults are the court layout).