build(deps): move the guard pin from >=0.3,<0.4 to >=1.2,<2.0

The operator's condition for revisiting the pin was met (v1.2.0 contains
the flow-mapping frontmatter fix, commit 5870483) and the operator has now
approved the move itself, dispatched as its own order because it changes
consumer-visible Requires-Dist and shifts golden-fixture admission.

Floor 1.2: this library needs the flow-mapping support (`generated: { by:
x, at: y }`) that landed there — without it Door C fail-secures every
concept carrying that stamp. Ceiling <2.0, not narrower: the guard's own
1.0.0 release freezes its exported surface until a 2.0.0, and explicitly
keeps calibration (severities, dispositions) free to move within 1.x, so a
tighter ceiling here would claim a stability guarantee neither side needs.

Re-measured through Door C against the guard's own default
(allow_reserved=True, matching how the earlier recommendation measured
it), over the 9 concept documents across all four Door-A goldens:
4/9 admitted at 0.3.4 -> 8/9 admitted at 1.2.0, confirming the number
already reported. The ninth (a `sources` block-list carrying `resource`)
stays refused by design (G30) and is not expected to move.

Failing-test-first: test_guard_adapter.py::test_guard_version_is_inside_the_pin
and test_packaging.py::test_the_only_runtime_dependency_is_the_security_boundary
were updated to the new pin first and confirmed red against the
still-installed 0.3.4, then `uv sync --extra extract` installed 1.2.0 and
both went green. Full suite (615), ruff and mypy --strict clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RxcxzNwpX1kDP53n1rLhM5
This commit is contained in:
Kjell Tore Guttormsen 2026-08-23 21:05:18 +02:00
commit e60f7261a1
8 changed files with 37 additions and 21 deletions

View file

@ -35,7 +35,7 @@ one boundary rule:
when the bytes there are already identical, never overwritten otherwise.
**Boundary rule (non-negotiable, zero overlap):** `llm-ingestion-guard`
(pinned `>=0.3,<0.4`) answers "is this content safe to persist?" —
(pinned `>=1.2,<2.0`) answers "is this content safe to persist?" —
scan/sanitize/quarantine/fail-secure/provenance-stamp. This library is
plumbing: connect source → materialize deterministic OKF bundle → generate
index. Never reimplement security; call the guard at persist gates
@ -176,12 +176,12 @@ Phase 4 preconditions (coordination, not unilateral moves):
## Stack
Python 3.10+. Package `llm_ingestion_okf` (src layout, hatchling).
**Exactly one runtime dependency, ever:** `llm-ingestion-guard>=0.3,<0.4`
**Exactly one runtime dependency, ever:** `llm-ingestion-guard>=1.2,<2.0`
(itself zero-dep), landed with the Door B/C persist gates. Everything else is
stdlib, and a packaging test enforces it. Only `guard_adapter.py` imports the
guard; importing the package does not. Install channel until the package
index exists (a direct reference is a channel, not the pin):
`pip install "llm-ingestion-guard @ git+https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git@v0.3.4"`.
`pip install "llm-ingestion-guard @ git+https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git@v1.2.0"`.
Binary extraction parsers live behind the `[extract]` extra only — today
`pdfplumber>=0.11.10,<0.12` for `pdf`. Extracted PDF text is pinned to an
exact transitive parser version (`pdfminer.six==20260107`), so widening that

View file

@ -42,7 +42,7 @@ pip install "llm-ingestion-okf @ git+https://git.fromaitochitta.com/open/llm-ing
The guard tag is paired to the okf tag, not to this branch: `v0.4.0` declares
`llm-ingestion-guard>=0.2,<0.3`, which `v0.2.0` satisfies and later guard tags
do not. `main` has since moved its own pin to `>=0.3,<0.4` (see
do not. `main` has since moved its own pin to `>=1.2,<2.0` (see
[Requirements](#requirements)); that pin reaches you in the next stable tag,
not in the commands above. Reading a pin off this branch and installing it
against `v0.4.0` is the one combination that fails.
@ -75,7 +75,7 @@ bundle:
Security is owned by the sibling package
[`llm-ingestion-guard`](https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security)
(pinned `>=0.3,<0.4`). The division is strict:
(pinned `>=1.2,<2.0`). The division is strict:
- **guard** answers "is this content safe to persist?" — scan, sanitize,
quarantine, fail-secure, provenance stamping.
@ -239,7 +239,7 @@ structure from either.
## Requirements
Python 3.10+, and exactly one runtime dependency — the security boundary,
`llm-ingestion-guard>=0.3,<0.4`. Everything else is stdlib. The commands are
`llm-ingestion-guard>=1.2,<2.0`. Everything else is stdlib. The commands are
under [Install](#install); what follows is why they look the way they do.
From a checkout, the test suite runs with:
@ -259,7 +259,7 @@ clone rather than a `pip install`.
A git URL is a PEP 508 direct reference and pins one exact tag, so it is an
install-time *channel*, not the pin: the range above stays the declared
dependency — a wheel built from this branch carries `Requires-Dist:
llm-ingestion-guard<0.4,>=0.3`, measured 2026-08-10 — and resolves normally
llm-ingestion-guard<2.0,>=1.2`, measured 2026-08-23 — and resolves normally
once the package index exists. A wheel built from a *tag* carries that tag's
range instead, which is why the install commands pair tag with tag.

View file

@ -22,7 +22,17 @@ classifiers = [
# against a package index, and is satisfied today by the git+https tag
# install documented in the README (a direct reference is an install-time
# channel, not a dependency declaration).
dependencies = ["llm-ingestion-guard>=0.3,<0.4"]
#
# Floor 1.2, not the 1.0.0 freeze: this library needs the flow-mapping
# frontmatter support (`generated: { by: x, at: y }`) that landed in the
# guard's 1.2.0, without which Door C fail-secures every concept carrying
# it. Ceiling <2.0, not a narrower minor: the guard's own 1.0.0 release
# promises no exported name is removed, renamed or given a different
# meaning short of a 2.0.0 — calibration (severities, dispositions) is
# explicitly free to move within 1.x under that same promise, so a tighter
# ceiling here would claim a stability guarantee the guard does not need to
# keep and we do not need to demand.
dependencies = ["llm-ingestion-guard>=1.2,<2.0"]
[project.optional-dependencies]
# Binary file-type extraction parsers. OPT-IN ONLY: this extra pulls binary
@ -94,10 +104,10 @@ ignore_missing_imports = true
# both the failure and the two-command recovery).
#
# Either way the range above stays the pin, and the pin is per-tree: a wheel
# built from THIS tree carries `Requires-Dist: llm-ingestion-guard<0.4,>=0.3`,
# measured 2026-08-21 against the built wheel. The `<0.3,>=0.2` this comment
# carried before was the `v0.4.0` tag's range — still true of that tag, never
# built from THIS tree carries `Requires-Dist: llm-ingestion-guard<2.0,>=1.2`,
# measured 2026-08-23 against the built wheel. The `<0.4,>=0.3` this comment
# carried before was the `v0.3.4` tag's range — still true of that tag, never
# true of this tree. Reading a range off one and installing it against the
# other is the one combination that fails.
[tool.uv.sources]
llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", tag = "v0.3.4" }
llm-ingestion-guard = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git", tag = "v1.2.0" }

View file

@ -50,7 +50,7 @@ from .profiles import DEFAULT
# The guard's non-blocking floor and its review queue, by VALUE (`Disposition`
# is a `str, Enum`, so the value is the stable thing to compare against across
# the pinned `>=0.3,<0.4` range). Pinned as constants here rather than imported
# the pinned `>=1.2,<2.0` range). Pinned as constants here rather than imported
# because the dependency is injected — deliberately restated independently of
# Door B's copy in `inbox.py`, so drift in either door is visible rather than
# silently shared. The step-4 adapter's signature smoke test is what catches a

View file

@ -133,7 +133,7 @@ def render_inbox_concept(
# The guard's non-blocking floor. `Disposition` is a `str, Enum` in
# llm-ingestion-guard, so its VALUE is the stable thing to compare against
# across the pinned `>=0.3,<0.4` range. Pinned as a constant here rather than
# across the pinned `>=1.2,<2.0` range. Pinned as a constant here rather than
# imported, because the dependency is injected (see `Gate`): the step-4
# adapter's signature smoke test is what catches a rename in the guard.
_DISPOSITION_PERSIST = "warn"

View file

@ -58,11 +58,17 @@ def _snapshot(directory: Path) -> dict[str, bytes]:
def test_guard_version_is_inside_the_pin() -> None:
"""The pin is `>=0.3,<0.4`; a version outside it invalidates every
"""The pin is `>=1.2,<2.0`; a version outside it invalidates every
by-value comparison below and must fail loudly rather than be discovered
through a mis-branched verdict."""
through a mis-branched verdict. The floor is 1.2, not the freeze at 1.0,
because this library relies on the flow-mapping frontmatter support that
landed in 1.2.0; the ceiling is 2.0 because the guard's own 1.0.0 release
promises no exported name is removed, renamed or given a different
meaning short of a 2.0.0 calibration (severities, dispositions) moves
freely within 1.x by that same promise, so pinning past minor 2 here
would be tighter than the guarantee it rests on."""
major, minor = (int(part) for part in guard.__version__.split(".")[:2])
assert (major, minor) == (0, 3), guard.__version__
assert major == 1 and minor >= 2, guard.__version__
def test_guard_screen_output_signature_is_what_door_b_calls() -> None:

View file

@ -31,7 +31,7 @@ def test_the_only_runtime_dependency_is_the_security_boundary() -> None:
"""
tomllib = pytest.importorskip("tomllib") # stdlib from 3.11; the pin holds on 3.10 too
pyproject = tomllib.loads((PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8"))
assert pyproject["project"]["dependencies"] == ["llm-ingestion-guard>=0.3,<0.4"]
assert pyproject["project"]["dependencies"] == ["llm-ingestion-guard>=1.2,<2.0"]
def test_the_declared_version_agrees_with_the_packaged_one() -> None:

6
uv.lock generated
View file

@ -496,8 +496,8 @@ wheels = [
[[package]]
name = "llm-ingestion-guard"
version = "0.3.4"
source = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v0.3.4#adf93e47fbcdbd76bdb9baf6393611230342397a" }
version = "1.2.0"
source = { git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v1.2.0#9aeceb02c0b422160f6064c2b94505eae5cea418" }
[[package]]
name = "llm-ingestion-okf"
@ -521,7 +521,7 @@ dev = [
[package.metadata]
requires-dist = [
{ name = "llm-ingestion-guard", git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v0.3.4" },
{ name = "llm-ingestion-guard", git = "https://git.fromaitochitta.com/open/llm-ingestion-pipeline-security.git?tag=v1.2.0" },
{ name = "pdfplumber", marker = "extra == 'extract'", specifier = ">=0.11.10,<0.12" },
]
provides-extras = ["extract"]