llm-ingestion-okf/src/llm_ingestion_okf
Kjell Tore Guttormsen ea64b9915d feat(profiles): SEGMENTED_OKF_V0_2 so a segmented bundle can declare its spec
Neither shipped profile could do both: SEGMENTED_V1 names `bundle_id` and not
`okf_version`, OKF_V0_2 names `okf_version` and has no segmentation at all. The
sixth profile is where the two intersect. Additive, as upstream support always
is here -- a new profile, never a migration.

THE INDEX POLICY IS DECIDED, NOT INHERITED, and that is the part that was easy
to get wrong. Measured: OKF_V0_2.index has facets=None and per_directory=False,
while SEGMENTED_V1.index has both. Building on OKF_V0_2's index would have
produced a segmented bundle with no faceted index -- structurally valid, SPEC
conformant, and missing the surface a consumer reads. So the index comes from
SEGMENTED_V1 with both root keys named, and the spec declaration from OKF_V0_2.
The premise is asserted in the suite rather than trusted to stay true.

`SegmentationPolicy.adjudication_key` is the discriminator BETWEEN segmented
profiles. It has to be a field with a value: every 1-to-N branch keys on
`profile.segmentation is not None`, which both segmented profiles satisfy, so a
later step surfacing the adjudication marker on that check would write it into
SEGMENTED_V1 too and move a byte-pinned golden. `None` means the profile does
not surface adjudication state at all.

`okf_version`'s VALUE is not in this module and must never be: a profile names
a key, the caller owns its value (decision E1). A constant here would claim a
decision this library does not own and would be the one thing to chase on every
upstream release. Asserted.

Five existing profiles untouched, each still equal to a freshly constructed
copy, root keys pinned literally, and OKF_LATEST still DEFAULT -- adding a
profile is not a GA event.

Suite 926 -> 931.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 14:21:15 +02:00
..
__init__.py feat(extract): implement pdf behind the [extract] extra with pdfplumber 2026-08-21 20:22:39 +02:00
_pandoc.py feat(extract): resolve the vendored converter and refuse an unpinned version 2026-09-02 14:07:20 +02:00
connectors.py fix(errors): type the NUL-byte path failure as SourceError 2026-07-20 10:30:42 +02:00
errors.py feat(profiles): a profile may name a per-suffix renderer 2026-09-02 14:19:18 +02:00
extract.py feat(profiles): a profile may name a per-suffix renderer 2026-09-02 14:19:18 +02:00
guard_adapter.py feat(guard): bump the pin to >=0.3,<0.4 and pin Door C's allow_reserved=False 2026-08-02 21:08:53 +02:00
importer.py feat(index): one ordering helper, called by both doors 2026-09-01 19:54:45 +02:00
inbox.py feat(profiles): a profile may name a per-suffix renderer 2026-09-02 14:19:18 +02:00
manifest.py feat(profiles): materialize_bundle takes a keyword-only profile (req 6) 2026-07-31 15:37:17 +02:00
materialize.py fix(frontmatter): a nested key must not substitute for a top-level one 2026-08-31 23:31:53 +02:00
profiles.py feat(profiles): SEGMENTED_OKF_V0_2 so a segmented bundle can declare its spec 2026-09-02 14:21:15 +02:00
py.typed feat(typing): ship the PEP 561 py.typed marker 2026-07-17 03:46:51 +02:00
render.py feat(errors): stable machine-readable codes on IngestError 2026-07-17 04:01:04 +02:00
segmentation.py feat(segmentation): slice canonical extracted text by declared spans 2026-08-31 23:59:05 +02:00
structure.py fix(frontmatter): a nested key must not substitute for a top-level one 2026-08-31 23:31:53 +02:00