fix(profiles): P1-F1 — a permitted root key is not a required one
`IndexPolicy.root_frontmatter` carried two claims at once: which keys the root index may carry and in what order, and which it must carry. The judge read it as the second, so `OKF_V0_2` — which names `okf_version` only to fix its position, upstream granting it as a MAY (§8:509-510, §12:773-775) — reported every bundle exercising that MAY as violating. Measured in P1 over 17 bundles: 14 failed with exactly this one violation, upstream's own four reference bundles among them, while D5's emitter treated the same key as optional. We emitted a MAY correctly and graded it a MUST. `root_frontmatter` now permits and orders; `root_frontmatter_required` demands. A required key outside the ordered set fails at construction, the same contradiction `FrontmatterSchema` already refuses when `required` strays outside `allowed`. - `OKF_V0_2` requires none — upstream's MAY, stated on the judge side too. - `STRICT_V1` requires all three: the proving consumer's root index carries exactly those keys in that order on every bundle measured (`c5141f8`), so separating the meanings costs them nothing. - `DEFAULT` names no root keys and is untouched. Re-measured over the eight bundle-root indexes reachable locally (our four goldens, upstream's four): 7 of 8 failing under the old semantics, 0 of 8 after. Emit path byte-identical — the golden suite fails otherwise. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqCmfJ2ukpFXeFjfab8wvy
This commit is contained in:
parent
a8ceea9667
commit
2c01e58259
2 changed files with 85 additions and 5 deletions
|
|
@ -375,9 +375,18 @@ class IndexPolicy:
|
|||
caller supplies the listing, and a policy that does not judge it refuses
|
||||
to be handed one at all — so code written to the wrong contract fails at
|
||||
the call rather than passing every test one would think to write.
|
||||
- `root_frontmatter` — the ordered keys the ROOT index carries, where
|
||||
- `root_frontmatter` — the ordered keys the ROOT index may carry, where
|
||||
nested indexes carry none. Confirmed independently in both consumers, so
|
||||
the asymmetry is shape rather than one repo's preference.
|
||||
the asymmetry is shape rather than one repo's preference. Naming a key
|
||||
here PERMITS it and fixes its position; it does not demand it.
|
||||
- `root_frontmatter_required` — the subset that must actually be present.
|
||||
Separate from the tuple above because the two are different claims, and
|
||||
collapsing them was P1-F1: `OKF_V0_2` names `okf_version` to fix its
|
||||
position, but upstream §8/§12 grant it as a MAY, and judging the pair as
|
||||
one field failed 14 of 17 real bundles — upstream's own four included —
|
||||
each with exactly this one violation, while our emitter treated the same
|
||||
key as optional. A profile whose consumer really does demand the keys
|
||||
says so here, which is what `STRICT_V1` does.
|
||||
"""
|
||||
|
||||
name: str
|
||||
|
|
@ -388,8 +397,16 @@ class IndexPolicy:
|
|||
allows_prose: bool = True
|
||||
entries_match_directory: bool = False
|
||||
root_frontmatter: tuple[str, ...] = ()
|
||||
root_frontmatter_required: frozenset[str] = field(default_factory=frozenset)
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
stray = sorted(self.root_frontmatter_required - set(self.root_frontmatter))
|
||||
if stray:
|
||||
raise ValueError(
|
||||
f"required root keys must be named in the ordered set "
|
||||
f"(got {', '.join(repr(key) for key in stray)}) — a key demanded "
|
||||
"but never named could not be judged for position"
|
||||
)
|
||||
if self.requires_description and "description" not in self.link_pattern.groupindex:
|
||||
raise ValueError(
|
||||
"link_template names {description} but link_pattern has no "
|
||||
|
|
@ -526,7 +543,7 @@ class IndexPolicy:
|
|||
found = [
|
||||
IndexViolation(key, "is pinned on the root index and absent", "index_root_key_missing")
|
||||
for key in self.root_frontmatter
|
||||
if key not in head
|
||||
if key in self.root_frontmatter_required and key not in head
|
||||
]
|
||||
present = [key for key in head if key in self.root_frontmatter]
|
||||
declared = [key for key in self.root_frontmatter if key in head]
|
||||
|
|
@ -646,6 +663,11 @@ STRICT_V1 = BundleProfile(
|
|||
allows_prose=False,
|
||||
entries_match_directory=True,
|
||||
root_frontmatter=("okf_version", "bundle_profile", "okf_spec_commit"),
|
||||
# The proving consumer demands all three, not merely permits them: their
|
||||
# root index carries exactly these keys in exactly this order on every
|
||||
# bundle measured (`c5141f8`). Stated explicitly so that separating
|
||||
# "permitted" from "required" costs them nothing.
|
||||
root_frontmatter_required=frozenset({"okf_version", "bundle_profile", "okf_spec_commit"}),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -700,7 +722,11 @@ _OKF_V0_2_KEY_ORDER = (
|
|||
#
|
||||
# Declaring it stays a MAY: none of upstream's four reference bundles carries
|
||||
# the key at all (catalog grepped `okf/bundles` and `okf/samples`: zero hits),
|
||||
# so omitting `root_frontmatter_values` emits no block.
|
||||
# so omitting `root_frontmatter_values` emits no block. The JUDGE says the same
|
||||
# thing since P1-F1 — the key is named here and left out of
|
||||
# `root_frontmatter_required` — because for one release it did not, and a
|
||||
# bundle exercising the MAY was reported as violating by the very profile that
|
||||
# had emitted it correctly.
|
||||
#
|
||||
# **Measured limitation (guard 0.2.0, 2026-07-26):** a bundle emitted under this
|
||||
# profile cannot be read back through a guard-gated import. The guard's T2
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue