feat(guard): wire Doors B and C to the real guard (Phase 2 step 4)
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).
This commit is contained in:
parent
f10fc60de2
commit
241e00f27a
8 changed files with 578 additions and 38 deletions
|
|
@ -17,9 +17,12 @@ classifiers = [
|
|||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
]
|
||||
# Core stays stdlib-only. The single permitted future runtime dependency is
|
||||
# llm-ingestion-guard (>=0.2,<0.3), added when the first persist gate lands.
|
||||
dependencies = []
|
||||
# Exactly one runtime dependency, ever: the security boundary. Everything
|
||||
# else is stdlib. The version range is the real pin — it resolves normally
|
||||
# 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.2,<0.3"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
# Reserved for binary file-type extraction parsers (pdf/docx/xlsx).
|
||||
|
|
@ -39,3 +42,10 @@ target-version = "py310"
|
|||
[tool.mypy]
|
||||
strict = true
|
||||
python_version = "3.10"
|
||||
|
||||
# llm-ingestion-guard ships no py.typed marker, so its symbols arrive as Any.
|
||||
# The adapter coerces every value it carries across the seam to a concrete
|
||||
# type, which is what keeps --strict meaningful on this side of it.
|
||||
[[tool.mypy.overrides]]
|
||||
module = ["llm_ingestion_guard", "llm_ingestion_guard.*"]
|
||||
ignore_missing_imports = true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue