feat(profiles): SEGMENTED_V1 profile and SegmentationPolicy capability

This commit is contained in:
Kjell Tore Guttormsen 2026-09-01 00:02:14 +02:00
commit cd2c7517b0
3 changed files with 190 additions and 0 deletions

View file

@ -758,6 +758,37 @@ class IndexPolicy:
return found return found
@dataclass(frozen=True)
class SegmentationPolicy:
"""Whether a bundle admits one document expanding into many concepts.
OKF v0.2 §2 calls a concept "a single unit of knowledge within a bundle"
and a concept ID "the path of the concept's file within the bundle"
neither ties a concept to a source file. A door emitting exactly one flat
concept per dropped file therefore implements the shape Appendix A
presents v0.2 as migrating AWAY from, and §11 cannot notice: it checks
that every non-reserved `.md` parses with a non-empty `type`, so one giant
concept is fully conformant. Conformance is the floor, not the proof.
The presence of this object IS the capability. Every downstream branch
reads `profile.segmentation is not None` and never
`IndexPolicy.per_directory`: `STRICT_V1` already sets that field True
while Door B ignores it, so keying the 1-to-N path there would silently
change a shipped profile's output and break its byte-stability pin.
Every field NAMES a key and none supplies a value. The value of
`bundle_id` tracks the caller's own identity scheme, so it arrives through
`root_frontmatter_values` (decision D5): a constant here would claim a
decision this library does not own.
"""
hierarchical_paths: bool = True
bundle_id_key: str = "bundle_id"
segment_id_key: str = "segment_id"
offset_key: str = "source_offset"
nav_label: str = "index"
@dataclass(frozen=True) @dataclass(frozen=True)
class BundleProfile: class BundleProfile:
"""One bundle contract: types, frontmatter, filenames, index.""" """One bundle contract: types, frontmatter, filenames, index."""
@ -767,6 +798,11 @@ class BundleProfile:
paths: PathPolicy paths: PathPolicy
index: IndexPolicy index: IndexPolicy
ownership: OwnershipPolicy = field(default_factory=OwnershipPolicy) ownership: OwnershipPolicy = field(default_factory=OwnershipPolicy)
# Defaulted, so all four existing constants construct unchanged and every
# positional call site stays source-compatible. `None` is not "segmentation
# off" as a setting — it is the profile not having the capability at all,
# which is what the downstream `is not None` checks read.
segmentation: SegmentationPolicy | None = None
# The ingest-spec + Phase 2 contract. Every value here was a constant in # The ingest-spec + Phase 2 contract. Every value here was a constant in
@ -1039,6 +1075,41 @@ OKF_V0_2 = BundleProfile(
) )
# STRUCTURED_V1 plus the capability to split ONE document into MANY concepts.
#
# A new profile rather than a flag on an existing one, for the same reason
# STRUCTURED_V1 was: `DEFAULT` states commons' ingest-spec §6 layer and
# `STRICT_V1` mirrors a consumer's ratified contract, so moving either one's
# bytes from here would be this repo editing another repo's contract (O2).
# `OKF_V0_2` states upstream's. What stays byte-stable is therefore all four of
# them, proven by `tests/test_segmented_profile.py` at the profile level and by
# the golden suite at the byte level.
#
# What this profile adds on top of STRUCTURED_V1's facets and structure keys:
#
# - `segmentation` — the capability itself. Its presence is what every 1-to-N
# branch keys on.
# - `per_directory` — a segmented bundle has directories, and a bundle whose
# nested concepts are reachable only by guessing a path is a filing cabinet
# again. Set HERE rather than inherited, because STRUCTURED_V1 leaves it off.
# - `root_frontmatter=("bundle_id",)` — the identity carrier settled by order
# `…2527032751`: form (c), a root identifier consumers join on. Naming the
# key PERMITS it and fixes its position; the caller supplies the value (D5),
# because a bundle is a collection the caller delimits and only the caller
# knows what it is called. It is deliberately absent from
# `root_frontmatter_required`: Door B refuses a segmentation plan without an
# id, which is a rule about the door, not about every index this profile
# might ever write.
SEGMENTED_V1 = BundleProfile(
types=STRUCTURED_V1.types,
frontmatter=STRUCTURED_V1.frontmatter,
paths=STRUCTURED_V1.paths,
index=replace(STRUCTURED_V1.index, per_directory=True, root_frontmatter=("bundle_id",)),
ownership=STRUCTURED_V1.ownership,
segmentation=SegmentationPolicy(),
)
# "The latest version supported as STABLE", not the latest present in this # "The latest version supported as STABLE", not the latest present in this
# module. It therefore keeps v0.1 UPSTREAM semantics for as long as v0.2 is # module. It therefore keeps v0.1 UPSTREAM semantics for as long as v0.2 is
# provisional, and flipping it is the GA event — one auditable action rather # provisional, and flipping it is the GA event — one auditable action rather

