feat(import): Door C flow against an injected import gate (Phase 2 step 5)
Reads an external OKF bundle as {bundle-relative path -> document text},
hands it WHOLE to an injected gate over the guard's okf.import_bundle (a
bundle-level call: it resolves the cross-link graph across concepts), and
merges only concepts clearing the non-blocking floor. Same injection pattern
as Door B, so the core stays dependency-free while the CI channel for the
real guard is settled.
Two constraints shaped the design and are pinned by tests:
- A merged concept is written VERBATIM. Stamping provenance into it would
require round-tripping its frontmatter through this library's line-oriented
parser, which cannot represent the block lists the guard's parser accepts --
silent data loss -- and would persist bytes the guard never screened.
- Ownership is therefore proven by content identity: identical bytes at the
target name are a no-op re-merge (re-import of an unchanged bundle is
idempotent), and anything else at the name is refused. Curated content and
an updated concept are refused alike; refusing is what never destroys.
The floor is fail-closed beyond the plan's "no error" wording: an error, an
unrecognised disposition, and a concept the gate returned no verdict for are
all refusals. quarantine_review stays its own bucket, as at Door B.
origin/channel are validated against the guard's pinned vocabulary -- it
derives trust from origin by enum identity, so an unrecognised string would be
silently downgraded rather than caught.
Three primitives promoted for reuse rather than duplicated:
reduce_to_id_grammar and check_filename_length to materialize.py, and
extract.decode_text. Door C slugs the WHOLE concept path, so tables/users.md
and views/users.md stay distinct. Concept discovery folds case explicitly
rather than globbing *.md, whose case-sensitivity follows the filesystem and
would import the same bundle differently on APFS and ext4.
README's "what is gated today" section corrected: it claimed nothing is gated,
which is no longer true, but the honest statement is narrower than "the doors
are gated" -- the library cannot verify that an injected adapter is a real
guard, and a permissive stub is believed.
405 tests green; ruff, ruff format and mypy --strict clean.
This commit is contained in:
parent
d812a839be
commit
f10fc60de2
11 changed files with 1250 additions and 61 deletions
|
|
@ -65,6 +65,40 @@ No scanning, sanitizing, or quarantine logic is implemented here.
|
|||
(reserved-file policy differs per consumer and becomes configurable in
|
||||
Phase 3).
|
||||
|
||||
### Settled during implementation (step 5)
|
||||
|
||||
- **Verbatim merge, no frontmatter of ours.** A merged concept is written
|
||||
exactly as the gate saw it. This is a correctness constraint, not a
|
||||
preference: the guard's frontmatter parser accepts block lists, which this
|
||||
library's line-oriented `parse_frontmatter` cannot round-trip, so stamping
|
||||
provenance into an external concept would silently drop sender data — and
|
||||
would persist bytes the guard never screened.
|
||||
- **Ownership by content identity.** With no stamp available, an occupied
|
||||
target name is re-used only when the bytes already there are identical (a
|
||||
no-op re-merge, so re-import of an unchanged bundle is idempotent).
|
||||
Anything else at the name — curated content or an updated version of the
|
||||
same concept — is refused under `collision_unstamped`; the operator removes
|
||||
the file to accept an update. A configurable stamping/reserved-file policy
|
||||
is Phase 3's.
|
||||
- **The merge floor is fail-closed, not "no error".** Deliverable 1's wording
|
||||
is the looser reading: a concept carrying no error but a FAIL_SECURE or
|
||||
unrecognised disposition is refused, and a concept the gate returned no
|
||||
verdict for at all is refused. Only the guard's non-blocking floor merges —
|
||||
the same floor Door B applies, with `quarantine_review` reported as its own
|
||||
bucket rather than folded into rejection.
|
||||
- **Upstream has already moved past the pin (observed, not acted on).** The
|
||||
guard's `main` carries a commit that adds `allow_reserved=True` to
|
||||
`okf.import_bundle` and *scans* `index.md`/`log.md` in a mode-b import
|
||||
instead of path-rejecting them. The pin is the `v0.2.0` tag, where the
|
||||
kwarg does not exist and rejection is unconditional, so this door is built
|
||||
against the tag. Whoever bumps the pin owns re-checking that branch: today
|
||||
a reserved name in a received bundle arrives as a per-concept rejection.
|
||||
- **`origin`/`channel` are validated against the guard's pinned vocabulary.**
|
||||
The guard derives trust from `origin` by enum *identity*, so an unrecognised
|
||||
string would be silently downgraded to untrusted. The library refuses to
|
||||
carry a provenance declaration it cannot recognise rather than let a typo
|
||||
decide trust — refusing to guess, not deciding trust itself.
|
||||
|
||||
## Design decisions fixed by this plan
|
||||
|
||||
- Trust follows `origin`, never `channel` (guard doctrine) — callers must supply
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue