Adds llm_ingestion_guard>=0.2,<0.3 as this library's first and only runtime dependency, and guard_adapter.py -- the one module that imports it. The flows themselves are unchanged: they still take an injected gate, and importing the package still does not import the guard, so a Door A consumer is unaffected by the dependency's state. Door B screens the exact bytes it persists. The guard's §6 bookends (prepare_input -> model -> screen_output) assume a model call in between; this library makes none, and prepare_input returns prompt-shaped text (sanitized AND spotlight-fenced with a per-call nonce) that must never reach disk. So the adapter calls screen_output alone, on the extracted text as it stands, and hands that same text back -- the verdict is then a statement about the bytes actually written. This supersedes the plan's "bookends" wording, recorded there under "Settled during implementation (step 4)". It follows that the gate refuses rather than repairs: a file carrying an invisible carrier is rejected, not stripped and persisted. Sanitizing first would write a document differing invisibly from the operator's file while source_sha256 still points at the original bytes. Operator decision; the policy is PRESET_USER_UPLOAD, so any finding at all is held back. Door C hands the bundle over whole to okf.import_bundle, which resolves the cross-link graph across concepts. Per-concept reasons are derived from the scan findings (severity:label) because stamp_concept keeps the disposition and drops the reason strings behind it. Assumption B1 closes as a signature smoke test over what the adapters actually call -- screen_output and okf.import_bundle signatures, the Disposition values both doors compare by value, the Origin/Channel vocabularies Door C validates, the result fields read, and the upload preset's shape. prepare_input is not pinned: drift there cannot reach this library. Behaviour is pinned against the real scanner too, including the persist-gate proof that a fail-secure fixture leaves the bundle byte-identical. B2 closes with it: git+https tag install over anonymously readable HTTPS, no credential. A direct reference is an install-time channel, not the pin -- the range stays in pyproject, is satisfied by the tag install today, and resolves normally once the package index exists. A packaging test enforces that the guard remains the only runtime dependency (verified by hand-mutation).
34 lines
1.3 KiB
Python
34 lines
1.3 KiB
Python
"""Packaging contract: a PEP 561 typed package with exactly one dependency.
|
|
|
|
Consumers run mypy --strict against the inline annotations; without the
|
|
py.typed marker mypy degrades every imported symbol to Any.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
import llm_ingestion_okf
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def test_package_ships_py_typed_marker() -> None:
|
|
package_dir = Path(llm_ingestion_okf.__file__).parent
|
|
assert (package_dir / "py.typed").is_file()
|
|
|
|
|
|
def test_the_only_runtime_dependency_is_the_security_boundary() -> None:
|
|
"""The stdlib-only rule, enforced rather than asserted in prose.
|
|
|
|
One dependency is permitted — the guard — because security is the one
|
|
thing this library must not implement. Everything else stays stdlib, so
|
|
a consumer vendoring this package takes on no transitive surface. The
|
|
version RANGE is the pin: it resolves against a package index, and is
|
|
satisfied by the git+https tag install until that index exists.
|
|
"""
|
|
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.2,<0.3"]
|