View file

@ -221,4 +221,10 @@ def test_a_profile_is_assembled_from_its_policies() -> None:
"paths", "paths",
"index", "index",
"ownership", "ownership",
# SEGMENTED_V1's capability, registered here for the same reason
# `ownership` was at D2: the alternative to a policy on the object is a
# profile branch somewhere else, which is exactly what this assertion
# exists to forbid. Defaulted to None, so the four shipped profiles
# construct unchanged and their bytes do not move.
"segmentation",
} }

View file

@ -0,0 +1,113 @@
"""SEGMENTED_V1: the capability that lets one document become many concepts.
Additive in the strict sense the upstream-version policy demands. `DEFAULT`,
`STRICT_V1`, `STRUCTURED_V1` and `OKF_V0_2` keep the bytes they had, because
the new field is defaulted and every branch downstream is keyed on
`profile.segmentation is not None`.
That toggle is deliberately NOT `IndexPolicy.per_directory`. `STRICT_V1`
already sets that field True while Door B ignores it, so keying the capability
there would change `STRICT_V1`'s Door B output and break a byte-stability pin
that four consumers depend on. The measured trap is the reason the assertion
below is written as an identity check on all four profiles rather than on one.
"""
from __future__ import annotations
from dataclasses import replace
from llm_ingestion_okf.profiles import (
DEFAULT,
OKF_LATEST,
OKF_V0_2,
SEGMENTED_V1,
STRICT_V1,
STRUCTURED_V1,
BundleProfile,
SegmentationPolicy,
)
EXISTING = (DEFAULT, STRICT_V1, STRUCTURED_V1, OKF_V0_2)
# --- the capability -------------------------------------------------------
def test_only_the_new_profile_declares_the_segmentation_capability() -> None:
assert SEGMENTED_V1.segmentation is not None
for profile in EXISTING:
assert profile.segmentation is None
def test_the_capability_is_not_keyed_on_per_directory() -> None:
# STRICT_V1 already sets per_directory True and Door B ignores it. Keying
# the 1-to-N branch there would silently change STRICT_V1's output.
assert STRICT_V1.index.per_directory is True
assert STRICT_V1.segmentation is None
def test_the_new_profile_writes_an_index_per_directory_and_names_bundle_id() -> None:
assert SEGMENTED_V1.index.per_directory is True
assert SEGMENTED_V1.index.root_frontmatter == ("bundle_id",)
def test_bundle_id_is_permitted_at_the_root_rather_than_demanded_by_the_policy() -> None:
# The policy NAMES the key and fixes its position; the caller owns the
# value (D5). Demanding it here would claim a decision this library does
# not own -- Door B enforces the plan-requires-an-id rule instead.
assert "bundle_id" not in SEGMENTED_V1.index.root_frontmatter_required
def test_the_new_profile_keeps_the_structured_facets_and_keys() -> None:
assert SEGMENTED_V1.index.facets == STRUCTURED_V1.index.facets
assert SEGMENTED_V1.frontmatter.order == STRUCTURED_V1.frontmatter.order
assert SEGMENTED_V1.types is STRUCTURED_V1.types
assert SEGMENTED_V1.paths is STRUCTURED_V1.paths
# --- the additivity proof -------------------------------------------------
def test_the_four_existing_profiles_equal_freshly_constructed_copies() -> None:
# A defaulted field must not move any of them. `replace()` with no changes
# reconstructs each profile through the dataclass constructor, so a field
# that had become required -- or had picked up a non-default value -- shows
# up here as inequality rather than as golden churn six steps later.
for profile in EXISTING:
assert replace(profile) == profile
def test_okf_latest_is_still_default() -> None:
assert OKF_LATEST is DEFAULT
def test_a_profile_constructs_without_naming_the_new_field() -> None:
minimal = BundleProfile(
types=DEFAULT.types,
frontmatter=DEFAULT.frontmatter,
paths=DEFAULT.paths,
index=DEFAULT.index,
)
assert minimal.segmentation is None
# --- the policy's own defaults -------------------------------------------
def test_the_policy_names_the_keys_it_governs() -> None:
policy = SEGMENTED_V1.segmentation
assert policy is not None
assert policy.hierarchical_paths is True
assert policy.bundle_id_key == "bundle_id"
assert policy.segment_id_key == "segment_id"
assert policy.offset_key == "source_offset"
assert policy.nav_label == "index"
def test_the_policy_is_frozen() -> None:
policy = SegmentationPolicy()
try:
policy.bundle_id_key = "other" # type: ignore[misc]
except Exception:
return
raise AssertionError("SegmentationPolicy must be frozen")