Adapter-Author Surface¶
External subjects implement the TermVerify producer contract: an Adapter
drives a run, ConstraintPorts applies the requested constraints and states
each receipt's enforcement tier, and (for the in-process path)
DirectApplication executes input and clock epochs. The
curated public surface for that work is the top-level termverify package:
every contract name is re-exported there and is identical to its module-path
definition, so both import styles are interchangeable.
from termverify import (
Adapter,
ConstraintPorts,
DirectAdapter,
DirectApplication,
RunConfiguration,
TextInput,
is_key_chord,
parse_transcript,
)
The module paths remain public and documented — termverify.adapter defines
the contract, termverify.direct the deterministic in-process runtime,
termverify.transcript the authoritative codec.
tests/test_public_surface.py pins the guarantee that the top level and the
module paths never drift: every name in termverify.adapter.__all__ and
termverify.direct.__all__ is importable from termverify, and every codec
and registry name re-exported at the top level is the identical object to
its defining module's, not a copy. The same file pins termverify.__all__ to
an exact set, so an accidental future export fails the suite.
The key registries are the one exception to the interchangeable-import rule:
KEY_NAMES, is_key_chord, and encode_key_chord are public names whose
defining modules stay private. Import them from termverify, never from an
underscore path.
What the surface contains¶
- The contract protocols:
Adapter,ConstraintPorts,DirectApplication, and the reference in-process runtimeDirectAdapter. - Run configuration values:
RunConfigurationand the per-constraint configurations (ClockConfiguration,TerminalConfiguration,FilesystemConfiguration,NetworkConfiguration,NetworkEndpoint), plusManualTime. - Inputs:
TextInput,KeyInput,Resize,ClockAdvance,Stop, and theDispatchInputunion. - Results and receipts: the start results (
Started,StartFailed,StartTerminated,StartUnsupported,StartResult), epoch results (EpochCompleted,EpochResult,TerminalResult,AdapterFailure), run outcomes (RunFinished,RunFailed,ExitStatus), the enforcement receipts (SeedReceipt,ClockReceipt,LocaleReceipt,TimezoneReceipt,TerminalReceipt,FilesystemReceipt,NetworkReceipt,EnforcementReceipt,AppliedConstraints,ConstraintUnsupported), and the enforcement-tier vocabulary (EnforcementTier,ENFORCEMENT_TIERS,DeliveryRecord). - Observations and evidence values:
Observation,UiObservation,ProcessObservation,Frame,Cursor,Region,Event,Diagnostic. - Supporting types:
ConstraintName,JsonInput,FrozenJsonValue,freeze_json, the package version string__version__, the transcript-schema access API (TRANSCRIPT_SCHEMA_V1_ID,transcript_schema_v1_bytes,transcript_schema_v1_json), and safe evidence persistence (persist_transcript_evidence, the surface's only transcript-writing function; onlymode="safe"persists —mode="sensitive"raises). - The authoritative transcript codec:
parse_transcript,serialize_transcript, andTranscriptValidationError. These decidetermverify.transcript/v1acceptance; the schema access API above is a non-exhaustive structural aid and schema acceptance is not conformance (docs/knowledge/protocol.md). The aid and the validator are both on the surface, so the authoritative one is never the harder import. Records are plaindicts; the aliasRecordis defined attermverify.transcript, a public module path, and theJsonValueit is built from is re-exported there from a private module — use it viatermverify.transcript, whose re-export is the supported spelling. - The closed key registries' entry points:
KEY_NAMESandis_key_chordfromtermverify.key/v1, andencode_key_chordfromtermverify.key-encoding/v1. Useis_key_chordto validate a chord before putting it in aKeyInput, andencode_key_chordwhen your adapter drives a real terminal — it returns the xterm-legacy encoding as astr(encode it yourself for a byte channel), orNonefor the explicit fail-closed verdict unencodable. Both functions take a chord by exact type: alistortupleofstr, matching the codec's fail-closed discipline. ANamedTupleof key names or any otherSequenceis rejected even when the names it carries are valid, andencode_key_chordraisesValueError— with the same message it uses for a genuinely invalid chord — rather than returning the unencodable verdict. Your type checker will not catch theNamedTuplecase: it is atuplestatically and a rejection at runtime. Model chords as plain tuples.
StartFailed.observation is normally absent. An adapter may provide it only
after complete constraint negotiation, when startup failed after independently
observing process exit. The observation must use the effective initial manual
time and carry exited-process evidence; recorders emit it before run.failed.
What the surface deliberately excludes¶
termverify.terminal(real-terminal runtime, and the platform-specific bindings it ships) andtermverify.cooperation(opt-in delivered-tier ports) stay at their module paths; importing them from the top level would make the portable core's import surface platform- and opt-in-dependent. The adapter in that module is itself platform-neutral, but the bindings beside it are not, and the exclusion is about the module as a whole.- The verification core (
termverify.recorder,termverify.comparator,termverify.replay) is consumer-side, not adapter-author-side; it stays at its module paths until a consumer-surface decision curates it separately. The codec above is not part of that core — it is the shared contract both sides validate against. _-prefixed modules are never part of the surface, and neither is any name they define that is not listed intermverify.__all__— including the rest oftermverify._key_v1(KEY_MODIFIERS,KEY_NAMED_BASES,KEY_MODIFIED_BASES) andtermverify._key_encoding_v1.all_key_chords. Importability is not membership;termverify.__all__is.
Compatibility intent¶
The package is pre-1.0 (see the policy in CHANGELOG.md): every 0.x
release may contain breaking Python-API changes, always listed in the
changelog with a migration note, never silent. The stated intent for this
surface is that the top-level names above, and their module paths where the
surface has one, move only with such a documented entry. KEY_NAMES,
is_key_chord, and encode_key_chord are name-only guarantees: the
top-level names carry the intent, their private defining modules carry none
and may move or be renamed without an entry. Protocol artifacts
(termverify.transcript/v1 and its registries) are versioned independently
of the package and are immutable after freeze.
Where to go next¶
- Direct adapter guide — semantics of the in-process path: single-flight discipline, manual-time rules, failure containment, receipt binding.
- JSONL adapter guide — operating the
termverify.control/v1adapter: spawning subjects over pipes, tree containment, honest teardown, and the reference fixture subject. docs/knowledge/protocol.md— the transcript wire contract your recorded runs must satisfy.- Issue #114 tracks the
external subjects' asks, including a future examples directory; GlyphWright's
direct-adapter spike is the current external conformance fixture at
tests/fixtures/external/glyphwright-direct-spike/.