Skip to content

core.typedshims

core.typedshims

Outer-ring residue of the typed-shim boundary (dn-inner-outer-core §2.7, K1 / bp-090).

The package's inner init text moved to core/kernel/typedshims/. The shims themselves stay here in the outer ring: each wraps an untyped third-party surface (lancedb, sknetwork, psutil) that is inadmissible to the inner base, so the shim modules compute outer. Core imports the shim, never the raw package. This init is stdlib-import-free so it stays inner by construction (a pure package marker); the residue shim submodules beside it are the outer machinery.

lancedb

Typed facade over lancedb (type-system-as-core-audit.md §2.5 boundary wrapper).

lancedb ships no py.typed (V2, 2026-07-11), so a raw import launders Any through every downstream call. This module is the ONE place core touches the raw package: values coming back from lancedb are pinned to the minimal Protocol surface the vector store actually uses, so the checked region sees honest types. Compute/storage-only dependency — embedded, no daemon, no network (Invariant 2).

Do not widen these Protocols speculatively: they describe what core calls today, and each addition should arrive with the call that needs it.

[cross-ref: extension] bp-103 (warrant finding-0176) brings four such calls, all for VectorStore.supersede_source/rows_for_source: update() (in-place column write), a FILTERED count_rows(filter), select() (column projection) and scan(). Additive — every pre-existing caller compiles and behaves unchanged.

Why this file stopped being a pure typing facade. Until bp-103 connect() simply annotated the raw object and returned it; nothing was wrapped at runtime. scan() has no counterpart on the raw Table — it is search(None), an overload whose None arm means "no vector, just filter/project". Absorbing that overload is precisely what a boundary shim is for (finding-0176: "the shim is the place where the raw package's overloads are made honest"), and it cannot be done with an annotation alone. So connect() now returns thin ADAPTERS that add scan() and forward the rest. The adapters spell every method out rather than forwarding through __getattr__, because a __getattr__ wrapper would launder Any straight back into the checked region — the exact hole this module exists to close.

Row = dict[str, object] module-attribute

ArrowTable

Bases: Protocol

The slice of pyarrow.Table the store consumes from to_arrow().

to_pylist()

TableNames

Bases: Protocol

Result of list_tables() — we read only the name list.

tables instance-attribute

UpdateResult

Bases: Protocol

Result of Table.update — rows matched-and-written, and the new table version.

[banner: correction] Declared for completeness of the boundary, but the store deliberately does NOT read rows_updated. UpdateResult is the 0.33 return shape and pyproject.toml:12 pins lancedb>=0.10, a range whose older members returned None from update(); the pin is outside bp-103's write_scope, so VectorStore.supersede_source derives its count from a filtered count_rows() instead (bp-103 §11, first parked decision). Raising the floor pin is the re-entry condition for trusting this field.

rows_updated instance-attribute
version instance-attribute

VectorQuery

Bases: Protocol

LanceDB's chained query builder — VectorStore.search (KNN) and .scan() (filter-only).

metric(name)
where(predicate, *, prefilter=...)
select(columns)
limit(k)
to_list()

VectorTable

Bases: Protocol

The slice of a LanceDB table the store calls.

add(rows)
count_rows(filter=...)
delete(predicate)
update(where, values)
to_arrow()
search(vector)
scan()

VectorDB

Bases: Protocol

The slice of a LanceDB connection the store calls.

list_tables()
open_table(name)
create_table(name, *, schema)
drop_table(name)

connect(uri)

Open/create an embedded LanceDB at uri — the sole typed entry point.

psutil

Typed facade over psutil (type-system-as-core-audit.md §2.5 boundary wrapper).

psutil ships no py.typed (V2, 2026-07-11). This module is the ONE place the REPO touches the raw package; the vitals path and the lifecycle launcher read system measurements through these typed functions only. Local measurement only — no network (Invariant 2).

[banner: correction] That first sentence used to read "the ONE place core touches the raw package", and it was aspirational, not enforced — until bp-106 nothing anywhere (ops/type_gate.py, ops/import_lint.py, scripts/check_imports.py, the hooks, CI, [tool.ruff]) mentioned typedshims at all, so the rule was a docstring sentence. bp-105 then imported raw psutil in ops/lifecycle/launcher.py (finding-0198) and nothing objected — the violation was authored, reviewed, gated and merged. Two corrections, both from bp-106: the scope is the whole repo, not only core/ (bp-105's violation was in ops/, which the old wording arguably did not cover), and the rule is now scanned mechanically by ops.type_gate (raw_shim_imports), with an inline # typedshim-exempt: <reason> as the only waiver. See bp-106 §3 Q1 and docs/findings/finding-0223.md.

Exceptions are ABSORBED here, never re-raised (bp-106 §3 Q5). The states a caller must distinguish — NoSuchProcess, AccessDenied — are psutil types. A facade that raises them forces every caller to import psutil to name them in an except, which moves the import while keeping the type dependency: a quarantine in name only. So the process accessors below return None on any failure. loadavg_1m() set this precedent before the rule was written.

VirtualMemory dataclass

The fields of psutil.virtual_memory() the vitals emitter reads.

total instance-attribute
available instance-attribute
percent instance-attribute

virtual_memory()

process_rss(pid)

Resident-set size of pid, in bytes.

cpu_percent()

System-wide CPU percent since the previous call (non-blocking: interval=None).

loadavg_1m()

1-minute load average, or None on a platform without getloadavg.

process_create_time(pid)

Unix epoch seconds at which pid's process was created, or None if unreadable.

None, never an exception: psutil.NoSuchProcess / AccessDenied are psutil TYPES, so a raising facade would force every caller to import psutil to name them — moving the import while keeping the dependency. Absorbing them here is what makes the quarantine real.

The launcher's D1 disproof reads this: a process created after its run row's started_at cannot have written that row (finding-0198).

process_exe_name(pid)

Basename of the binary pid is actually EXECUTING, or None if unreadable.

The BASENAME and not the whole path: an interpreter living under ~/python-projects/ would otherwise make every binary on that path read as one, and the launcher's D2 disproof ("is this process a Python interpreter?") could never fire.

⚑ Empty is UNREADABLE, not an answer. psutil returns '' — it does not raise — when a process's executable cannot be determined, so '' -> None and the caller falls through to its fallback. Taking '' as the answer would make D2 fire against every such process (test_an_empty_exe_is_treated_as_unreadable_not_as_an_answer is the mutation pin).

exe() and not cmdline(): on macOS cmdline() raises AccessDenied for a foreign owner (measured against pid 1) while name()/exe() read fine — and a foreign owner is exactly the deployed case, the daemon running as the ouroboros principal.

process_name(pid)

pid's process name (e.g. 'launchd', 'python3.13'), or None if unreadable.

This is the comm/argv0 basename, which depends on HOW the process was invoked — under uv run pytest on Linux it resolves to the console script, not the interpreter (warrant finding-0211). It is therefore the launcher's FALLBACK, never its first choice; see _process_identity for why the order is load-bearing in both directions.

name() and not cmdline(), for the same AccessDenied reason recorded on process_exe_name.

Emptiness is deliberately NOT folded to None here, unlike process_exe_name: name() is the last probe, so '' there is a real (if uninformative) answer that the launcher's D2 reads as "not a Python interpreter". Folding it to None would silently convert a disproof into ambiguity, and ambiguity REFUSES (finding-0186) — a behaviour change, not a tidy-up.

sknetwork

Typed facade over sknetwork (type-system-as-core-audit.md §2.5 boundary wrapper).

scikit-network ships no py.typed (V2, 2026-07-11). This module is the ONE place core touches the raw package — today that is the Louvain cross-check the reasoning complex runs as a diagnostic beside its spectral partition. Offline compute only — no network (Invariant 2).

The raw import stays lazy (inside the function): sknetwork is heavy and the Louvain cross-check is off the live path, matching the call site's prior behavior in core/complex/spectral.py.

louvain_labels(adjacency, *, resolution=1.0, random_state=0)

Modularity (Louvain) community labels for a (sparse) adjacency — one label per node. Deterministic under the fixed random_state.