Local MCP integration¶
Install through Styxis plugins¶
Styxis Plugins is the user installation catalog. Installing a plugin in Codex CLI or Claude Code registers its MCP configuration and supplies the pinned runtime command. Requires Git, uv and Python 3.11–3.13; first launch may download dependencies.
Codex CLI¶
codex plugin marketplace add seadonggyun4/styxis-plugin
# Choose Local or Core.
codex plugin add truthound@styxis-plugin
codex plugin add truthound-core@styxis-plugin
Claude Code¶
claude plugin marketplace add seadonggyun4/styxis-plugin
# Choose Local or Core.
claude plugin install truthound@styxis-plugin
claude plugin install truthound-core@styxis-plugin
Profiles and permission setup¶
| Profile | Scope | After installation |
|---|---|---|
| Local | Null/duplicate checks, profiling and sampled PII | Approve read directories with the Truthound setup skill |
| Core | Additional schema, drift and masking workflows | Approve read, workspace, control and sensitive-result grants separately with Core setup, then reconnect |
Installation grants no data access. Updates retain existing permissions. Both profiles install the pinned truthound[mcp]==3.3.4 runtime from PyPI through Styxis; full Core parity through MCP is not verified.
For updates, refresh the catalog, then update only the profiles you already use:
# Codex
codex plugin marketplace upgrade styxis-plugin
codex plugin add truthound@styxis-plugin
codex plugin add truthound-core@styxis-plugin
# Claude Code
claude plugin marketplace update styxis-plugin
claude plugin update truthound@styxis-plugin
claude plugin update truthound-core@styxis-plugin
Restart or reconnect the client after updating. Check the installed versions, tool list and a call using an approved synthetic file; keep existing directory permissions unchanged.
These CLI instructions target Claude Code. Claude Desktop ordinary chat uses a separate .mcpb extension. Keep it installed if you use Desktop chat; installing the Code plugin does not replace it.
Core data quality workflow¶
- Install the plugin for your client using the Codex CLI or Claude Code instructions above, then approve only the data directory you intend to use.
- Choose the profile. Truthound Local exposes
check_file,profile_fileandscan_pii. The Core profile in 3.3.0 also exposes the operations below; the published 3.2.0 package does not include that expanded profile. - Ask for the task and inspect the result.
execution_statusdescribes whether execution succeeded;quality_statusdescribes data quality. A successfully executed check can report failing data quality. Automatically generated schema suites can returnquality_status: unknownwhen complete target accounting is unavailable; inspectdata.success, checks and issues rather than treating unknown as a pass.
| Task / example request | Core operation |
|---|---|
| “Check this CSV for nulls, duplicates and ages outside 0–100.” | validation_check with explicit checks |
| “Summarize column types and quality.” | data_profile |
| “Find likely PII columns.” | privacy_scan |
| “Hash the email column without changing the source file.” | data_mask |
| “Learn this file's schema, then validate against the agreed schema.” | schema_learn; validation_check with schema_spec |
| “Compare this batch against the baseline for drift.” | Capture both inputs as assets, then drift_compare |
Install the Core plugin above and use its setup skill to approve the intended directories. Schema contents and masked rows require the operator's explicit --allow-sensitive-results grant; installation alone does not grant it. Results may be returned as handles: read the associated artifact and release handles after use. Summary-only responses omit raw data by default.
Other existing Core and MCP capabilities remain available. Their configuration and lifecycle details are documented later in this guide; this quick workflow does not enumerate them.
Truthound 3.2 adds an optional Model Context Protocol (MCP) adapter. Its stdio
server runs on your computer and calls the existing th.check(), th.profile()
and th.scan() APIs. It does not need a hosted validation service.
Connection and supported formats¶
Use the plugin installation and setup skill above. No separate virtual environment or hand-written MCP JSON is needed. Local accepts .csv, .parquet, .json, .ndjson and .jsonl. The server uses local stdio, not a hosted HTTP/OAuth connector. After initialization, call tools with a file path inside an approved directory.
Tools and results¶
The three default tools take one path string. Supported suffixes are .csv, .parquet,
.json, .ndjson and .jsonl (the latter two are newline-delimited JSON).
| Tool | Core API | Summary |
|---|---|---|
check_file |
th.check() |
Null and duplicate-row checks, verdicts and issue totals |
profile_file |
th.profile() |
Row/column counts, column names/type families and null/unique percentages |
scan_pii |
th.scan() |
Column, PII category, confidence and estimated finding count |
check_file deliberately selects built-in null/duplicate checks rather than
loading project suites, plugins or arbitrary validator configuration. Profile
dtype is an allowlisted family such as Enum, List, Struct or Datetime;
category values, nested type details and timezone/precision parameters are
omitted. Unknown dtype text becomes Unknown. The Core Python API retains its
full type metadata. A quality
failure is a completed tool call with ok=true and data.success=false.
Execution or policy errors set MCP isError=true and return a stable error code.
Structured responses contain schema_version=1, operation, ok, data,
truncated and error. Errors include access_denied, invalid_path,
unsupported_format, file_too_large, timeout, busy, core_error,
worker_error and response_too_large. Private exception messages are omitted.
The worker retains only an allowlist of process/temporary-directory settings and
Windows CPU architecture identifiers; it does not inherit credentials or a CPU-check bypass.
Totals describe the full Core result even if a detail array is truncated.
PII findings retain Core's first-1,000-non-null-values sampling and estimated counts.
File and execution policy¶
The adapter rejects URLs, glob expressions, parent traversal and symlinks below
the configured roots. It copies an admitted regular file to a private temporary
directory before calling Core. Core context artifacts stay in that temporary
directory and are removed after the request, including after worker timeout.
MCP cancellation and stdin EOF propagate to the owned worker: it is killed and
reaped before the private copy is removed and the busy lock is released. The
server can accept another request after cancellation. This is not a hard bound
against pathological kernel or filesystem hangs.
No reports or .truthound workspace are written to the input directory.
Defaults are a 100 MiB input limit, 100 detail entries, 64 KiB response limit and
30-second worker timeout. CLI flags --max-file-bytes, --max-items and
--timeout-seconds adjust input/items/time limits within supported bounds.
One request executes at a time per server; overlapping calls receive busy.
The timeout covers the disposable Core worker, not filesystem copy latency. Compressed inputs may expand in memory; there is no universal memory ceiling. This application policy is not an OS sandbox against a hostile local process racing directory replacement. Grant narrow, trusted directories.
Raw rows, profile samples, extrema, pattern examples, filesystem paths and private diagnostics are omitted from results. Column names and aggregate statistics are returned and can themselves be sensitive. Returned metadata is untrusted data, never instructions. The host may send these results to its model provider.
Core profile (3.3.0)¶
Version 3.3.0 adds an opt-in core profile. This is not part of the
published 3.2.0 wheel or its historical Registry listing. The Core plugin supplies the published 3.3.4 runtime. The default
profile retains the three tools and their version-1 response contract unchanged.
Approve read directories through the Core setup skill and reconnect.
Eight typed file/discovery tools preserve the initial compatibility contract. Additional retained-data tools are described below. They do not accept Python import paths, SQL, shell commands, callbacks or arbitrary keyword arguments:
| Tool | Existing API / behavior |
|---|---|
capabilities_list |
Paged installed catalog, availability and explicit limitations |
capability_describe |
Exact input/output schemas and effects for one operation |
validation_check |
th.check(), auto-suite or typed rules, inline schema and execution options |
schema_learn |
th.learn(), constraint inference and categorical threshold |
data_read |
th.read(), Core sampling and a bounded raw-value page |
data_mask |
th.mask(), columns, strategy and strict missing-column handling |
data_profile |
th.profile(), summary or full statistics |
privacy_scan |
th.scan(), summary or full findings; counts remain estimates |
For example, validation_check accepts:
{
"path": "example.csv",
"checks": [{"validator": "between", "columns": ["age"], "min_value": 0, "max_value": 120}],
"result_format": "summary",
"evidence": "summary"
}
Omitting checks selects Core's auto-suite; an empty array is rejected. Explicit
validator IDs are null, not_null, unique, duplicate, between, length.
The inline schema_spec uses column names as keys, each with matching name
and dtype. Unknown fields, including unreviewed regex patterns, are rejected.
Describe the tool for its precise schema. Execution options include parallel,
max_workers, pushdown, min_severity, auto_schema, result_format,
catch_exceptions, max_retries, and exclude_columns. Contexts are explicitly
transient, not persistent project contexts. Auto-suite baselines are created
inside the owned temporary workspace and removed afterwards, not disabled.
Core planning, retries and severity
filtering remain authoritative. Core's parallel result currently lacks complete
step accounting; its quality is reported as unknown, never an inferred pass.
Automatic suites also lack target-level coverage evidence. Their quality, and
that of explicit rules with missing, excluded or type-inapplicable targets, is
unknown rather than a pass inferred from an empty issue list. Explicit rules
with confirmed applicable targets can return pass or fail.
Raw pages, learned constraints and evidence="full" require the operator to
restart with --allow-sensitive-results in addition to the profile and directory
grant. Without it, calls return approval_required before accessing the file.
A tool argument such as approved: true cannot grant consent. This permits
sensitive values and full evidence from every admitted file to enter the
host/model context; enable it only for narrow, trusted directories. Masking does
not make unselected columns safe. No source file is overwritten or exported.
New responses separate execution_status from quality_status, with typed
data, issues, coverage, and provenance. Quality violations are not tool
execution errors. Failures, zero checks and incomplete accounting cannot mean
quality pass. Provenance records a session-keyed opaque identity of the actual
staged bytes; the content SHA-256 is disclosed only with operator consent.
Full evidence uses tagged-v1: objects become
{"$type":"object","entries":{...}}; decimals, temporal values, binary and
non-finite floats carry explicit tags. Unknown objects fail, never become reprs.
Integers outside the exact JavaScript integer range are decimal-string tags.
Raw row conversion rejects nanosecond datetime/duration and time columns
(including nested columns) rather than silently losing precision.
Authorized evidence may include values and temporary source/context paths.
Legacy summaries stay redacted even when disclosure is enabled.
data_read and data_mask take offset and limit (default 50, maximum 100,
also bounded by --max-items). Each call stages a new snapshot; compare hashes
before combining pages. Samples and fake masking are nondeterministic: no
continuation offset is provided and nonzero input offsets are rejected. The
retained-data tools below instead provide stable handles. Large inline full results fail with
response_too_large, not a silently truncated full-result claim. All existing
file, timeout, concurrency and memory limitations still apply.
Implementation boundaries and remaining work¶
Full Core-to-MCP parity is not established. Existing bindings and scoped tests do not certify every native configuration, error or restart path.
| Area | Current boundary / remaining work |
|---|---|
| Checkpoint configuration lifecycle | Signed save/restore, approved discovery/deletion and persistent native age/count/size retention on save/list or explicit apply. Idle background cleanup is not provided. |
| Native realtime validation | Broker consume/ack/window/processing exists. Approved factories connect realtime.StreamingValidator batch validation, statistics and reset. Approved sync/async generator sessions, cursor reads and closure are connected. |
| Streaming validation pipelines | Registered streaming validators and local file cursors exist. StreamingValidationPipeline.validate_source and stream_validate_many orchestration are exposed through approved sessions. |
| Arrow Flight streams | Native ArrowFlightStreamingSource is accepted through approved BrokerRuntime.file_source; streams_open remains local-file only. |
| Physical tenant SQL isolation | The verified physical-source wrapper accepts file-backed SQLite/DuckDB, PostgreSQL ordinary tables, MySQL InnoDB tables and ordinary Oracle PDB tables. SQL Server is implemented with native x86 verification pending; other SQL providers remain unimplemented by this wrapper. |
| Stores, execution, profiler, reporting and extensions | Existing native service bindings are present; exhaustive configuration/result/error/persistence/restart equivalence remains unverified. Arbitrary code, credentials and paths must still come from operator bindings. |
| External services | Real cloud, external brokers, paid AI and external notification targets remain unverified; this is distinct from missing local adapter code. |
The full native Checkpoint runner/escalation durable recovery implementation is present; older descriptions that call all durable service recovery unimplemented are obsolete. Signed configuration snapshots and runtime recovery journals are separate resources. This table does not claim that the remaining work is exhaustive.
Retained data, constructors and artifacts¶
The same opt-in profile also exposes these named operations:
| Tools | Core behavior |
|---|---|
validators_list, validators_describe |
Concrete registry inventory, inherited constructor fields, rich-input grammar and pinned source AST identity |
assets_capture, assets_inline |
Immutable file read or typed in-memory Arrow asset |
assets_sqlite |
Core SQLite table materialization from a private transaction-consistent backup |
sources_open_sqlite |
Actual resident native SQLite datasource and SQL engine over a private quota-accounted backup |
sources_open_duckdb |
Native DuckDB SQL datasource on a bounded private WAL-free copy; read-only physical tables with external access/extensions disabled |
sources_open_binding |
Explicitly approved installed-plugin factory returning a resident native synchronous datasource; configuration stays operator-side |
streams_open, streams_next, streams_inspect |
Native CSV/JSONL/Parquet/Arrow file-stream cursors, complete retained batches and native read state |
sources_open_polars, sources_inspect, sources_sample |
Owned resident native Polars datasource, complete native config/metadata and Core sampling into a retained asset |
sources_validate_connection, sources_validate_columns, sources_check_size_limits, sources_validate |
Actual connection/column/size checks and validation through Core's native source= execution path |
assets_validate |
Core validation with closed constructor settings, retained schema and reference assets; canonical result artifact |
suites_create, suites_inspect, suites_validate |
Native kernel suites with stable check IDs, schema handles, tags, metadata and evidence/severity policies |
assets_page, assets_sample, assets_mask |
Stable lossless pages, seeded Core sampling and masking |
assets_profile, assets_scan |
Complete Core profile and PII reports retained privately |
schemas_learn, schemas_create, schemas_inspect, schemas_export |
Learned or explicit typed schemas, authorized inspection and owned JSON/native YAML export |
profilers_catalog, profilers_profile, profilers_generate_rules |
Advanced native profiler configuration, analysis components and profile-based rule generation |
artifacts_export, artifacts_read |
Owned IPC/Parquet/CSV/JSON/NDJSON files and bounded byte reads |
artifacts_render, artifacts_datadocs |
Canonical validation re-entry, JSON/Markdown/console/HTML reporters and English/Korean validation Data Docs |
resources_describe, resources_release |
Session ownership, expiry, integrity, current grants and descendant cleanup |
contexts_open, contexts_inspect, contexts_initialize, plans_status |
Separately granted workspace configuration, approved initialization and intent status |
contexts_execute |
Explicit approved native source tracking, baselines, applied suites, result/docs persistence, metrics and workspace listing |
drift_compare |
Complete Core dataset-comparison report with all method choices and parameters |
drift_catalog, drift_detect |
Full constructor settings of 13 concrete native drift detectors on two owned Series columns |
openlineage_services_open, openlineage_services_execute |
Approved pinned native emitter, run transitions, graph emission, uncertain delivery resolution and shutdown |
lineage_catalog, lineage_create, lineage_edit, lineage_query |
Typed native graph configuration and immutable graph edits/traversals |
lineage_track |
Native manual source, transformation, validation and output tracking into a derived graph |
lineage_load, lineage_export |
Approved local native JSON snapshot re-entry and Core JSON artifacts |
lineage_render |
Native whole/subgraph D3/Cytoscape JSON, Graphviz DOT or Mermaid text with full closed rendering configuration |
lineage_impact, lineage_critical_paths, lineage_what_if_delete, lineage_schema_impact, lineage_dependency_chain |
Native impact and dependency analysis retained as artifacts |
models_catalog, models_create, models_inspect |
Closed configurations for the 12 concrete Core ML algorithms and resident lifecycle state |
models_fit, models_partial_fit, models_fit_predict, models_predict, models_score |
Native training, supported online training, combined training/prediction, prediction and anomaly scoring |
models_detect, models_learn_rules, models_rules, models_diagnostics |
Two-input ML drift detection, rule learning and native model diagnostics as owned artifacts |
models_rule_query |
Native learned-rule filtering and suite/validator configuration conversion; no automatic application |
models_result_query |
Native prediction score iteration, anomaly filtering and threshold-based drift column queries |
models_ensemble_add, models_ensemble_remove |
Revision-checked native editing; detached children remain restorable artifacts |
models_persist, models_resume |
Separately approved private model snapshots and new-session restoration |
jobs_submit, jobs_execute, jobs_status, jobs_result, jobs_cancel, jobs_recover |
Approved durable validation occurrences, retained results and indeterminate lease recovery |
schedules_register, schedules_status, schedules_disable |
Approved persistent cron recurrence and lifecycle, executed by the local foreground worker |
storage_policy_register, storage_policy_status, storage_policy_disable |
Approved persistent automatic age/count retention policies |
storage_retention_preview, storage_delete |
Native age/count/size and ALL/ANY retention preview; separately approved exact payload deletion |
schedules_preview |
Native cron validation, matching and bounded next-time preview; no job registration |
models_save, models_restore |
Native trained-state JSON artifacts and new owned model workers restored from them |
schema_audit_contract, schema_audit_create, schema_audit_inspect |
Strict native metadata documents, complete JSON Schema and canonical digests |
schema_audit_standards, schema_audit_implementation |
Native standards/design/catalog audits with complete findings and explicit unknown coverage |
Explicit release and expiry close and reap owned native readers before deleting their snapshots, including SQLite connections and mapped Parquet/Arrow streams on Windows. Cleanup needs session ownership, not a renewed data-read grant. A failed file deletion is reported as a failure and remains tracked for cleanup.
Capture returns data.resource_id. Pass it as asset_id to retained-data tools.
Kernel suites preserve actual check-instance identity even for repeated registry
algorithms and identical display names. Check metadata reserves
_truthound_check_id and _truthound_execution_incomplete; user metadata cannot
override these runtime fields. Suite evidence policy is applied to the executed
validator, not only the result's format label, without mutating the factory's
retained object. Severity filtering cannot turn an execution failure or
dependency skip into a passed check. SQL fallback failures and missing/mismatched
query results are preserved, never interpreted as an empty successful result.
Canonical re-entry retains structured detail, failed-row records and captured
exception/retry evidence. Native row-record serialization does not carry the
original physical dataframe dtype schema; use retained typed assets for that.
Approved native datasource factories¶
sources_open_binding takes only binding_id and idempotency_key. It requires
the Core profile, sensitive-result permission and a private control directory.
An operator installs and pins a plugin using the installed-extension binding
contract below, exposes a zero-argument factory as a capability, and adds this
mapping to private bindings.json (never a repository or release artifact):
This is a fragment to merge with the existing plugins configuration, not a
complete binding file. Exactly one pinned plugin must expose orders-factory.
The factory must return a Core BaseDataSource or AsyncBaseDataSource with
name="orders".
For example, a reviewed plugin can return SQLiteDataSource(table="orders",
database=operator_selected_path, config=SQLiteDataSourceConfig(name="orders")).
Use the stable register_capability plugin port to register the factory.
Provider dependencies and credential references are configured by the operator;
MCP does not install plugins, accept connection strings, forward arbitrary
environment variables, or accept import paths/settings in the tool request.
Do not put credentials into plugin settings, names, metadata, data columns or logs.
The first call returns an approval plan without importing the plugin. Approve the exact plan using the existing operator CLI, then repeat the request. Approval covers trusted extension execution, native connection, reads and cleanup for that owned session handle. This is not an OS sandbox or a read-only SQL guarantee; review the factory and use least-privileged database credentials. Remote providers and paid services have not been validated by local SQLite evidence.
The returned source works with sources_inspect, sources_validate_connection,
sources_validate_columns, sources_check_size_limits, sources_sample,
sources_sample_source and
sources_validate. Validation passes the actual resident source to Core's
source= path; it does not replace the provider with a captured DataFrame.
Inspection deliberately returns config=null for bound sources to avoid exposing
connection configuration. Native data freshness/caching and engine behavior remain
the provider's semantics; this is not an immutable snapshot or a reconnect service.
Validation leaves input_sha256 and input_identity unknown for a live binding;
the source handle's resource hash describes its binding label, not database rows.
Async sources use Core's AsyncToSyncAdapter on an owned event loop in the
resident worker. It preserves native size limits and schema helpers, connects
sampled async sources on that loop, and closes each connection before the loop.
Failed entry releases partial connections; no automatic reconnection/retry is
added. Local async-source lifecycle and canonical-result comparison are tested;
external MongoDB/Elasticsearch and other remote providers remain unverified.
Arbitrary protocol objects are not accepted by the binding factory contract.
The same root or sampled source also accepts sources_read, sources_profile,
sources_scan, sources_mask and sources_learn. These call Core's public
read(source, sample_size=...), profile(source=...), scan(source=...),
mask(source=...) and learn(source=...) directly, preserving the provider's
materialization limits and sampling behavior. Read uses Core's fixed sampling
seed of 42; sample_size is optional and bounded to 1–10,000,000. Profile and scan
accept only the source handle. Mask accepts optional columns, native redact,
hash or fake strategy, and strict (default false). Learn accepts
infer_constraints (default true) and categorical_threshold (default 20,
0–10,000).
Read and mask retain immutable Arrow assets for paging and downstream work;
fake values are generated once per call. They leave the resident source unchanged.
Profile and PII reports retain their native JSON representation and public source
name. Learned schema handles can enter schemas_inspect, schemas_export
and sources_validate(schema_id=...). Reports and schemas can contain data
values; use the existing sensitive-results policy. Every result remains dependent
on its source's grants, expiry and lifetime. Output quota or native operation
failure does not publish partial output or discard a healthy source connection.
PII scanning retains Core's sampled detection behavior; it does not prove that
all sensitive values were found.
sources_sample keeps its Arrow-snapshot contract. sources_sample_source
accepts the same source_id, n and optional seed, and returns a source
handle for the actual native sampled datasource in the existing worker. It does
not re-create the provider from the factory or replace it with an Arrow frame.
The sampled source supports inspect, connection/column/size checks, validation,
Arrow capture and further native sampling. At most 16 child handles are retained
per worker. Native sampling and caching semantics still apply; a native provider
may return the same object rather than a new sample.
Children borrow the owner's connection/event-loop lifetime. Child release closes its native context when its final alias is released, and leaves the root and siblings alive. Parent release revokes descendants and closes their native contexts before the root/provider and plugin teardown. As with root close, bounded cleanup failure kills the owning worker; provider-side cleanup is not guaranteed. Derived live samples have unknown input content hashes, and every access reauthorizes parent grants. No extra SQL/Python, provider path, credential or callback can be supplied to sample a live source. Local Polars and actual async-source tests cover nested samples, validation, Arrow capture, shared-loop cleanup and capacity reuse.
Repeated successful opens with the same key return the same live handle, never a
new connection. Factory failures/cancellation after execution begins are recorded
as outcome_unknown and cannot be blindly retried with that key. Released or expired
handles cannot be resurrected by replay. Release and session shutdown close the
source before plugin teardown; timeout/failed bounded cleanup may require killing
the owned worker and cannot guarantee provider-level cleanup. Binding changes,
package identity changes and policy changes revoke the source and derived artifacts
when next checked; TTL bounds idle workers. No monitoring process is started.
sources_open_polars takes an owned asset_id and optional complete native
PolarsDataSourceConfig under config; it returns a source resource ID.
Use that ID as source_id. sources_validate accepts the same validation
settings as assets_validate, except that callers cannot substitute asset_id.
It passes the actual resident datasource to Core, not a DataFrame stand-in.
Inspection/check results are retained artifacts, read only with disclosure
consent. A negative connection result remains connected=false; native size
gate exceptions fail the operation. Sampling returns an owned Arrow asset, not a
new resident connection. Releasing the input asset/source revokes descendants and
closes the owned worker. Normal shutdown permits native context-manager cleanup;
on timeout/EOF/protocol failure only that owned process is invalidated/reaped.
The source handle hash identifies configuration, not a separate dataset snapshot.
sources_open_sqlite takes path, a literal table name and optional config.
It opens an actual SQLiteDataSource over a transaction-consistent private
backup, not a materialized DataFrame. SQL/base config fields are forwarded;
the native database field is bound to that backup, never a caller-selected
URI. The backup counts against file/session quotas and its hash identifies the
database bytes. Only rollback-journal databases and real tables are admitted:
WAL, views and model-supplied SQL are rejected. Releasing a SQLite source also
releases its owned backup. assets_sqlite remains the separate materialization
operation. This does not establish other SQL/cloud/NoSQL/async provider parity.
Native config is not an OS sandbox.
sources_open_duckdb takes path, a literal physical table, and optional
config with the native base/SQL/DuckDB fields except database and read_only:
the private copy and read-only mode are fixed. Operator-managed environments
can use the published truthound[mcp,duckdb]==3.3.4 runtime. Stable DuckDB 1.4 or
newer, below 2, is required. Older/prerelease versions return
unsupported_dependency. Native SQL-engine dispatch and sampling are retained,
not replaced by an in-memory dataframe. WAL-bearing files, views and arbitrary
SQL are not admitted; a live-copy stamp/WAL change rejects publication.
Connections disable external reads, extension auto-install/load, community and
unsigned extensions, then lock configuration following the
DuckDB security guidance.
Threads default to 1 (explicit maximum 16); memory defaults to max_memory_mb
in MB unless memory_limit is supplied. These are connection controls, not an
OS sandbox or a complete process-memory bound. unsupported_table, malformed
database, quota and cancellation failures publish no usable source. Explicit
release closes the worker and deletes its private copy, never the original.
streams_open takes a path and a closed format-specific source:
{"path":"/approved/data.csv","source":{"format":"csv","config":{"chunk_size":1000,"columns":["age"],"skip_rows":0,"max_rows":10000,"separator":",","has_header":true}}}
Formats are csv, jsonl, parquet and arrow. All take chunk_size,
columns, skip_rows and max_rows; Parquet/Arrow also take use_mmap, CSV
also takes separator and has_header. CSV skip_rows skips physical lines
before the header, exactly as the native source does. Arrow requires PyArrow.
The returned resource ID is stream_id for streams_inspect and
streams_next; each advance requires an idempotency_key. A batch returns
{artifact: <Arrow asset metadata>, exhausted: false}; EOF returns
{artifact: null, exhausted: true}. Feed the batch resource ID to
assets_validate or other retained-data tools. This is per-batch validation,
not global uniqueness, cross-batch windows, Kafka offsets or continuous-stream
validation. Native chunk boundaries are retained rather than silently reshaped.
Retries of the same key replay the same retained batch, never another advance.
Receipts are bounded by max_items per session. These cursors/receipts are not
durable across server restart. An uncertain advance invalidates the worker and
must not be retried against a guessed position. Releasing the stream releases
its owned input snapshot and revokes batches. This local file-stream support
does not establish Kafka/Kinesis/PubSub account or realtime parity.
For example, after capture, call assets_validate with:
{
"asset_id": "asset_<returned-session-token>",
"checks": [{"validator": "between", "settings": {"columns": ["age"], "min_value": 0, "max_value": 120}}]
}
Use actual returned identifiers, not the placeholder. validators_describe
documents the closed fields for each of the 260 concrete registered entries,
including inherited ValidatorConfig, cache and algorithm options. Unknown
settings are rejected instead of being silently dropped by the Core constructor.
Numeric constructor fields retain finite Python integers without coercion to
float, including integers outside floating-point range. Native constructor or
execution errors remain errors; NaN, infinity and booleans are not accepted as
numeric settings. This does not increase resource or response budgets.
The inventory includes six native streaming validators (streaming_null,
streaming_completeness, streaming_nan, streaming_range,
streaming_positive, streaming_outlier), with their native chunk_size option.
That inventory is not evidence that all 260 algorithms have passed parity.
Constructor rejection, native execution failure and an exception deliberately
caught by an algorithm are separate coverage categories. An invalid constructor
returns validator_configuration_error without raw settings; after approved
trusted code has begun, failures instead preserve outcome_unknown and prohibit
automatic replay. Missing optional libraries remain failures, not successful
quality verdicts. Polygon operations require truthound[geospatial,mcp].
column_consistency.rules accepts only when, then and optional description;
the two conditions use the closed Polars expression grammar below, not Python.
Core corrections in 3.3.0 also change observable algorithm behavior:
numeric PSI/CSI and PSI-based feature drift include observations outside the
reference histogram range rather than discarding them. Cached and uncached PSI
paths use the same tail-accounting rule; thresholds are unchanged.
Constant reference distributions retain their point mass rather than becoming
empty histograms. Default reference caches are instance-scoped; cross-instance
reuse requires an explicit versioned cache_key for the same baseline.
validators.memory.SGDOneClassSVM, used by online_svm, now delegates to the
native unsupervised scikit-learn SGD one-class estimator, not a binary classifier
trained with synthetic negative labels. Its scaler and kernel projection are
fitted on the first batch and remain fixed while subsequent batches update the
estimator. Predictions from the earlier binary surrogate are not equivalent.
This composition follows the scikit-learn one-class SGD guide.
These changes belong to 3.3.0; already published 3.2.0 files are unchanged.
The Core JSON-schema validator uses Draft 7 when its optional library is present.
Untrusted schema settings never fetch external references. The transport follows
local pointers/anchors, including references into otherwise literal JSON data,
and checks the reached schemas too. Reference analysis has a separate bounded
depth/node budget; exceeding it fails rather than silently truncating validation.
General property names such as id remain ordinary data. Executable regex or
external-reference schemas require a reviewed operator binding.
Untrusted SQL, callbacks, caller filesystem paths and unrestricted regex return
operator_configuration_required. Exact operator-owned validator templates and
installed native extension bindings are available through the separate approval
boundary below; they do not make model-supplied executable inputs trusted.
Inline JSON Schema may use local # references, not filesystem/network $ref.
This adapter does not silently replace these unsupported settings.
A DataFrame/LazyFrame constructor parameter takes {"asset_id":"<returned-id>"};
maps of reference tables contain such references as values. Every reference is
separately authorized and retained in the output's parent chain. Non-string-key
maps use {"entries":[{"key":1,"value":0.5}]} to preserve key types.
Datetime/date settings use {"value":"2026-01-01T00:00:00"} or an ISO date;
timedelta settings use {"value":1000000} (integer microseconds). Type settings
use the same closed DType schema as assets_inline.
Polars expression settings use a bounded AST, e.g.
{"op":"gt","left":{"op":"column","name":"age"},"right":{"op":"literal","value":120}}.
Supported nodes are column/literal, arithmetic, comparisons, Boolean operators,
null tests, absolute value and sum/mean/min/max/count/unique-count aggregates.
Python expressions, SQL and serialized expressions are not accepted.
File capture exposes all FileDataSourceConfig fields through options;
options.sample_size is source configuration, while the top-level sample_size
is the independent th.read() sample. .pq is also accepted. SQLite requires an
admitted .db, .sqlite or .sqlite3 and a real table name, not SQL or a view.
It bounds backup size and Core materialization, closes connections and never
executes caller SQL. WAL databases are rejected: SQLite read-only WAL access may
still modify shared-memory bookkeeping. Other SQL/cloud providers and their
credentials are not implied by this local binding.
Handles are private to one server session. Defaults are 128 handles, 1 GiB total
artifact bytes and 900 seconds TTL; configure --max-handles,
--max-session-bytes and --handle-ttl-seconds. Derivatives cannot outlive their
parents. Release invalidates descendants. Each access rechecks grants and bytes;
EOF closes the session and deletes owned files. These are not durable cursors.
assets_page preserves nested nanosecond temporal values via physical-value tags.
Untrusted inline datatypes are limited to 16 nesting levels and 524,288 fixed
scalar slots per value. Before native allocation, scalar slots across all rows
and actual nested list/struct elements × 16 bytes must fit the remaining session
byte budget. This conservative admission check
prevents tiny nested-array/null declarations from requesting enormous buffers;
it is not an OS memory sandbox or a change to Core's data algorithms.
artifacts_read returns base64 byte pages (maximum 32 KiB), SHA-256 and a next
offset. Raw pages, schema inspection and byte reads require
--allow-sensitive-results. Artifact creation alone does not disclose its data.
Renderers never accept output/template paths, launch a dashboard or open a browser.
Rendered HTML remains untrusted content; host/browser handling is separate.
schemas_create accepts schema_spec with the same closed column constraints
and statistics as inline validation. schemas_export takes resource_id and
format=json|yaml (default YAML), producing an owned artifact, not writing an
agent-selected path. YAML uses Core Schema.save() and can be re-entered with
Core Schema.load(). Arbitrary regex is not an inline schema input; use a
reviewed native validator binding for executable schema patterns.
profilers_catalog describes ProfilerConfig and the closed built-in component
constructors. profilers_profile takes asset_id, config, options, optional
column_analyzers, table_analyzers, type_inferrers, and table name/source
labels. Component lists contain {"component":"CorrelationAnalyzer","settings":{"threshold":0.8}}
(in this case a table analyzer). It exposes six column analyzers, three table
analyzers and three type inferrers. config and inherited profiler options
are explicit Core inputs. config.sample_size controls the leading-row sample;
a supplied options.sample_size overrides it without mutating the provided config.
The effective config also controls default correlation, pattern and distribution
analysis. This path does not use random_seed to select a random sample.
Unknown fields, arbitrary component imports and custom regex
pattern definitions are rejected. Custom extension sessions and suite execution use the separately approved
profiler_services_* lifecycle below. Streaming/incremental/distributed
profilers require their own contracts.
Advanced native profiling retains component failures in execution_issues
(stage, component, exception type and optional column), excluding exception bodies.
A column/table profile's complete property reports whether the selected analysis
finished; it does not mean every input row was analyzed when sampling is configured.
Incomplete profiles serialize complete: false and their diagnostics, and both
native save/load and dictionary adaptation preserve that verdict. MCP returns
execution_status: failed with native_profile_incomplete and an owned partial
profile artifact. Read it under sensitive-result consent; source authorization
still applies. Rule generation stops before invoking any generator when profiling
is incomplete. The native suite generator also raises on a selected generator's
failure instead of returning a successful partial suite.
profilers_generate_rules accepts the same profile inputs plus a required
generator, for example {"component":"SchemaRuleGenerator","settings":{}},
and strictness (loose, medium, or strict, default medium). The closed
catalog also includes StatsRuleGenerator, PatternRuleGenerator and
MLRuleGenerator. One selected native generator receives the fresh native
profile; its complete GeneratedRule.to_dict() results are retained as an
owned artifact. Generation does not execute validators or apply a suite.
Set representation to validator_configs to retain a list of
{"validator_class": "...", "config": {...}} records instead. Each config comes
directly from native GeneratedRule.to_validator_config(), including columns
and mostly; it is not a configuration inferred from a summary. The default
rules representation remains unchanged. Export does not instantiate, approve
or execute a generated validator; later use must pass the normal configuration
and authorization checks.
Omit column (or pass null) for table-wide generation. To use the native
column-only algorithm, pass a literal column name such as "column":"amount".
This calls generate_for_column() on that column's fresh profile, not a filter
over table-wide rules; table-level correlation rules are therefore not added.
An absent column fails with core_error and does not produce a partial artifact.
Exceptions fail the operation rather than silently returning partial rules.
Artifact reads require sensitive-result permission and retain the source asset's
authorization; releasing that asset also prevents reading its rule artifact.
Approved custom profiler sessions¶
profiler_services_open and profiler_services_execute retain custom native
profiler compositions in one approved worker. An installed capability factory
is an async context manager yielding truthound.mcp.profiler_services.ProfilerRuntime.
The runtime selects profilers (native Profiler instances), generators
(ValidationSuiteGenerator instances with custom rule generators), executors
(SuiteExecutor instances with the operator's registry/listeners), and optional
profile_slots/suite_slots mapping fixed aliases to absolute Path objects.
The factory owns all configuration, custom patterns/analyzers/inferrers, physical
paths and cleanup. Add the capability to private plugins bindings and map
"profiler_services":{"local":"profiler-factory"} in bindings.json.
No import string, callback, regex definition, validator class or path is accepted
from the model. This is trusted installed code, subject to exact operation approval.
After opening, pass the returned resource_id as service_id with binding_id
and a unique idempotency_key. Commands are:
| Commands | Native effect |
|---|---|
inspect |
Bounded composition, slot and retained-result aliases |
profile (profiler_id, asset_id) |
Run the selected native composition; retain its exact TableProfile |
profile_get, profile_export (profile_id) |
Read the native profile or export a re-enterable advanced-profile artifact |
profile_import (artifact_id) |
Decode an owned advanced-profile artifact and restore through native ProfileSerializer |
profile_save, profile_restore (slot_id) |
Save a retained profile_id or restore one from an operator-selected slot |
generate_suite (profile_id, generator_id) |
Run the selected native suite generator with strictness, optional category/confidence filters and name |
suite_get, suite_export (suite_id) |
Read/export the exact native generated suite |
suite_save, suite_restore (slot_id) |
Save a retained suite_id or load native JSON from a fixed slot |
suite_execute (suite_id, executor_id, asset_id) |
Execute with optional parallel, fail_fast and dry_run |
profile_release, suite_release |
Release a worker-local result alias |
slot_delete (slot_kind, slot_id) |
Explicitly delete the selected persisted profile/suite |
Each owner retains at most 16 profiles and 16 suites within the session byte budget. JSON save uses Core serialization followed by replacement of the fixed slot; it is not a transactional database or a power-loss durability guarantee. Slots contain results, while composition configuration stays in the pinned factory. Restoring a fixed slot in a new owner requires a fresh approval and explicitly authorizes that stored data independently of a prior session's input. Artifact import keeps the source artifact's authorization dependencies. Import into a separate owner: self/descendant dependency cycles are rejected before approval. No arbitrary suite artifact import is provided: suite loading uses operator-owned slots because rule-to-validator conversion can execute trusted code.
Incomplete profiles retain diagnostics, return native_profile_incomplete and
cannot generate suites. Failed profile responses are idempotent and do not
re-execute analyzers on replay. Missing validator conversions, runtime errors or
skipped rules produce a failed native_suite_incomplete artifact rather than
successful empty execution. A completed validation may still have success: false
when data violates rules. Dry-run success proves construction only, not validation.
Native reports retain error types rather than exception bodies. Sync/async
executors use the supplied native registry and the same failure accounting.
Native components, generators and validators check authorization before their
next callback; the owner also watches policy changes. Release, expiry, timeout,
revocation and session close reclaim the owner and factory resources. Incremental and chunked native profilers use the same retained profile/suite
lifecycle. The factory may provide incrementals (IncrementalProfiler),
streaming (StreamingProfiler), chunk_factories (async context managers
returning native ChunkIterator objects) and stream_progress callback aliases.
incremental_profile takes incremental_id, asset_id, optional
previous_profile_id and force_refresh; it returns native profiled/skipped
columns and change reasons beside the retained profile. stream_profile takes
streaming_id, stream_id and optional progress_id. It reads the operator's
chunk source directly, closes its iterator and source context on success/failure,
and never asks the model for a source path or callback.
Incremental profiles persist a per-column content_fingerprint with the native
modulo sampling configuration/hash. Equal row/null counts alone do not permit
reuse; missing fingerprints and incomplete columns are re-profiled. The sample
can miss changes outside selected rows and is not a full-data integrity check.
Chunk errors remain incomplete with current-run diagnostics, while an inconsistent
chunk schema raises and closes the source. Chunked analysis uses native streaming
statistics, not every advanced DataProfiler analyzer. Distinct-value tracking can
grow with cardinality; chunking alone is not a constant-memory guarantee.
Distributed profiler lifecycle remains a separate contract.
Native profile caches can also be selected by the factory: caches maps aliases
to ProfileCache instances, cache_keys maps aliases to native cache-key objects,
and cache_patterns maps aliases to fixed logical glob patterns. Commands
cache_get, cache_set and cache_get_or_compute require cache_id and
cache_key_id; set additionally takes profile_id, while compute takes
profiler_id and asset_id. Optional ttl_seconds includes an immediate-expiry
value of 0; omission uses the native cache default. cache_invalidate removes
one selected key, cache_invalidate_pattern uses pattern_id, cache_clear
clears the selected cache and cache_stats returns selected counters. The
operator must namespace keys by dataset, configuration and implementation version;
this binding does not invent a new cache-key algorithm.
Cache hits retain the actual native profile and incomplete hits remain failed
artifacts. File caches serialize to a private temporary file before replacement;
corruption or access-stat write failure preserves committed bytes and raises
ProfileCacheReadError/ProfileCacheWriteError. The MCP intent becomes
outcome_unknown after a worker error and is not automatically replayed. Repair
uses a fresh approved owner and explicit invalidation. File pattern invalidation
validates all logical key/file mappings before deleting anything; older entries
without stored keys must first be read by their known key or explicitly cleared.
Unsupported custom backends raise instead of pretending to invalidate zero keys.
Redis parsing/connection errors retain corrupt values and report safe error types;
failed construction and close attempt all owned client/pool cleanup. Redis
namespace scans escape the physical prefix and recheck each logical match.
cache_stats excludes physical keys, paths, endpoints, raw metadata and exception
bodies. connected is native observed state, not a fresh health check; unavailable
native key counts return native_cache_incomplete. Actual local file persistence
and synthetic Redis protocol failures are covered; a real Redis service remains
unverified. These changes do not promise a transactional cache or power-loss
recovery.
Generated-suite execution also accepts timeout_seconds (positive, finite,
at most 3600) and max_workers (1–32). Omission preserves the selected executor's
configured deadline/worker defaults; the whole MCP operation still uses the server
policy timeout. A per-validator deadline uses native ProcessExecutionStrategy,
with importable/picklable validator state and a serializable binding guard checked
before validation. Serialization failures are explicit failures, never a fallback
to an uninterruptible thread. The native deadline includes child startup and result
waiting; parent-side serialization and final cleanup can add overhead. The child
owns a separate inherited process group, private result pipe and parent-lifetime
lease. Timeout, normal completion and parent loss close that execution scope.
Errors cross the native pipe by type without original text/tracebacks, and result
serialization failure is an error rather than a fabricated timeout.
The generic native thread strategy can time out while its daemon still runs:
was_terminated stays false, outcome_unknown is true and automatic timeout
retries stop. It is not used for generated-suite validator deadlines. Parallel
fail-fast cancels queued work and drains work already running; skipped work is
retained in the failure accounting. Native rule start/completion listeners are
serialized, and their failures remain visible. These lifecycle controls are not
an OS sandbox for installed extension code.
Approved continuous schema monitoring¶
schema_monitor_open runs native SchemaWatcher or AsyncSchemaWatcher in a
session-owned process. It accepts binding_id, mode (sync default or async),
poll_interval (0.1–3,600 seconds, default 60), max_events (1–1,000, default 100,
also bounded by operator max_items) and idempotency_key. Core determines
changes, versions and breaking alerts; this adapter does not implement a second
schema comparison algorithm.
The operator privately configures bindings.json, alongside installed plugin
distribution/version/entrypoint/SHA-256 pins:
{"schema_monitors":{"orders-watch":{"source":"orders-schema-factory","notifier":"orders-alert-factory"}}}
Both names must be exposed by the pinned plugins' capabilities maps. Each is a
zero-argument factory: the source returns Core's SchemaSource protocol (for
example FileSchemaSource); the optional notifier returns AlertNotifier with
send(alert) -> bool. A native CallbackNotifier can adapt an operator-owned
HTTP/email implementation. There is no built-in hosted endpoint, model-selected
URL, import string or credential input. Destination, credentials and connection
policy remain operator-side. Installed plugins are trusted code, not sandboxed.
Alerts contain schema details and source identifiers, which may include local
paths; approving sending authorizes disclosure to that fixed destination.
{"binding_id":"orders-watch","mode":"async","poll_interval":60,"max_events":100,"idempotency_key":"orders-watch-01"}
Opening first returns an approval plan for installed extension execution,
continuous schema reads and, when configured, external schema alert sending.
Use the existing operator approval flow, then repeat the exact request. No plugin
imports, reads or sends occur before approval. The returned resource_id is the
monitor_id for schema_monitor_status and schema_monitor_stop. Both return
an artifact resource; read it with artifacts_read for state, error, poll/event
counts, successful send count, native versions and alerts. Initial registration
is a baseline; only breaking changes send alerts. Sync polling begins after the
interval; the native async poller checks immediately after starting.
Monitoring continues without further tool calls, until stopped, released, expired
or the session closes. Stop joins the native poller and shuts down the plugin;
release/expiry/timeout force-reap only this owned worker. Session termination is
not a graceful delivery guarantee. Binding and package identities are rechecked
before each poll and send; a parent-side supervisor also reaps the worker on
operator policy changes without waiting for another tool call (0.1-second checks).
A failed source, rejected/unknown send, revoked binding or exhausted event/byte
budget stops further effects and exposes state="failed", never healthy status.
The status read itself can succeed while the reported monitor is failed. No
automatic notification retry is attempted: a network error may follow a remote
effect. Bytes are bounded by the smaller operator file/session limit and 16 MiB.
Replaying an approved open returns its existing handle, not a replacement worker.
This monitoring history is private in-memory state, separate from
schema_history_* checkpoints. There is no autonomous cross-session restart,
durable delivery queue, exactly-once delivery, monitor pause/resume or transfer of
source execution authority through these session-owned operations. Local real HTTP transport and sync/async native parity
are tested; actual cloud, email and broker services remain unverified.
Durable schema recovery and notification outbox¶
For separately approved restartable monitoring, call schema_watch_register
with the same binding/mode/interval/event settings plus lifetime_seconds
(1–2,592,000 seconds, default 86,400) and a new idempotency_key. Review and
approve the returned plan using the operator CLI, then repeat the exact request.
This consent explicitly covers persistent plugin execution, automatic restart
reads and, when a notifier is configured, persistent external notification delivery.
Registration does not load plugins or poll. The returned watch_id is durable,
not a session handle. There is no model-selected URL or credential.
Run the existing python -m truthound.mcp worker with the same Core profile,
sensitive-result consent, allowed directories, private control directory and
limits as registration. --once performs one bounded sweep; otherwise the
foreground worker continues until stopped. Restarting that worker recovers
registered watches without another MCP session or approval, until the approved
expiry, disablement, policy/binding change or failure. Nothing installs an OS
service or starts itself at boot. Each sweep performs at most one queued send
or due native poll per watch; queued delivery takes priority, so the effective
poll interval also depends on the worker interval and backlog.
Native sync/async check_now, version serialization and breaking-alert creation
are reused. A signed SQLite transaction commits the last schema/checksum,
native history/counters and newly queued alerts together. Internal schema state
preserves closed rich values losslessly and native Polars dtype metadata through
empty Arrow IPC; unsupported or lossy values fail instead of being stringified.
Native history/alert presentation still uses Core serialization. Earlier
unversioned development checkpoints fail closed and retain their evidence; they
require a new approved registration rather than an implicit migration.
Changes while the worker is stopped are
compared against the last committed baseline when it resumes; intermediate
changes between polls cannot be reconstructed. This is separate from the
schema_history_* checkpoint API.
The outbox uses pending → sending → sent | unknown. A send is marked durably
before calling the pinned native notifier. Only an explicit True response
counts as sent. Exceptions, false responses or a crashed sender leave unknown;
these entries are never automatically retried. A dead poll owner can be recovered,
but concurrent workers cannot claim a live owner's work. The isolated child exits
when its parent's pipe closes, including while a plugin is blocked. This is at most one
automatic attempt, not guaranteed delivery or exactly-once remote effects.
Operator-side notifiers must not hide their own retries. Unknown outcomes need
destination-side reconciliation; no automated reconciliation is provided.
schema_watch_status and schema_watch_disable accept watch_id and optional
include_snapshot: true. Status reports state, expiry, poll and delivery counts;
the optional artifact exposes sensitive native history and outbox payloads via
artifacts_read. Disable prevents future claims but cannot undo an in-flight
send. Evidence is retained. Storage is bounded to 32 definitions and at most
1 MiB per definition (also bounded by operator file/session limits); event limits
remain enforced. Exhaustion fails closed, not by silently dropping notifications.
Real loopback HTTP, separate-process restart and crash-after-send are tested;
cloud/email/broker accounts remain unverified. These features ship in Core 3.3.2.
Approved lifecycle, reconciliation and retention¶
schema_watch_update requires watch_id, the current status revision as
expected_revision, a new idempotency_key, and action. Review its operator
plan, approve through the existing CLI, then repeat the exact request. Changes
since review make the plan stale; obtain fresh status and use a new key. Retrying
a committed request returns its saved result, not another transition.
pausestops new polls and sends while keeping the baseline and queue. In-flight work may finish.resumerequires a paused, unexpired watch and does not extend consent. Changes during the pause are compared to the last baseline, not replayed as intermediate events. Disabled/failed/expired watches cannot be resumed. These are durable scheduling controls over Core polling, not a claim that the native async watcher exposes a pause method.renewrequireslifetime_seconds(1–2,592,000), setting expiry relative to approval execution time. Active, paused, expired and failed watches qualify; disabled watches do not. An expired active watch becomes paused. Failed watches remain failed until explicit recovery. Renewal preserves all history and outbox.restartrequires a failed, unexpired watch and moves it to paused. It preserves its baseline and queue, so fix the underlying provider failure before resuming. An incompatible checkpoint requiresreset_checkpoint: truetogether withaccept_history_loss: true: this deletes the checkpoint, starts a new baseline on the next poll and resets its event budget. The outbox and monotonic alert counter survive. Reset can miss changes during the discarded baseline interval.deleterequires a non-active watch, no in-flight lease andaccept_history_loss: true. Pending/sending/unknown notifications additionally requirediscard_unresolved: true. It atomically removes the definition and returns adeletedreceipt, freeing its slot. Status then returnsinvalid_handle; exact committed deletion replay returns the saved receipt. Approval receipts, exported artifacts, backups and SQLite free pages are not securely erased.confirm_delivery,discard, orretryrequire an unknownalert_idandevidence_referencefrom operator-side destination reconciliation. Use a non-secret case identifier, not credentials or raw data. Confirmation records an operator assertion (confirmed), not a successful observed send; discard recordsdiscarded, not delivery. Retry additionally requiresaccept_duplicate_risk: trueand explicitly approved possible duplicate external delivery. It queues the same native alert for one new attempt, never sends during the approval call, and remains paused if the watch is paused. Every decision records its plan, reference and time; attempts are counted.-
retentionacceptsretention: {"max_age_minutes": 1440, "max_count": 100, "mode": "all"}; at least one age/count limit is required. Native CoreTimeBasedPolicy,CountBasedPolicyandCompositePolicychoose retained entries (all: retain only if all rules retain;any: if any rule retains).retention: nulldisables future cleanup. Approval authorizes immediate and subsequent worker-sweep removal of timestamped terminal queue entries only (sent,confirmed,discarded). Pending/sending/unknown and legacy entries without resolution timestamps are protected. No active lease is reordered. Status exposes the policy and cumulativepruned_notificationscount. -
history_retentionuses the sameretentionpolicy shape, independently for checkpoint schema versions and alerts. It preserves the latest version, current polling baseline, event/poll counters and every alert referenced by the outbox. Protected entries may exceed the requested count. Old parent references may refer to removed versions.retention: nullstops future history cleanup. Status reportshistory_retentionand cumulativepruned_history; alert IDs remain monotonic across pruning, process restart and checkpoint reset.
Retention runs on worker sweeps, including paused or disabled watches, under unchanged authority. Queue retention alone does not erase native checkpoint history; use the separate history policy for that. Neither retention policy removes approvals, external copies or definition slots, and neither provides secure physical erasure. History/event/byte budgets still apply. No service-side delivery lookup or exactly-once guarantee is implied. Real external services remain unverified.
Session-owned schema history¶
schema_history_create constructs Core InMemorySchemaHistory (backend="memory",
default) or FileSchemaHistory (backend="file") in an owned private worker.
Choose strategy: semantic (default), incremental,
timestamp or git_like; max_versions defaults to 100 and must be between 1
and the operator's max_items (at most 1,000). The result contains history
(resource information) and revision=0.
schema_history_execute takes history_id, expected_revision and one closed
command. Its result contains the history information, the current revision
and an artifact; read the complete native result with artifacts_read.
| Command action | Fields | Native result |
|---|---|---|
save |
asset_id, optional version, metadata |
New version using the owned asset's schema, not its rows |
get |
version_id |
Version or null |
latest, baseline |
None | Newest or oldest retained version, or null |
count |
None | Native retained version count |
breaking_since |
version_id (ID or version string) |
Native breaking-change comparison against latest |
by_version |
version |
Version with that version string, or null |
list |
Optional limit, since, until |
Newest-first versions; ISO datetime filters are exclusive |
diff |
source_id, optional target_id (ID or version string) |
Native changes and summary; target defaults to latest; null if absent |
rollback |
version_id (ID or version string), optional reason |
New copied version with native rollback metadata, or null |
delete |
version_id |
Whether a version was removed |
watch_add, watch_check |
name, asset_id |
Register an initial schema, or update its snapshot and run native SchemaWatcher.check_now; status or change events |
watch_remove, watch_status |
name for removal only |
Removal result or native source status |
alerts, alert_stats |
None | Native local alert history or statistics |
alert_get, alert_acknowledge, alert_resolve |
alert_id |
Native alert or acknowledgement/resolution result |
snapshot |
None | File backend only: closed native JSON file checkpoint; an artifact, not durable storage approval |
Example commands, using the returned identifiers:
{"history_id":"<history.resource_id>","expected_revision":0,"command":{"action":"save","asset_id":"<asset.resource_id>","metadata":{"author":"operator"}}}
save, delete and rollback advance the revision even when a missing version
produces false/null. Queries do not advance it. Stale revisions fail before
mutation. Retention evicts the oldest native versions; rollback creates a new
version rather than overwriting history. Native limit=0 means no limit.
Use timezone-naive filters for the Core's timezone-naive creation timestamps;
an aware/naive comparison fails as it does in Core. UUIDs, creation/diff times
and timestamp/hash version strategies retain native nondeterminism.
Watch add/check/remove and alert acknowledgement/resolution also advance revision.
The watcher compares explicitly supplied immutable asset schemas; it does not poll
external files or start a background thread. Initial registration is a baseline,
not a saved version. Changes pass through native HistoryEventHandler and
AlertingEventHandler: changed schemas become versions and breaking changes become
local alerts. Read events/alerts through artifacts; no email, webhook or remote
notification is sent. Source and alert counts are bounded by max_versions;
reaching the alert cap rejects further checks rather than silently losing alerts.
Native logged storage/watcher failures are failures at the MCP boundary.
For cross-session recovery, create with backend="file", then call
schema_history_persist with history_id, expected_revision, and an
idempotency_key. It requires the existing operator approval flow and control
directory. The immutable signed checkpoint contains the native version files,
index and local alerts, including acknowledgement/resolution state. It stores no
rows. Save returns snapshot_id, sha256, and bytes. Repeating the same approved
intent at the same revision returns the original result.
{"history_id":"<history.resource_id>","expected_revision":1,"idempotency_key":"history-checkpoint-01"}
Call schema_history_resume in a new session with exactly one of snapshot_id or
the original idempotency_key. It revalidates signatures, file hashes, operator
policy and original source/workspace grants, and creates a new private history
handle at revision zero. Native version IDs, dates, schemas and alerts survive;
old session handles and watcher registrations do not. Each resume is an independent
working copy, not a shared mutable directory; subsequent edits require a new
approved checkpoint to survive session termination. There is no automatic crash
recovery of uncheckpointed edits. Operator-installed executable source bindings
cannot be transferred through this snapshot path.
Checkpoints are limited to 16 MiB (or the smaller operator file limit), 128 records
and 32 MiB total. storage_retention_preview and approved automatic retention
accept kind="histories"; storage_delete accepts the returned snapshot IDs.
Deletion also revokes active restored handles. Private working files are removed
on release/expiry; approved checkpoints remain until explicit/approved retention
deletion. Arbitrary directories, archives and compressed-history imports are not
accepted. The history resource hash describes the creation configuration,
not mutable history contents. Save observes schema only, but all observed asset
grants and handles remain dependencies, even after version deletion/eviction.
Release, expiry, revoked input grants or worker failure invalidate the history
and derived results. A worker/output failure after a command starts invalidates
the handle instead of claiming rollback or permitting an ambiguous retry.
Reaping occurs on the next session operation, and an owned lifetime timer bounds
idle workers. Use resources_release to close explicitly. Output bytes and
retained resources remain subject to session quotas. Custom strategies, caller
paths, arbitrary code and remote/paid services are not accepted.
Drift and resident ML¶
profilers_schema_evolution compares the native schemas of two owned assets
using baseline_id and current_id. Optional detect_renames (default true)
and rename_similarity_threshold (default 0.8) are passed to Core's
SchemaEvolutionDetector. The owned artifact contains all native change
records and the native compatibility summary, including its detection timestamp.
Change ordering follows Core's unordered column-set iteration. This binding
detects additions, removals, inferred renames and type changes; it does not infer
constraint/nullability changes or persist a baseline, start a watcher, or apply
migrations. Artifact reads require sensitive-result permission and both source
assets to remain authorized and live.
profilers_column_renames analyzes only columns added/removed between two assets
through Core's ColumnRenameDetector. It returns all confirmed and possible
candidates, similarity/confidence, type compatibility, reasons, metadata and
unmatched names in an owned artifact. It does not rename columns or migrate data.
Inputs are baseline_id, current_id, similarity_threshold=0.8,
require_type_match=true, allow_compatible_types=true,
high_confidence_threshold=0.9 and low_confidence_threshold=0.7.
An omitted calculator preserves Core's default composite. Explicit calculator
kinds are levenshtein, token, jaro_winkler (prefix_scale=0.1), ngram
(n=2), and composite (up to 16 weighted, non-nested calculators).
Composite calculators=null keeps native defaults; [] deliberately preserves
the native empty-weight behavior. Finite weights/thresholds and native clamping
are preserved; no arbitrary calculator code is accepted. For example:
{"baseline_id":"<baseline-handle>","current_id":"<current-handle>","calculator":{"kind":"composite","calculators":[{"calculator":{"kind":"ngram","n":2},"weight":0.6},{"calculator":{"kind":"token"},"weight":0.4}]}}
As with schema evolution, reading results requires sensitive-result permission and both live, authorized parent assets. Column ordering and matching follow Core; this is rename inference, not proof that two columns have the same meaning.
profilers_schema_compatibility calls Core's SchemaCompatibilityAnalyzer
on two owned asset schemas. Input fields are baseline_id, current_id,
rules (ordered names types, nullability, column_presence) and strict.
Omitting rules, setting it to null, or supplying [] preserves Core's
default three rules. The complete artifact includes is_compatible, level,
issues, and rules_checked; reading it requires sensitive-result permission
and both live parent assets. Custom Python rules are not accepted.
In the Core profile, strict=true rejects any reported issue and
returns compatibility level none. Failed or wrongly typed native rule verdicts
raise CompatibilityAnalysisError; they cannot produce a successful compatibility
artifact. Optional baseline_nullability and current_nullability map column names
to declared booleans, with both maps required and at most 1,000 entries each.
They require the nullability rule; declarations for a common column must be paired.
Core treats nullable-to-non-nullable tightening as breaking. Unknown columns and
untyped declarations fail. Polars dtypes alone still carry no declared nullability;
omitting these maps does not validate nullability or infer constraints from a
sample's observed values. Ordered/repeated built-in rule selection is preserved.
{"baseline_id":"<baseline-handle>","current_id":"<current-handle>","rules":["types","column_presence"],"strict":false}
Native panics are distinct from ordinary validation failures: when a native
extension aborts a worker without returning a canonical result, MCP reports
worker_error without a successful or partial result artifact. For example,
Polars timestamps outside Python's datetime range can trigger this during
privacy-validator value conversion. This is failure isolation, not a repaired
Core result or a successful privacy assessment.
For concrete detector configuration, use drift_catalog and drift_detect with
detector, settings, baseline_id, current_id, baseline_column and an
optional different current_column. This calls the selected native detector's
detect() rather than substituting the comparison facade defaults.
drift_compare takes baseline_id, current_id, optional columns, threshold,
sample_size and method. Methods are auto, ks, psi, chi2, js, kl,
wasserstein, cvm, anderson, hellinger, bhattacharyya, tv (alias
total_variation), energy and mmd. Computation and sampling remain Core's;
the full report is an artifact, not a summary presented as complete evidence.
Both snapshots are retained and reauthorized. A comparison that Core applies to
no columns is not an MCP quality pass.
Use models_catalog (paged, maximum five entries) for actual algorithm names and
all closed configuration fields. models_create takes algorithm, config,
and optional nested detectors for EnsembleAnomalyDetector. The catalog covers
ZScore/IQR/MAD/IsolationForest/Ensemble anomaly detectors, Distribution/Feature/
Concept/Multivariate drift detectors, DataProfileRuleLearner, ConstraintMiner and
PatternRuleLearner. A name is a fixed catalog key, never an import path. Unknown
options fail instead of becoming ignored ML kwargs.
Creation returns model_id and revision=0. Fit with asset_id and
expected_revision=0; success returns revision 1. Predict/score/inspect require
the current revision. Learning, fit-predict and explicit two-input detection
also advance the revision. models_detect adds reference_id and optional
columns to the ordinary model/data input. Inapplicable methods and unsupported
online learning are failures, never skips or implicit fallback retraining.
models_rules and models_diagnostics require model_id and expected_revision.
models_result_query takes model_id, expected_revision, asset_id, and
action: all_scores, anomalies, or drifted_columns. It predicts with the
native model and calls the Core result iterator/filter, retaining the output in
an owned artifact without incrementing revision. all_scores includes normal
rows and their score metadata, not just the anomalies in the usual summary.
Only drifted_columns accepts a finite threshold (default 0.5); Core's
inclusive comparison is preserved. A mismatched model type, untrained model or
stale revision fails explicitly. Artifacts depend on both model and input, and
reading them requires disclosure consent. Each call makes a fresh prediction;
it does not query an earlier prediction artifact.
models_rule_query additionally requires asset_id and action: suite,
validator_specs, by_type (with rule_type), or by_column (with column).
It calls the native rule learner's prediction and result conversion/filtering
methods, retaining the full output as an owned artifact without changing the
model revision. Selectors for other actions are rejected. Unknown types/columns
produce the native empty list, not a validation pass. The generated suite is
configuration data; it is never implicitly applied or executed. Model and input
revocation invalidate derived artifacts. Non-rule models fail explicitly.
Diagnostics retain the native configuration/info plus applicable statistics,
feature importance/profiles, reference correlations, column profiles, learned
patterns and ensemble names/weights. Read their artifact under disclosure consent.
Models live in owned isolated processes, preserving their actual learned state
between calls. Up to four resident model workers exist per session, also bounded
by the handle quota. Training/reference assets remain authorization parents.
Revision mismatches do not execute Core. Timeout/cancellation/protocol failure
kills and reaps the owned worker; a failed mutation invalidates its model rather
than reusing uncertain state. TTL expiry, release, revoked grants and session EOF
also invalidate model workers and dependent handles. These processes still have
the host user's OS privileges; the application policy is not an OS sandbox.
On Windows virtual environments, workers launch the underlying interpreter
directly while preserving the virtual environment. Shutdown waits for that
interpreter, not an intermediate launcher, before removing its working directory.
Resident native workers resolve HOME and USERPROFILE to their owned temporary
workspace, including on Windows. Default native caches are therefore temporary;
durable state requires explicit operator-pinned slots or factory configuration.
The operator's actual home directory and credentials are not inherited.
Private worker output sinks use UTF-8 explicitly, including Windows SDK progress
output; these streams remain separate from MCP protocol responses.
ML result artifacts contain encoding, model_algorithm, model_revision and
the tagged native result. This records the model state used without lossy
Python object serialization. models_save takes model_id and expected_revision
and retains native JSON; models_restore takes that artifact's resource_id
and returns a new model at revision 1. The twelve catalog algorithms preserve
trained trees, ensemble children, drift reference data and learned rules. The
versioned data-only format rejects executable types, duplicate JSON keys and
mismatched model identities. Incomplete legacy states fail instead of becoming
ready. Restored models inherit the saved artifact's authorization parents;
releasing training data also revokes restored models. These are session-owned
artifacts unless explicitly transferred to durable storage as described below.
Arbitrary plugin models and complete method-by-method lifecycle equivalence remain outstanding.
models_ensemble_add takes model_id, expected_revision, a fresh detector
specification and finite weight (default 1.0). It invokes native add_detector;
it does not implicitly train the child. models_ensemble_remove takes an index
(-8 through 7) and invokes native removal. Its model artifact retains the removed
child's configuration and learned state. Restore produces revision 1 for a trained
child or revision 0 for an untrained child. A successful edit advances the ensemble
revision once; failed mutations invalidate the resident handle. Up to eight
children and four nested levels are admitted. Instead of detector, supply
source_model_id and its exact source_revision to copy an existing trained
detector through native data-only save/load. This preserves learned state without
retraining; it does not share mutable Python objects. Subsequent source edits do
not change the copy. Self-inclusion and dependency cycles are rejected. The copy
retains source authorization and lifetime: releasing the source invalidates its
dependent ensemble. Stale source revisions fail before editing the ensemble.
models_persist takes a model artifact resource_id and an idempotency_key
(8–128 characters). It returns approval_required until the operator approves
the exact plan using approve-plan. This explicitly transfers the snapshot's
lifetime beyond the session; merely saving a model does not do so. models_resume
takes the resulting snapshot_id in a new session with the same private control
directory and policy. It restores native JSON without retraining, rechecks source
directory grants and creates new handles. Policy changes deny both restoration
and access to derived handles. Provider-bound model snapshots are not admitted.
If the successful save reply was lost, models_resume accepts the original
idempotency_key instead of snapshot_id; it resolves the committed receipt
without repeating persistence. Supply exactly one identity selector.
Storage is private local SQLite, not a cloud service or workspace export: at most
128 snapshots, 16 MiB per snapshot and 32 MiB total, additionally bounded by the
file budget. Snapshot bytes and signed metadata are checked on restoration; the
snapshot and successful intent receipt commit in one transaction. Use the approved
storage deletion operation below; do not edit the control database manually.
Approved native Checkpoint execution¶
Development Core profiles expose checkpoints_run. Unlike validation jobs,
this calls truthound.checkpoint.Checkpoint.run itself, including configured
actions, notification conditions, action retries and optional routing. It is
not part of the published 3.2.0 package.
An operator installs a trusted plugin and pins its distribution version, hash
and entry point in private bindings.json. Add
"checkpoints": {"daily": "checkpoint-factory"} and expose that capability
through the existing plugins binding. The zero-argument capability must return
a synchronous context manager yielding a native Checkpoint. That owner must
close its datasources and action resources on exit. Never place credentials in
model arguments or public plugin metadata. This executes trusted installed code
with the host user's privileges, not sandboxed code.
The first call returns an approval plan before importing the plugin. Review it through the existing operator approval CLI; approval includes the pinned factory's actions, external notifications and configured native retries. Repeat the exact request after approval. Changing the binding, package or policy invalidates authority. Arbitrary imports, data connections, actions and context objects cannot be supplied over this wire contract.
The response is a sensitive artifact handle containing the rich-value encoded
native CheckpointResult.to_dict(), including validation_run, action_results,
status and error. Read it through artifacts_read. Transport success only means
the result was retained; inspect the native status and action results to judge
validation/delivery success. Operator permission for sensitive results is
required; native diagnostics may contain private information.
Replay returns the retained result without rerunning actions. Timeout, shutdown,
revocation during execution or an unconfirmed failure is outcome_unknown and
is never automatically retried. Native action retries are different: they are
explicitly included in the approved configuration and can repeat external
effects. Terminating a worker cannot undo an effect or guarantee graceful
third-party cleanup. This operation does not start triggers, a Checkpoint
scheduler or an escalation service. Remote services remain unverified.
checkpoints_run_async separately approves the native AsyncCheckpoint.run_async
path. It accepts binding_id and idempotency_key, without use_router. The
bound zero-argument factory must return an asynchronous context manager
yielding AsyncCheckpoint, and close its executor, sources and actions on exit.
The same result-artifact, revocation and unknown-outcome rules apply; this is
not a conversion of the synchronous Checkpoint into a thread wrapper.
Native Checkpoint services¶
Development Core profiles also expose checkpoint_services_open and
checkpoint_services_execute. Use private checkpoints bindings for these
factories as well. For kind="runner", the factory returns a synchronous
context manager yielding a stopped native CheckpointRunner, with its
Checkpoints, triggers, callbacks and queue limits configured by the operator.
For kind="escalation", it returns an asynchronous context manager yielding a
stopped native EscalationEngine, with operator-selected policies, store,
scheduler and notification handler. Owners must stop services and close all
resources on exit. No factory, credentials, notification destination, arbitrary
context or Python object is accepted from the model.
Approve the returned plan with the operator approval CLI, then repeat exactly
the same request. The returned monitor_... handle belongs to this MCP session.
Opening does not start native scheduling. Each command, including status and
queue consumption, requires its own handle-bound approval:
{"binding_id":"daily-runner","service_id":"monitor_<returned-id>","command":"start","idempotency_key":"runner-start-01"}
- Both kinds support
start,stopandstatus. - A runner additionally supports
results, withmax_results(1–1000, default 100, also bounded by policy). This consumes the native result queue. Replaying the approved request returns the retained artifact without draining again. Native queue capacity/drop behavior is unchanged. - An escalation engine supports
triggerwithincident_idand optionalpolicy_name;record,acknowledge,resolve,cancelandescalaterequirerecord_id. Acknowledgment/resolution/cancellation recordmcp-approved-operator, not a model-provided person's identity. Escalation does not force otherwise-disallowed transitions. Native failure results are retained, not rewritten as successful delivery. Stats encode integer-keyedescalations_by_levelas[{"level":1,"count":2}]to preserve key types.
Read command results through artifacts_read; they remain sensitive and tied
to the service grant. stop stops native execution but retains the owner and
its native in-memory state for a later approved start. Releasing the handle,
closing the session, expiry, revocation or a failed exchange terminates the
worker. Unknown command effects are never automatically retried. Termination
cannot retract notifications or guarantee third-party cleanup. These are
session-owned services. Policy configuration can be persisted and restored as
described below. Durable native execution is separately opt-in through the pinned
operator factory, as described under durable checkpoint recovery.
Real external notification and store services remain unverified. These tools
are 3.3.0 features, not part of 3.2.0.
Additional approved management commands reuse the native service methods:
- Runner
schedule_updatetakescheckpoint_name,trigger_nameand a nonemptyschedule_updatepatch for one built-in interval or cron trigger. It reusesCheckpointRunner.edit_schedule; the runner and its execution thread must be stopped. Editing retains the trigger's run count, last-run clock and attachment, rather than creating a new first run. Interval seconds/minutes/hours, enabled, max runs, run-immediately, catch-up, max-concurrent, JSON metadata, start/end times, weekdays and timezone can be edited. Omitted settings remain unchanged; explicit null clears only start/end time, weekdays or timezone. Boundaries use naive local datetimes; empty weekdays and nonpositive total intervals are rejected. Native scheduling semantics apply: timezone/catch-up/concurrency settings do not add guarantees absent from Core. Cron triggers acceptexpressionand shared trigger settings; interval-only settings on cron are rejected. Renaming is not supported. For provider-defined triggers, only shared fields (enabled,max_runs,run_immediately,catch_up,max_concurrent, JSONmetadata) can be edited; provider-specific paths, connections and callbacks stay in the factory. It does not itself run or restart a checkpoint. Each edit needs exact-request approval. - Runner
checkpoint_updatetakescheckpoint_nameand a nonemptycheckpoint_updatepatch. It reusesCheckpointRunner.edit_checkpointwhile quiescent.checksreuses the closed registry constructor/settings contract (1–1000 checks, without callback bindings); an explicit checks list replaces validation configuration. Other fields aremin_severity,auto_schema,run_name_template,tags, JSONmetadata,fail_on_critical,fail_on_high,timeout_seconds(1–86400) andsample_size(positive). Onlymin_severityandsample_sizeaccept explicit null. Omitted fields retain their current values. Sources, schema objects/paths, actions, routers and callbacks remain factory-owned. - Runner
run_oncetakescheckpoint_name;run_allexecutes every registered checkpoint. Native actions and result callbacks run; results are returned in artifacts, not inserted into the scheduled-result queue.remove_checkpointtakescheckpoint_nameand returns the native removal boolean. Removal does not cancel an already executing checkpoint. - Escalation
policyandpolicy_removetakepolicy_name. Missing policies return null/false. Removing a policy does not resolve existing incidents.active_recordsoptionally filters bypolicy_name;incident_recordsrequiresincident_id. Results are complete native lists subject to the artifact byte budget, not silently truncated. policy_updaterequirespolicy_nameand a nonemptypolicy_updateobject. It updates an existing policy'senabled,severity_filter,max_escalations,cooldown_minutes,business_hours_only,business_hours_start,business_hours_end,business_daysortimezone. Omitted settings are preserved; explicit nulls and unknown fields are rejected. It also acceptsdescription,triggers,levelsand JSONmetadata. A supplied levels list replaces the whole list (unique positive level numbers). Each level supports delay, targets, repeat count/interval, acknowledgment, auto-resolve, JSON conditions and metadata. Each target supports native type, identifier, name, priority and JSON metadata. Omitted fields are preserved. All eight native trigger and target types are accepted; custom callbacks and delivery transports still come from the pinned operator factory. Approval of the exact request includes destinations, not just the policy name. Do not put secrets in these settings: requests and policy artifacts are retained locally. This replaces the registered native policy, not existing records or scheduled jobs. Subsequent native execution decides when it consults that policy. Core's explicitpolicy_namelookup bypasses enabled/severity selection; disabling a policy affects automatic selection, not explicit triggering. Business-hours enforcement is also Core-defined (currently advisory). Timezone strings are passed through: Core treats timezone lookup failure as within business hours. This setting is not an authorization boundary.
{"binding_id":"incident-engine","service_id":"monitor_<returned-id>","command":"policy_update","policy_name":"approved-policy","policy_update":{"enabled":false,"cooldown_minutes":30},"idempotency_key":"policy-disable-01"}
Every command, including reads, needs its own exact-request approval. Cached
replay never re-executes a removed checkpoint or restores a removed policy.
Invalid command shapes are rejected before execution. A native exception or
lost receipt after execution starts remains outcome_unknown, not safe retry.
policy_create takes the same policy_name / policy_update shape, applying
native defaults for omitted fields. It refuses to overwrite an existing policy.
policy_update refuses to create a missing policy. Empty levels/triggers are
allowed and retain native semantics, rather than inventing a default recipient.
The trigger command also accepts trigger_type (native default
unacknowledged) and JSON context. Both are forwarded to Core, including
native automatic policy selection; they are rejected on unrelated commands.
persist_configuration takes explicit unique policy_names (1–100), requires a
stopped escalation service, and returns a signed immutable snapshot_id, hash
and byte count in its artifact. Missing policies are stored as removal markers.
Storage is private approval storage, bounded to 1 MiB per snapshot, 128 snapshots
and 16 MiB total; no caller-controlled file paths. Native non-JSON metadata is
not silently converted. Use checkpoint_services_open with kind="escalation",
the same binding_id, this snapshot_id and a fresh idempotency key to restore
after session shutdown. A separate exact-request approval is required; altered
payloads, policy or binding changes are rejected before factory execution.
The pinned owner factory is reopened, selected policies/removals are applied,
and the service stays stopped. Unselected factory policies remain unchanged.
This restores policy configuration only, not incident records, pending jobs,
notification delivery receipts or runner schedules. Those records remain owned
by the operator's native store. They can use the separate durable native recovery
contract below. Opening a session never installs an automatically restarting service.
For a runner, persist_configuration instead takes 1–100 unique
checkpoint_names. The service and execution thread must be quiescent. Its signed
snapshot retains the listed checkpoints' editable scalar settings, explicitly edited
closed check specifications and interval/cron/shared trigger configurations. Missing
checkpoints are removal markers. Trigger names must be explicit and unique. Restore
with checkpoint_services_open(kind="runner", snapshot_id=..., ...) under the same
binding and fresh approval. The factory supplies all sources, actions, provider fields
and unchanged validators; these opaque objects are never serialized or imported from
a snapshot. Rich non-JSON metadata is rejected. Snapshot restore edits matching native
objects and preserves durable trigger history; it neither starts scheduling nor
replays previous actions. Runner and escalation snapshots cannot be interchanged.
checkpoint_services_execute(command="configuration_list", ...) discovers signed
configuration snapshots for the current binding, service kind and authorization.
It requires its own exact-request approval. configuration_limit (1–100, bounded
by the session item limit) and configuration_offset page stable snapshot IDs;
only ID, SHA-256, byte count and service kind are returned, never configuration
contents or private factory fields.
configuration_delete requires an explicit unique snapshot_ids list (1–100)
and a separate approval. The entire selection is authenticated and authorized
before any deletion; missing, corrupted, foreign-binding or wrong-kind entries
reject the batch without removing a valid prefix. The receipt is bounded before
deletion, and an exact replay uses the retained approval result. Deletion frees
snapshot count/byte capacity without changing running services, journals or
notification attempts. Deleted snapshots cannot be restored. This is explicit
logical deletion without guaranteed physical erasure.
configuration_retention_set persists a separately approved policy for the current
binding and service kind. retention requires expires_at (Unix seconds) and at
least one of max_age_minutes, max_count, max_size_bytes; mode is all or
any, using native Core policy semantics. enabled: false disables cleanup.
configuration_retention_status reports the signed policy and whether it is active;
configuration_retention_apply explicitly performs cleanup with its own approval.
While active, cleanup also runs automatically during snapshot save and discovery.
There is no idle background timer. Policies survive reopening the private control
store, remain bound to the original authorization, and stop at expiry. Undated
legacy snapshots are preserved. Signature, authorization or output-budget failures
roll back the transaction. A save receipt's retained: false means the policy
immediately removed that snapshot; it cannot subsequently be restored.
Standalone bound data sources, one-shot sync/async Checkpoints and schema monitors also check readiness before consuming approval. Parent-only request/artifact preparation finishes before the execution intent starts.
One-shot Checkpoints resolve the fixed Core validation facade and sync/async Checkpoint classes during readiness, before loading an operator factory. A missing Core dependency at this stage leaves the approval available after repair. This uses the existing shared deadline and does not retry uncertain operator effects.
Shared factory-bound services (including store, execution, profiler, plugin and tenant services) use the same effect-free readiness exchange before consuming an approval. The readiness and native factory calls share the original deadline. Bootstrap failures preserve approval; uncertainty after factory entry still blocks automatic replay.
Checkpoint service startup first performs an effect-free Core worker readiness
exchange. It shares the original request deadline with factory execution; it does
not grant another timeout window. A readiness failure closes the worker and leaves
the exact request approved because no operator factory has run. Once native
factory execution begins, an uncertain failure remains outcome_unknown and is
not automatically replayed. High host load may still cause a real startup timeout;
readiness is not a guarantee of eventual startup.
Durable checkpoint recovery¶
The operator factory can opt into RunnerConfig(journal_path=...) or
SchedulerConfig(journal_path=...). MCP requests never accept journal paths.
The APScheduler adapter uses its supported 3.x API; install the optional
.[mcp,checkpoint-scheduler] extra from the development checkout. Asyncio and actual APScheduler restart/
notification-drain tests exercise the journal path. An escalation engine requires a file-backed SQLite record store and journal in
the same file; memory stores and separate files are rejected. This lets the
notification receipt, incident counters and following reservation commit together.
A runner requires explicitly named, unique triggers. Trigger configurations must
be JSON compatible; providers with additional mutable state must implement
export_runtime_state and restore_runtime_state alongside the base clocks.
Custom provider state and external broker delivery remain provider responsibilities.
Opening a durable service takes exclusive process-bound ownership of that journal.
A live owner is never displaced by a timeout; stop retains ownership, and close
or process exit releases it. Use the same approved binding/factory in a new session,
with fresh approval, then approve start to resume unclaimed reservations.
Policy, binding, control-directory or plugin identity changes reject recovery.
Scheduled effects recheck the current binding and installed plugin identity.
Escalation jobs also pin the native policy definition; changed or disabled policies
quarantine the job instead of notifying a new destination.
Intent commits before native actions or notification delivery. Interrupted claimed
work becomes unknown, never automatically replayable. Unknown notifications also
block later/manual escalation for that incident. A handler returning false,
raising, being cancelled or losing its completion commit cannot register a following
notification. This does not promise exactly-once delivery to an external service.
Runner run counts and last-run clocks survive restart. In-memory result queues do
not; retain results separately if required. A runner's unknown action/callback
outcome blocks that trigger, including a new manual run after an unknown manual run.
Both service kinds support these separately approved commands:
recovery_status: returnsenabled, boundedattemptscontaining onlyidentity/state, andnext_offset.recovery_limitis 1–1000 (default 100, also bounded by policy);recovery_offsetdefaults to 0. Payloads, raw source values, exception messages and filesystem paths are not returned. A live attempt isattempting; only a new exclusive owner classifies abandoned claims as unknown.recovery_resolve: takesattempt_idandresolution(completedorcancelled), while stopped/quiescent. The operator must independently determine the outcome. This never sends a notification or executes a checkpoint. Confirming an unknown notification updates its count and reserves its following level under the unchanged policy. Discarding it records cancellation without creating a following notification. Confirming/discarding an unknown scheduled runner attempt consumes that trigger slot and updates its last-run clock.recovery_forget: takes 1–100 uniqueattempt_ids, while stopped/quiescent. Only completed/cancelled receipts can be deleted, atomically; pending, attempting and unknown evidence is protected. This is logical deletion, not secure erasure.
{"binding_id":"incident-engine","service_id":"monitor_<returned-id>","command":"recovery_status","recovery_limit":100,"idempotency_key":"recovery-status-01"}
Native users should call await engine.close() or runner.close() when finished;
MCP worker cleanup does this automatically. Reopening never implicitly approves
execution or retry. Local subprocess restart tests cover native actions, notification
interruption and installed factory/session reopening; real external notification,
cloud, broker and paid AI services remain unverified.
Native schedule preview¶
schedules_preview takes a cron expression (up to 256 characters), an explicit
ISO datetime after, and count (1–100, default 5). It reuses Core's parser,
matches and next_n in an owned, deadline-limited worker. Five/six/seven-field
expressions, presets and special fields have the same native semantics. Naive
times remain naive; explicit UTC offsets are retained, not converted to the
server timezone. This is not IANA timezone/DST scheduling support.
The result distinguishes invalid expressions (valid=false, native errors)
from a valid expression whose native four-year search horizon yields fewer
times (search_exhausted=true). Neither is a data-quality PASS. Preview does
not register a schedule, start a checkpoint or execute any action. Persistent
scheduling is available separately for bounded validation occurrences below.
Durable validation occurrences¶
jobs_submit takes validation (an assets_validate request), an
idempotency_key, and optionally schedule with expression, offset-aware
after, and count (1–100). Due times use native cron semantics. All requested
times must be found; partial cron search results are rejected. Submission
requires separate operator plan approval to persist the captured input and
authorize deferred computation. Context/schema handles, reference-frame handles,
provider bindings and external actions are not admitted; inline schema_spec
and closed validator settings remain available.
Submission atomically persists all occurrences or none, returning the first
job_id and the complete occurrence_ids. Each occurrence owns its own state,
lease and result; cancelling one does not cancel the others. All input copies
count against the unchanged storage quotas. These are a finite series, not an
unbounded recurrence rule. Use each ID with jobs_status, jobs_execute, jobs_result,
jobs_cancel, or jobs_recover. A client must explicitly execute a due job;
jobs_submit itself does not install a daemon or authorize recurring execution.
There are no paid provider calls or automatic retries.
Queued input survives session closure. Completed native results survive restart
and can become newly authorized session artifacts. A completed execution can
still contain data-quality violations; native execution failures remain failed.
Cancelled queued work is not executed. Cancelling a running job returns busy
rather than claiming another process was stopped. After the execution deadline
plus a 30-second cleanup allowance, explicit recovery marks an unfinished lease
outcome_unknown, never PASS or an implicit retry. This allowance is not an
extension of the native worker timeout. Successful receipts require computation,
owned-resource cleanup and a transactional result commit.
The private store admits at most 128 jobs, 8 MiB per input/result and 16 MiB total
job payloads. To recover a lost submission reply, the five job operations after
submission also accept the original idempotency_key instead of job_id; they
resolve and reauthorize the existing record rather than submit another job.
For a series, the original key resolves the first occurrence and its complete
ID list. Supply exactly one selector. These limits remain subject to existing
file/session budgets. Provider-backed jobs remain unsupported; this is not full scheduling parity.
Persistent recurrence and foreground worker¶
schedules_register takes the same validation request as jobs_submit, but requires
a schedule with count: 1. Its separately approved plan authorizes captured-input
storage and indefinite recurring execution. The definition and first occurrence
commit atomically. schedules_status and schedules_disable accept either its
control_id or original idempotency_key. Disabling cancels a queued head; work
already running may finish. Failed, cancelled or indeterminate heads pause the
definition without retrying. Successful heads advance by one native cron time per
sweep, including missed times in order. A native cron search with no next time
stops with an error, never a fabricated next occurrence.
The operator must start the foreground process explicitly with the same grants and limits as the registering server (replace paths with existing local directories):
truthound-mcp worker --allow-dir /data --control-dir /private/control \
--profile core --allow-sensitive-results --poll-interval 5
--once performs one sweep; --poll-interval is an integer from 1 to 3600 seconds.
SIGINT/SIGTERM stops after the current bounded operation. No system service is
installed. Separate workers share transactional cursors and execution leases.
Storage quotas do not increase: exhausted storage stops the worker. At most 64
definitions, including disabled ones, are retained. Persistent retention policies
can prune old completed payloads; current active/paused schedule heads are protected.
Policy changes, corrupt records and uncertain outcomes are not silently retried.
storage_policy_register accepts the preview policy plus an idempotency_key and
requires separate approval for future automatic deletion. The worker evaluates
approved policies before schedules on each sweep. storage_policy_status and
storage_policy_disable accept a control_id or original idempotency_key.
Disabling prevents later sweeps, not an already committed deletion. Policies and
schedule cursors survive session/process restart. This does not install an
always-on OS service or add external provider execution.
Durable storage retention and deletion¶
storage_retention_preview takes kind (models, jobs or histories) and at least one of
max_age_minutes (1–5,256,000), max_count (1–128), or max_size_bytes
(1–33,554,432). It reuses Core TimeBasedPolicy, CountBasedPolicy,
SizeBasedPolicy and CompositePolicy. With mode="all" (default), every selected
policy must retain an item; mode="any" lets any selected policy preserve it.
Size is the actual stored input plus result bytes, or model/history checkpoint payload bytes, not
SQLite file size or a new storage quota. Persistent automatic policies accept
the same fields and retain their approved composition after restart.
It returns candidates, preserved and excluded
identity lists without deleting anything. Queued/running/indeterminate jobs and
legacy records without a creation timestamp are excluded. Equal timestamps use
identity ordering for deterministic native count-policy evaluation.
storage_delete takes exact identities (1–128 unique snapshot/job IDs) and an
idempotency_key. It requires separate operator approval and rechecks ownership,
signatures, current grants and inactive state inside the deletion transaction.
Active and indeterminate jobs cannot be deleted. All requested payloads and the
successful receipt commit atomically; replay returns the receipt without deleting
again. Existing resumed models/result handles are denied after their durable
source is deleted. Approval receipts remain for audit and replay prevention.
Deletion is logical SQLite payload removal, not guaranteed disk sanitization.
Explicit previews do not delete data; automatic cleanup requires the separate
persistent policy approval and operator-started worker above. Archival, compression
and full retention subsystem parity are not claimed. Legacy undated records may still be explicitly selected for approved
deletion. All operations require the same private control store and operator
policy plus sensitive-result consent.
Native loopback dashboard lifecycle¶
dashboards_open uses a private dashboards binding whose pinned async factory
yields a stopped truthound.profiler.dashboard.server.DashboardServer or
truthound.datadocs.dashboard.ReflexDashboardServer. Install
.[mcp,dashboard-streamlit] for Streamlit or .[mcp,dashboard] for Reflex from
the development checkout. For Reflex, the operator explicitly prepares a private
project with Node/npm before opening the binding; MCP start never installs or
builds dependencies. See the native preparation guide.
The factory owns host, port, page settings and data-access controls. No source path,
Python script, port, host or configuration is accepted from the model. MCP permits
only localhost, 127.0.0.1 or ::1; it does not create a remote ChatGPT server.
dashboards_execute repeats the binding, live service_id, exact idempotency key
and command (start, stop or status). Each needs its own approval.
start accepts timeout_seconds in (0, 30] (default 10), reports native health
readiness separately from process liveness, and refuses an already occupied port.
Status returns running, ready, url and exit_code. Stop terminates only its
owned server/process group. Handle release, expiry and revocation reap the owned
worker's descendants; the native child also watches parent disconnect, including
hard parent termination. This is a local process lifecycle, not OS-service installation.
The native launcher now sends all dataclass settings as private JSON data to a
static application entry point; titles are never interpolated into executable
Python. Local tests exercise actual Streamlit and prepared Reflex startup,
HTTP readiness, stop/restart, handle release and parent hard-kill, plus
Streamlit occupied-port failure and native title rendering. Release requests
bounded native/factory cleanup before reaping the captured process tree; blocking
provider cleanup can still require hard termination.
Native UI configuration still does not create an authentication boundary:
require_auth is not enforced by the existing UI, and its file-loading controls
retain native semantics. The Reflex viewer's display/export flags control its UI,
not authorization or removal of profile values from the private compiled project.
Prepared configuration changes are refused at start and require a fresh build.
Remote deployment remains unverified.
Native broker adapters and processing¶
brokers_open takes a binding_id from private brokers bindings and an
idempotency_key. Its pinned installed capability returns an async context manager
yielding truthound.mcp.brokers.BrokerRuntime: a disconnected native
BaseStreamAdapter, optional native WindowProcessor and ExactlyOnceProcessor,
and an optional operator-selected output_topic. Opening does not connect. The
model cannot supply broker endpoints, credentials, import paths, topics or callbacks.
Raw messages require sensitive-result consent. The returned service_id is a
session-owned monitor_... handle; release, expiry and revocation terminate its owner.
brokers_execute repeats binding_id/service_id, an exact idempotency_key and:
connect,disconnectorstatus. Status exposes native state/metrics, retained batch count and configured processor statistics/watermark. Disconnect drops local batches. It cannot retract external effects or guarantee third-party cleanup.consume, withlimit(1–1000, default 100, policy bounded) andtimeout_ms(1–30000, default 1000), returns abatch_id, rich-value encoded native messages andsize. Consumption advances the native cursor and never implicitly acks. At most 16 batches are retained within the current artifact-byte budget.ack, with a retainedbatch_id, calls nativecommit_batchand releases that batch. Native adapters may commit only the highest offset per partition. Replaying the same approved request returns its receipt without committing again.window_addadds a retained batch once to the operator-selected native window;window_flushcallsforce_emit. Neither command acks the source.processpasses a retained batch once through nativeExactlyOnceProcessor.process_message. Native processing may commit offsets or produce through an operator-selected sink. Failures and deduplication remain explicit; exception text is replaced with a closed failure code.recovercalls native processor transaction rollback recovery and returnsrolled_back. It does not establish an external exactly-once delivery guarantee.seektakes nonnegativepartition/offsetwhile no batches are retained.producetakes a JSONmessagewithvalue, explicit ISOtimestamp, optionalkeyand nonnegativepartition; its destination is factory-owned.flushcalls the adapter's native flush. These may have external effects and native retries.
Every command, including reads, requires separate operator approval. Lost effects
are outcome_unknown, never automatically retried. Batch/window state stays in the
resident owner; cross-session state, source offsets and recovery durability depend
on the operator's native backend. Local tests use the real Core test adapter;
Kafka/Kinesis/cloud services remain unverified.
An operator factory may pin a native validator, callbacks and settings with
BrokerRuntime(..., validator=StreamingValidator(...)). validate_batch requires
a retained batch_id and uses native MessageBatch.to_dataframe() followed by
StreamingValidator.validate_batch(). validation_stats and validation_reset
use native statistics and reset. Each command requires exact-request approval;
replaying the same request uses its retained receipt. Validation does not imply
acknowledgement or processor execution. Native failures and output-limit failures
remain failures; uncertain effects are not automatically replayed. Native stream/async-stream sessions use the separate commands below.
Native stream and async-stream validation sessions¶
An approved brokers factory may return BrokerRuntime with no broker adapter:
runtime = BrokerRuntime(
file_source=ArrowFlightStreamingSource(
host=operator_host, port=operator_port, ticket=operator_ticket,
use_tls=True, token=operator_token, chunk_size=1000,
),
validator=StreamingValidator(validators=["null"], config=StreamingConfig(error_handling="fail")),
pipeline=StreamingValidationPipeline([StreamingNullValidator()], chunk_size=1000),
streaming_validators=[NullValidator()],
)
Flight factories require PyArrow with Flight support in the worker environment; it is an optional provider dependency.
The factory pins transports, credentials, validator objects, callbacks and settings.
stream_source instead accepts a native truthound.realtime.StreamingSource.
file_source accepts native file/Arrow/Flight StreamingSource objects; the two
source fields are mutually exclusive and initially disconnected. The factory owns
any additional resources; MCP owns generator closure and source disconnection.
The caller cannot supply hosts, URLs, tickets, tokens, imports or paths.
After brokers_open, use separately approved brokers_execute requests:
| Command | Behavior |
|---|---|
source_connect / source_disconnect |
Native connection/open and generator close/disconnect. |
source_status |
Connection and active validation/raw-cursor state, without credentials. |
stream_start |
Pin stream_mode="sync" or "async" and finite max_batches (1–10000, default 100). Uses native validate_stream or validate_stream_async. |
stream_next |
Read up to limit native batch results; returns exhausted only when the native generator stops. No speculative extra batch is consumed. |
stream_cancel |
Close the paused native generator; preserves the current provider cursor. It does not rewind or acknowledge data. |
source_next |
Page native raw chunks when validation is inactive; JSON data remains subject to sensitive-result permission and artifact budgets. |
pipeline_validate |
Apply the pinned native pipeline to retained broker batch_id. |
pipeline_stream |
Run native StreamingValidationPipeline.validate_source over the connected file/Flight source with shared validator state and finalization. |
stream_validate_many |
Run native multi-validator streaming and return its per-validator aggregates. |
stream_next/source_next limits also obey session item limits. Exact-request
replay returns the prior artifact rather than advancing the cursor or rerunning
callbacks. An exactly full page may need another call to observe exhaustion.
Do not mix a raw cursor with a validation generator; disconnect/reconnect to start
a new read according to the native provider's semantics. Native errors, partial
results and budget/deadline failures are not reported as completed validation;
uncertain effects are not automatically retried. Release, expiry, revocation,
parent loss and service shutdown terminate the owned worker and connection.
Flight uses real FlightStreamChunk.data, preserves all leftover RecordBatches,
normalizes Arrow slice offsets before supported Polars imports, applies configured
RPC timeouts and bearer headers, and cancels the reader before closing its client.
Unknown row count raises TypeError until exhaustion; it never returns a negative
Python length. No public/cloud Flight endpoint or production credential is implied.
Native AI proposal review and application¶
ai_services_open uses private ai_services bindings. The pinned async owner yields
truthound.mcp.ai_services.AIRuntime with a native TruthoundContext, a required
explicit provider, operator-selected data/source/model and optional native run.
Its workspace must be within an operator-allowed workspace root. The model cannot
supply provider credentials, provider/model selection, source paths or actor identity.
Opening and every command need exact-request approval; provider calls can disclose
native redacted summaries and incur provider charges. Local tests use a deterministic
provider and make no paid API calls.
ai_services_execute repeats the binding, session service_id, idempotency_key
and one closed command:
suggestrequiresprompt(1–16000 characters); optionalsample_sizeis 1–10000 (default 1000). It calls nativesuggest_suiteand persists the proposal.explainuses an optionalrun_idor the operator-provided native run, withinclude_history=trueby default. It calls nativeexplain_runand persists analysis.list_proposalsandlist_analysestakelimit(1–1000, default 100, policy bounded); the latter optionally filters byrun_id.proposalandhistoryrequireproposal_id;analysisrequires a nativeanalysis_id.approve,rejectandapplyrequireproposal_idandexpected_sha256from the previous result'sartifact_sha256, plus optionalcomment(up to 4096 characters). The digest pins the native proposal revision in the approved request; stale revisions are rejected before mutation. Each decision returns a new digest. Approval and application are separate exact operator decisions. Actor identity is fixed tomcp-approved-operator. Native applied proposals remain terminal.
Redaction is fixed to native summary_only; raw samples and executable code are not
accepted. Responses preserve native fields with rich-value transport, excluding
workspace_root. Lifecycle state lives in the native workspace; reopening requires
fresh approval and the same bound factory. MCP receipts do not make native multi-file
writes transactional against unrelated workspace writers. Unknown provider/application
effects are not retried. Paid providers and real external deployments remain unverified.
Approved OpenLineage delivery¶
openlineage_services_open resolves private openlineage_services bindings to
an installed async factory yielding
truthound.mcp.openlineage_services.OpenLineageRuntime(emitter, datasets={alias: dataset}).
The emitter is a native OpenLineageEmitter; endpoint, authorization and dataset
metadata are configured by the operator. Opening sends no lineage event. This is
an operator-configured Core feature, not a verified remote-service deployment.
Every openlineage_services_execute call repeats binding_id, service_id and
an exact idempotency_key and requires approval. Closed commands are status,
start, running, complete, fail, abort, graph, resolve, shutdown.
start takes job_name, optional owned active parent_run_id, and optional
datasets aliases for inputs; complete takes aliases for outputs. Transition
commands take the owned run_id. fail sends a fixed operator-reported failure
message. The wire cannot supply exceptions, credentials, endpoints or imports.
graph takes an owned graph_id and job_name, sends only dataset names, and
retains that graph's permission and expiry dependencies on the service.
Delivery failures return an outcome_unknown envelope with a sensitive artifact
containing run_id, event_type and outcome_unknown: true. The owner remains
available for status and resolve; repeated transitions cannot resend that
uncertain event. resolve requires run_id and an explicit boolean delivered
after external receiver inspection. It records the operator's determination
without a request. Approval replay returns the retained result without sending.
State artifacts omit transport configuration and dataset contents. New runs are
bounded to 1000 retained active/uncertain records. Graph network failures may be
partially delivered; there is no whole-graph transaction or automatic retry.
shutdown joins native work and factory teardown before acknowledgement. Releasing
an input graph revokes its dependent service; releasing the service, expiry or
parent loss closes the resident owner. Forced termination cannot determine remote
delivery. Local emitter state is not durable across process loss. Actual owned
loopback receiver tests do not establish live external service compatibility.
Native audit and telemetry administration¶
audit_services_open uses private audit_services bindings whose async owner yields
truthound.mcp.administration.AuditRuntime containing a native AuditStorage.
audit_services_execute supports separately approved query, read, count,
record, flush and delete_before. Query accepts UTC-offset-bearing
start_time/end_time, native event_types, actor_id, resource_id, outcome,
limit (1–1000, default 100, policy bounded) and nonnegative offset. Count accepts
the time/event-type filters only. Read requires event_id. Record requires action
and optionally outcome, message and JSON data; it creates a native custom
event with the fixed operator actor and native checksum. It does not impersonate
an authentication event. delete_before takes one explicit offset-bearing before
and deletes matching native records irreversibly after its own approval. No SQL,
storage paths, actor overrides or credentials are accepted from the model.
telemetry_services_open uses private telemetry_services bindings whose owner
yields TelemetryRuntime: native MetricsRegistry, TracerProvider, and explicit
maps of named metric/exporter instances. telemetry_services_execute supports:
collectfor native registry values;exportrequires an operator-ownedexporter_idand may send externally or write through the native exporter.metrictakes an existingmetric_id, finitevalue, optional stringlabelsandupdate:incfor Counter/Gauge,setfor Gauge,observefor Histogram/Summary. Native label/type/range validation applies.spanrequiresspan_nameand optional JSONattributes; it creates/ends one native span in this owner and returns its trace/span identity.flushtakestimeout_ms(1–30000, default 1000);shutdownends the tracer provider. Native false flush/shutdown results remain false. Shutdown ends further telemetry commands for that handle.
All administrative commands require exact approval and sensitive-result consent. Factories own storage, credentials, exporter endpoints, filtering and provider configuration. These tools control their resident instances, not other processes' global telemetry or a remote security control plane. Real external audit stores and telemetry exporters remain unverified. Tests use temporary SQLite and native local memory exporters, including a deliberately failing flush processor.
Bound Core security and plugin administration¶
security_services_open uses private security_services bindings. The pinned
async factory yields truthound.mcp.platform_services.RBACRuntime: a native
RBACManager, optional fixed tenant_id and explicit writable/readable
attribute_keys and optional additional condition_fields. The model cannot choose tenant, stores, credentials, authentication
callbacks or policy evaluators. A fixed tenant rejects targets belonging to another
tenant before CRUD or assignment. This manages the selected Core manager; its
permission decisions never change MCP consent, directory grants or approval requirements.
security_services_execute repeats the binding, live service handle, exact
idempotency key and one command; every read/mutation needs approval:
roles/principalslist native objects in the fixed tenant withlimit(1–1000, default 100, policy bounded) and nonnegativeoffset.role/principalrequire the correspondingrole_id/principal_id.role_create/role_updaterequirerole_idand nonemptyrole_patch. Fields arename,description,enabled,permissions,parent_rolesandconditions. Creation requires a name. Permissions use the strings/objects described below; list fields replace their whole native set. Parent roles must exist within the bound scope.permission_add/permission_removetakerole_idand onepermission.role_deletereturns the native deletion boolean.principal_create/principal_updatetakeprincipal_idand a nonemptyprincipal_patch:name,email,enabled,direct_permissions,attributes. Creation requires a name and uses the native default principal type; attributes are restricted to factory-declared keys.principal_deletereturns native deletion.assign_role/revoke_rolerequire both IDs.permissionsreturns native assigned/ inherited permission inventory; it is not an authorization decision.checkrequiresprincipal_id,resource,actionand optional JSONresource_attributes; it returns native allowed/effect/reason/matches.cache_clearuses native cache invalidation.
Permission inputs accept resource:action[:scope] ALLOW strings or complete JSON
objects with resource, action, optional scope (default empty), and effect
(allow or deny, default allow). Components cannot contain :. This applies
to role permissions, principal direct permissions and permission add/remove.
Removing an ALLOW string does not remove a distinct DENY object.
Role conditions use field, operator, required JSON value and optional
description. Operators are the native eq, neq, contains, not_contains,
starts_with, ends_with, matches, in, not_in, gt, lt, gte, lte,
exists and not_exists. Edits permit caller-supplied resource.*,
environment.timestamp, projected principal id/tenant/name/email/type/enabled/roles,
factory-allowed principal.attributes.<key> and exact factory condition_fields.
Dynamic ${...} value references obey the same allowlist. Other principal
attributes cannot be queried through a condition. Role views flag
conditions_redacted=true if any factory-owned conditions are outside this scope.
Do not store credentials in condition literals or descriptions.
Outputs expose defined role/principal/permission fields, including permission effects,
and omit arbitrary metadata and non-allowlisted principal attributes. Credentials
are never returned. Native workspace/store persistence and hooks remain factory-owned;
there is no implicit cross-process RBAC-store cache-coherence guarantee. The Core
manager now serializes decisions with mutations, invalidates cached grants after
changes, denies disabled principals/roles, gives explicit DENY precedence over
direct ALLOW, and treats a plain resource name as its actual resource type.
Shared ancestors in role diamonds are valid; actual cycles still fail. Native
checks apply inherited role conditions. Permission inventories/helpers are not
context-dependent authorization; use check for conditional access decisions.
Decision caching is limited to native unconditional role evaluators. Attribute,
ownership, time, custom or conditional-role evaluations run fresh; evaluator
registry edits invalidate cached decisions. An evaluator failure denies access
instead of authorizing from another evaluator. Cache identity components remain
separate when IDs/resources contain colons.
Native file and SQLite stores preserve DENY effects and role conditions across reopen. Ordinary ALLOW records retain legacy strings; DENY records use structured permission objects. Older Core readers fail on these objects and need upgrading. Already-lost DENY/condition information in older records cannot be inferred by this change and needs operator review. File stores reject corrupt records without overwriting them, serialize before atomic replacement, and invalidate mutated in-memory caches after failed persistence. Local tests include fresh native stores and actual approved SQLite worker close/reopen; remote stores remain unverified.
plugin_services_open uses private plugin_services bindings. Its factory yields
PluginRuntime containing one native PluginManager and explicit alias maps for
plugin classes, native configs, custom hooks, providers, reporters,
capabilities and check_factories. The model cannot submit import paths,
distribution names, Python, arbitrary constructors or callback objects.
plugin_services_execute supports:
cataloglists factory-selected class aliases.loadtakesaliasand calls nativeload_from_classwithout activation.activate,deactivate,enable,disable,unload,statusandhealthoperate on that loaded alias. Native states and boolean health are retained; an unloaded alias must be loaded anew.reloadtakes a loadedaliasandreload_strategy="graceful"|"immediate". The factory must supplyPluginRuntime.reload_factories[alias], a callable returning a fresh native class; no code, path or loader is accepted on the wire. It performs native teardown/setup/activation with binding guards before each admission phase. Results includereloaded,loaded,rolled_back,native_failedand redactederror_type. Failed setup restores a fresh old-class instance only after cleanup is confirmed; external effects are not rolled back. Signed bindings still require explicit signature renewal. This approved command does not start automatic file watching.portslists native capability/provider/check-factory names and reporter contract versions.provider_register,reporter_register,capability_registerandcheck_factory_registerregister only the matching factory-provided alias object. Matchingprovider_unregister,reporter_unregister,capability_unregisterandcheck_factory_unregisterremove manager-local port lookups. Removing a provider/capability does not close a shared resource; its factory still owns cleanup. Reporter removal only removes a global constructor if this manager registered it and it has not been replaced by another owner.hook_registertakes a factory-provided callbackalias, optionalpriority(0–10000, default 100) andhook_type(defaultcustom). Built-in native lifecycle events are supported; optionalPluginRuntime.hook_typesmaps additional public event aliases to operator-selected native event names.hook_unregisterremoves the selected handler/event.hooksreports metadata, without callback reprs.hook_firekeeps the native legacy result list for JSONpayload; a None value cannot distinguish success from a failed hook.hook_fire_detailedreports per-hook success/result/error type andnative_failed, never exception bodies. This is not external-delivery proof or automatic retry authority.validateuses the same manager's registeredcheck_aliasesand exactly one retainedasset_idor registeredprovider_id. Factories implement nativeCheckSpecFactory.build_check_specs(); legacy native validator classes/instances use Core suite compilation. Providers return nativeDataAssetobjects. Suite name, result format, parallel/max-workers and pushdown are explicit closed fields. CoreScanPlanner/ValidationRuntimedo the actual work, with native before/after/ issue hook outcomes retained separately from data-quality verdicts.render(alias,run_id)consumes an actual retained canonical run through the registered v3 reporter. It also accepts a canonical validationresult_idinstead ofrun_id. Reporter class/constructor or configured object stays factory-owned; no template/output path, constructor settings or renderer executable is accepted. Rendered text/bytes and report-hook outcomes are retained in a bounded JSON artifact.run_get,run_exportandrun_releaseinspect, export or free a retained native result. Use the returned opaqueplugin_run_...handle, distinct from its native canonical run ID. At most 16 runs are retained within the remaining byte budget.run_exportcreates a standard canonical validation artifact that works with existing reporters andstore_services_execute(command="save",result_id=...). Releasing a native run does not delete already-exported immutable artifacts.provider_inspect(alias)reads selected provider asset dimensions/backend.
The new native PluginManager.validate/validate_async and render/render_async
provide these same manager-local workflows directly in Core. Async variants await
actual hook results (including decorated async functions) while preserving the
calling thread for native provider/kernel operations. Sync variants reject a
running event loop before effects. Kernel-port validation creates no implicit
context/workspace persistence; configured providers/reporters own their resources.
Validation hooks receive immutable CheckSpec intent in checks/validators,
and issue hooks receive the corresponding check intent even when a native validator
name differs from its alias. Hook failures are visible, non-vetoing outcomes; they
are not converted into successful None results by the detailed API. Hook return
values crossing MCP must use the existing lossless JSON value grammar.
Retained asset/result inputs bind the owner's authorization lifetime: releasing,
expiring or revoking any consumed input closes the owner and its dependent artifacts.
All owned artifact reads synchronously reauthorize the native policy and binding,
including while a watchdog is unavailable; a background tick is not the access gate.
Native set_execution_guard rechecks the binding before provider/factory/report
operations and each hook; revoked authorization prevents later handlers. Plugin
unload still tears down/removes owned resources when an unload hook is denied.
Configured reporter instances remain manager-local and cannot corrupt the global
constructor registry. Normal unload retains {loaded:false}; native cleanup
failures add native_failed=true and a cleanup receipt with deactivation,
teardown completion, removal, hook failure and exception types, without
exception bodies. The underlying Core unload_plugin(name,detailed=True) returns
PluginCleanupResult; legacy calls keep their None return. A callback completing
is not proof that a remote provider released every resource. Trusted extension code is not an OS sandbox, and a started
external effect still has no exactly-once delivery guarantee.
Each operation requires exact approval and sensitive-result consent. Registrations apply to this resident manager and do not silently rewrite durable MCP binding files or other workers' registries. Native plugin callbacks own unregister/teardown behavior; release reaps the worker and cannot promise third-party cleanup. This contract does not expose arbitrary discovery, hot reload, credential retrieval or remote security control. Those broader native extension/admin surfaces still require separate work.
Native execution engines and general stores¶
Direct Python execution engines copy their configuration and cached result values.
Row limits and offsets reject negative, boolean and non-integer values. A native
limit=0 returns an empty value/frequency result; limit=None with an offset has
no hidden one-million-row cap. MCP retains its explicit 1–1,000 result limit.
SQL value pagination uses the provider limit/TOP/ROWNUM syntax; offsets without a
limit work on SQLite and use provider-specific forms elsewhere. SQL row order is
not stable without an explicit source ordering; SQL Server offset pages order by
the requested column. Remote-provider pagination remains unverified on live
services. Polars and Pandas filtering preserve native parse/source exceptions;
MCP still returns only its closed error codes. Frequency aggregation does not
reserve a user column named count or __truthound_count. Configuration row
budgets are positive integers, and explicit to_numpy_safe(max_rows=0) is rejected.
execution_services_open resolves private execution_services bindings. Its
approved async factory yields truthound.mcp.execution_services.ExecutionRuntime
with a native BaseExecutionEngine, optional predicates and queries alias maps
of complete operator-owned SQL strings, and optional native Polars expressions.
Factory configuration owns connections, backend/cluster setup and final provider
shutdown. No caller SQL, Python, import string, connection URL or expression source
is accepted. All commands use binding_id, service_id, command and an exact
approved idempotency_key. The engine starts at engine_id="root".
execution_services_execute delegates row/null/distinct counts, completeness,
statistics, quantiles, value counts, column/sample/distinct values, aggregations,
regex/range/set/duplicate operations and cache clearing to the native engine.
count_matching, count_not_matching and filter_by_condition use predicate_id;
execute_query uses query_id with no interpolation. Query statements can have
operator-defined effects: approval must cover the complete selected statement.
Native unsupported operations remain failures rather than fallback algorithms.
inspect projects base execution limits and public aliases, excluding provider
configuration. Value counts are {value,count} rows, preserving non-string keys.
sample, filter_by_condition, and native Polars select(columns),
filter(expression_ids) and with_columns(expression_ids) retain actual child
engines, at most 16. Reuse the returned engine_id, and release it when finished;
root release uses resources_release(service_id). Native SQL filtering now builds
a lazy subquery view, with nested filters and samples borrowing the parent connection.
Parent/factory teardown follows child cleanup. A limit of 1–1,000, further bounded
by operator max_items, controls value/sample/table/NumPy results. to_numpy
rejects a row count above the requested limit; to_numpy_safe delegates native
sampling. Native engine limits still apply. to_polars returns bounded rows from
the native lazy frame. NaN, timestamps and other supported values use the existing
lossless tagged-value encoding. Local Polars/SQLite and actual local Dask, Ray and
Spark SDK tests do not verify remote SQL or remote cluster deployments.
Install truthound[execution-dask,mcp], truthound[execution-ray,mcp] or
truthound[execution-spark,mcp] for the selected Core 3.3.4 operator-managed runtime. Spark also requires an operator-provisioned Java 17+ runtime and a
correct Python worker interpreter; those are not installed by pip. Distributed engine
commands include native partition counts, repartition/coalesce with 1–1,000
partitions, metrics/clear, and bounded Arrow conversion. Native Dask supports
persist, selected-column engines, alias-selected native_filter, head/tail and
compute; no model scheduler address, path or query expression is accepted.
Ray ports expose native materialize, selected columns, take and factory-owned
row_filters selected by filter_rows(row_filter_id). Spark ports expose native
persist/unpersist, factory-configured checkpoint and alias-selected SQL returning
a child engine. Unsupported backend commands remain explicit failures. Factory
cleanup owns shared client/cluster shutdown; releasing a child does not shut down
a shared cluster. Persist/checkpoint can write to operator-configured storage and
are included in exact approval effects. Full compute/Arrow conversions preflight
the native row count against limit; select/sample first for larger inputs.
Arrow output preserves the native Arrow schema, including backend index columns.
Metrics expose counts and failure flags without native error/warning text.
Use the approved root shutdown command when native teardown completion must
be acknowledged. It closes retained children, the root engine and its factory
before returning; a teardown error or deadline expiry cannot become success.
The same shutdown approval key replays its retained result. Other engine calls
then fail with invalid_handle; release the service resource afterward.
resources_release remains bounded forced cleanup and alone does not prove that
every native factory finalizer completed. Process supervision records owned
descendants while the driver is alive, preserving cleanup ownership after driver
failure. No global Ray/Spark stop command is used.
Ray and Spark samples return min(n, row_count) rows using randomized native
ordering; this can require a shuffle. Ray get_stats.count excludes null values.
Spark distinct aggregation uses the same non-null semantics as its
count_distinct method; Ray's native distinct list includes null. Spark quantiles
retain native approximate rank semantics with relative error 0.01, accepting
integer probability endpoints. Ray exact median reuses Core's Polars bridge and
its configured conversion row bound; it is not an unbounded distributed median.
Spark uses native median. PySpark's legacy MEMORY_ONLY_SER name maps to its
serialized MEMORY_ONLY level; unknown native storage-level names are rejected.
Standard NumPy integer, boolean, string, bytes and float scalars up to 64-bit are normalized to exact Python values before existing tagged-value encoding. Large unsigned counts and NaN/Inf remain lossless. Arrays, complex/temporal scalars and extended-precision floats are not silently coerced. Distributed FIRST/LAST now select the corresponding operation rather than a row-count fallback. Their order is the backend's native execution order; this creates no global ordering contract for unordered distributed input. Actual Dask tests cover partitions, aggregation, null/empty first/last, selected filters, persist, Arrow and child cleanup.
store_services_open resolves private store_services bindings to an approved
async factory yielding truthound.mcp.store_services.StoreRuntime. It selects a
native BaseStore, CachedStore, BatchedStore or ReplicatedStore, optional records alias map
of native persistence ValidationResult/ExpectationSuite objects, data_assets
aliases and a fixed version actor. The factory owns backend, namespace, serializer,
compression, credentials, native policy/wrapper configuration and store close.
VersionedStore is a native BaseStore subclass; its config reflects the wrapped
backend. No connection or storage path can be supplied through the tool.
Factory-selected cache/batch wrappers may be nested. The known wrapper primary chain is checked down to its native backing store; cycles and chains deeper than 64 wrappers are rejected. Authorization guards cover every selected cache, batch and replication wrapper in that chain. The factory owns the backing stores and inner-wrapper teardown; acknowledged shutdown waits for that factory cleanup.
store_services_execute supports native initialization, save/get/exists/delete,
query/list/count, bounded clear, latest/history/failure reads, suite operations,
and paged iteration. Filters select data_asset_id, ISO datetimes, status, tags
and native ordering. Pagination uses limit and offset; iteration also accepts
batch_size, and Core iteration honors the exact overall limit including zero.
Record IDs are owner-local opaque references; after reopening a persistent store,
list/query it again for fresh references. Durability is the selected backend's:
this contract does not make memory stores or native in-memory version metadata
persistent.
save accepts exactly one record_id, retained canonical validation result_id,
or a declarative suite. A retained result is converted using native
ValidationResult.from_validation_run_result, with its full native persistence
payload preserved. The service retains and reauthorizes the result's input grants;
releasing/expiring/revoking that input closes the owner and dependent artifacts.
A suite selects data_asset_id and supplies a safe suite name and native
expectations/settings/tags. This is the persistence expectation DTO, distinct
from the immutable kernel ValidationSuite. Record views expose native verdicts,
statistics, timestamps, tags and expectation settings, while omitting arbitrary
record metadata, runtime environment, issue messages/details and physical asset
identifiers. Version views omit operator metadata/authors/messages; native diffs
return structural paths/types without value payloads or private metadata paths.
Versioned stores support version_history, version_info, latest_version,
count_versions, diff, rollback, versioned get/exists/delete and save with
expected_version/message. Cached stores expose native invalidate/all, refresh,
warm, get-many and statistics. Batched stores expose add/start/stop/flush/statistics;
save remains the native immediate write. Background batch writes recheck the
pinned binding before effects. Pending native batches are not a durable journal,
and crash/failed/uncertain flush is not proof of persistence. Wrapper-specific
operations reject incompatible stores rather than bypassing wrapper semantics.
Filesystem stores support native rebuild_index.
Native RetentionStore adds retention_cleanup(dry_run=true), info, statistics,
history and estimate. Setting dry_run=false needs exact approval for the native
policy's delete/archive/compress/tier action. Result projections preserve counts,
dry_run, error_count and native_failed, without native error text. Core cleanup
history/statistics include preview runs; their counters are not independent proof
of actual deletion. The native retention schedule runs on store operations, not an
MCP-installed daemon. RetentionStore.config reflects its wrapped backend.
Native TieredStore adds list/info/statistics, tier_move(record_id,tier_id) and
tier_run(dry_run=true). StoreRuntime.tiers maps public aliases to every configured
native tier name. Preview and actual migrations return opaque record references,
public tier aliases and native counts/failure counts. Physical tier metadata and
error messages are omitted. Placement durability follows the configured native
metadata store, whose default is memory.
ConcurrentFileSystemStore adds concurrent_apply(actions,dry_run=true) and
concurrent_stats, and supports its native rebuild_index. Each of 1–1000 actions
has kind="save" or "delete" and an opaque record_id; save selects an actual
factory record or an already stored native validation DTO. Preview fills and
rolls back the native buffer, without validating serialization or performing the
pending file changes. dry_run=false commits within this single exactly approved
call; locks are not retained between MCP calls. Receipts report native buffered
or applied operation counts, not an independent proof of full crash atomicity.
Statistics contain aggregate lock values, without backend paths.
Native file locks now span the associated index mutation. Index commits reload committed state under the interprocess lock, preserve disjoint owners' changes and reject conflicting additions. Corrupt indexes/WALs retain evidence and fail; valid WAL recovery starts from existing committed entries. Disk commit failure blocks reuse of the uncertain index owner. Lock detection follows incompatible thread wait cycles; ordinary contention is allowed to wait.
A native batch context rolls back uncommitted buffers on exit. A failure before
index commit stops later operations and restores touched files while their locks
remain held. An uncertain index commit or incomplete restoration retains private
.batch-recovery-* backups with original hashes and a file mapping manifest;
the selected store and a reopened store are quarantined pending explicit operator
reconciliation. MCP records outcome_unknown, closes that owner and does not
replay it. concurrent_recovery_list(limit,offset) delegates native recovery
inspection and returns session-owned opaque recovery_id, phase and file count.
The worker keeps physical directories, original hashes and the inspected state
fingerprint private. concurrent_reconcile(recovery_id,resolution) requires a new
exact approval: rollback restores original bytes and removes originally absent
files; keep_current retains actual files, including partial effects, and
reconciles only their index entries. It does not resend missing saves. Native
list_batch_recovery and reconcile_batch(...,expected_fingerprint=...) recheck
manifest/backup integrity, safe paths and the current file fingerprint under the
batch-owner and affected-file locks. A changed state requires fresh inspection
and approval; failure retains quarantine and evidence.
The MCP extra includes filelock so default cross-platform store recovery has a
real process-locking provider. Install truthound[stores-concurrency] in the
operator environment to select both filelock and portalocker explicitly.
Native filelock zero/nonblocking timeouts remain zero; portalocker uses its actual
lock API with bounded polling, unique shared-reader descriptors and complete open
failure cleanup. Fcntl retains its lock-file inode after release so queued and new
owners cannot split ownership across different files. Actual local provider
contention and three-process owner handoff are tested; Windows runtime proof still
belongs to the final platform CI.
One stable native batch-owner lock fences publication, execution and recovery.
Live owners block reconciliation; interrupted owners release their OS locks.
preparing receipts precede backup copying and all file changes, so an explicitly
resolved preparing batch leaves current data intact. Completed data/index state
moves atomically to a private .batch-resolved-* namespace before any backup
deletion. A crash during deletion leaves usable data and a cleanup_pending
entry; concurrent_recovery_cleanup(recovery_id) separately approves the remaining
cleanup through native cleanup_batch_recovery. Cleanup fingerprints are also
pinned privately. Native no-op locking cannot provide process fencing and is
rejected for recovery. Manifest reads are capped at 16 MiB and 100,000 entries.
Pending entries count affected data files; cleanup entries count remaining evidence
files. These are explicit reconciliation and cleanup controls, not automatic
multi-file crash atomicity or an external-delivery guarantee. Actual local process
crashes before index commit, after index commit and during cleanup are tested;
power-loss durability and remote backends are not established.
ObservableStore uses the same factory-owned context, audit backend, metrics and
tracer for ordinary store operations. Native initialization now delegates through
BaseStore correctly, save/query operation counters increment once, and shutdown
attempts audit, tracing and owned metrics-server cleanup even if another close
fails. Underlying backend close failure also runs observability cleanup.
audit_query accepts optional record_id, audit.start_time, audit.end_time,
audit.event_type, limit and offset. Native ISO timestamp comparison rules
apply. The bounded scan requires offset + limit <= 10,000. Results include native
timestamp, event type, status, duration and opaque record reference; actor/tenant,
physical IDs, trace IDs, summaries, arbitrary metadata and error text are omitted.
metric_read(metric_id) selects StoreRuntime.metrics aliases to trusted
StoreMetric(name,labels,kind="counter") queries. The native in-memory or
Prometheus backend returns its actual counter/gauge scalar; names and labels stay
private. These queries do not expose arbitrary metrics or raw Prometheus exports.
observability_flush calls native audit/tracer flushing; it does not prove remote
export delivery. Factory-owned exporter configuration and external services remain
unverified.
MigratableStore supports migration_status, migration_validate(record_id) and
migration_run(dry_run=true,batch_size=100). Batch size is 1–1000; callbacks,
registry, target/minimum versions, retry policy and required backups are fixed
by the approved factory. Status retains native version distributions, estimated
migration need, sampled failures and in-memory migration counters. Validation
checks the native migration path; preview does not run custom migration callbacks
or prove their output is valid. An actual run processes the native store and can
partially commit before a failure or timeout. Receipts retain counts, dry-run and
success verdicts, with native_failed and error counts; raw record IDs, errors and
callback data are omitted. No automatic replay is added.
Core persists migration versions in ValidationResult.metadata, so reopening a
store does not apply a completed migration again. Required backup failure or a
false migration validator prevents rewriting the original; lazy write failures
and incomplete queries propagate rather than appearing successful. Original
backups remain readable and are excluded from later migration passes. Existing
conflicting backup identities fail instead of being overwritten. The native
set_io_guard callback rechecks authorization before callbacks and writes.
Lazy reads may themselves create backups and rewrite data under factory policy;
those effects are included in exact operator approval. Backup storage is native
backend state; migration counters remain process-local, and this is not a
transactional all-store rollback or a durable migration scheduler.
Native ReplicatedStore adds start/stop/flush/statistics and full synchronization,
optionally selecting replica_id. StoreRuntime.replicas must alias every configured
target. Save uses native save_async; sync/semi-sync replica failure raises after
the primary may already have been written. This remains outcome_unknown in MCP,
closes the owner and cannot safely replay. Native batch/full-sync failures stay
failed, and failed full sync leaves the target unhealthy for read routing. Native
stop/flush drain instance-owned async work and queued writes. set_io_guard
checks authorization inside native primary/replica I/O, including background
replication and health checks; retries cannot bypass revoked authorization.
Native retry and best-effort delete policies still apply. Async queues are memory,
not durable receipts; successful primary save is not a replica delivery guarantee.
Statistics use public replica aliases, including lag and success-rate map keys.
Local temporary-store tests exercise these effects and partial-write quarantine.
They do not establish actual cloud replication, regions, provider credentials or
cross-process metadata recovery.
Bound tenant administration¶
tenant_services_open uses private tenant_services bindings whose async factory
yields truthound.mcp.tenancy.TenantRuntime: one native TenantManager, a public
tenants alias map to fixed native IDs, and optional settings aliases to native
TenantSettings objects. The factory owns store, resolver, quota tracker, hooks,
physical database/schema names and notification configuration. This administers
the selected Core manager and does not grant MCP data access or change its consent.
tenant_services_execute repeats binding/service/key and needs exact approval:
cataloglists tenant/settings aliases withlimit(1–1000, default 100, policy bounded) and nonnegativeoffsetper collection. Other calls select atenant_idalias; physical IDs and tenants outside this map are refused.get,delete,suspend,activateuse the native lifecycle; suspend accepts optionalreasonup to 4096 characters. Delete also invokes native usage reset and the factory's configured lifecycle hooks.create/updatetake nonemptypatch:name,owner_id,owner_email,isolation_level, quota settings and operatorsettings_id. Creation requires a name and generates its slug;slugis editable through update. Optional createtieruses native defaults.set_tierrequirestierand optionalupdate_quota(default true), preserving the native choice to reset quotas to tier defaults.patch.quotaaccepts partial native quota fields: validations per day/hour, rows per validation, storage bytes, API calls per minute/day, checkpoints per day, maximum retention days/concurrent jobs/users andcustom_limits. Integer values are bounded to -1..2^63-1; native tracker semantics determine enforcement. Partial create quotas extend native tier defaults; updates preserve other fields.quota_checkandusage_trackrequire a nativeresourcename and positiveamount(default 1, maximum 10^9). Check uses the native decision; track records usage and does not implicitly perform a quota-checked workload.usage_summaryselectsresourceandwindow(hour,day,month, default day), returning native counters/limits/flags with the public tenant alias.contextenters/inspects/exits one native tenant context inside this worker. It does not persist a tenant context into other owners. Resolver integration and dataset row isolation require their separate explicit operations below.
Views expose defined tenant status/tier/owner/quota/time fields, omitting arbitrary metadata, physical storage names, settings endpoints and credentials. No model callback/import/path/store configuration is accepted. Native persistence is provider-selected: local tests cover actual SQLite owner close/reopen, status, quota and scoped context, including suspension metadata restoration. Usage tracker state is independent; the default memory tracker ends with its process. Native zero/unlimited tenant quotas do not report an exceeded summary. File/remote tenant stores and distributed trackers are not established by this SQLite evidence.
Native rate budgets and redacted secret administration¶
rate_limit_services_open uses private rate_limit_services bindings whose pinned
async factory yields truthound.mcp.governance.RateLimitRuntime. It owns a native
RateLimiter, public-to-native subjects aliases, optional opaque contexts,
native policies/quotas aliases, and the same PolicyRegistry/QuotaManager
instances supplied to the limiter. Storage, extractors and configurations remain
operator-side. These budgets do not change MCP rate limits or consent.
Every rate_limit_services_execute request repeats the binding, live service ID,
exact idempotency key and command, with its own approval:
cataloglists aliases;configprojects native scalar settings without callbacks.acquire/peek/resetrequiresubject_id; callers cannot supply native keys. Acquire takestokens(1–1000000, default 1).wait=truerequirestimeout_secondsin (0, 10]; nonwaiting requests omit it. Optionalcontext_idselects an operator object for native policy evaluation. Nativepeeksemantics apply; it is not a simulation of consuming tokens through every policy.quota_registertakes an operatorquota_id. Registered quotas then supportquota_check,quota_consume,quota_usageandquota_reset, withsubject_idandquota_id; check/consume usetokensas the amount. Missing registration is refused. Consumption returns native usage; exceeded consumption can raise a native error and quarantine the handle rather than report a successful mutation.policy_register/policy_unregisterselectpolicy_id.policy_resolveselectscontext_idand returns native match/configuration with public policy aliases.
Decisions expose native allowed/remaining/limit/reset/retry/utilization and native HTTP headers. Native bucket keys and arbitrary policy metadata are omitted. Exact successful replay returns its retained artifact and does not consume twice. State persistence is the selected Core storage/manager's semantics: memory budgets end with their owner; durable/distributed recovery is not supplied by MCP receipts. Local tests use native memory storage and quotas; remote Redis/Memcached are unverified.
secret_services_open uses private secret_services bindings yielding
truthound.mcp.governance.SecretRuntime: a native SecretManager, fixed public
reference aliases to native SecretReference objects, and provider aliases to
SecretProviderSlot(provider, name, priority). Factories own physical keys,
provider endpoints, versions, default values and credential resolution. Model
requests cannot supply these fields or secret values.
secret_services_execute supports catalog, resolve(reference_id),
cache_clear (all or selected provider_id) and provider_add/provider_remove
for an existing provider_id. Resolution performs the native lookup but returns
only resolved, and when present, expired, created_at, expires_at. It never
returns value, hash, length, physical key/provider name, URI or arbitrary metadata.
resolved describes native lookup/cache state, not proof that credentials are
still accepted by an external service. Native consumers resolve/use credentials
inside operator-selected factories; raw get_value/resolved-config output is
not exposed through this transport. Close clears the selected manager's caches;
the factory owns external client cleanup. Other owners' caches are independent.
Both catalogs accept limit (1–1000, default 100, policy bounded) and nonnegative
offset per collection. All calls require sensitive-result permission and exact
approval. Provider failures never return native exception text and do not replay
effects automatically. Core also emits a generic lookup warning without provider
exception material; non-strict absent providers return None. Tests use synthetic
local providers, verify actual cache hits/clearing and factory cleanup, and do not
read production environment secrets or invoke cloud secret services.
Native schema audits¶
schema_audit_contract retains the native document JSON Schema. Pass strict
UTF-8 JSON as document_json to schema_audit_create; this produces an owned
document artifact. The overall operation input limit remains 64 KiB. Unknown
fields, duplicate keys, invalid references and wrong document kinds fail.
schema_audit_inspect takes resource_id and returns canonical JSON and raw and
semantic digests. schema_audit_standards takes standard_id and design_id;
schema_audit_implementation takes design_id and catalog_id. All findings,
rule coverage and native verdicts are retained, including UNKNOWN. Empty or
incomplete coverage is not promoted to PASS. No database connection is made.
Optional term mappings are operator-owned entries under schema_audit in the
private bindings.json, not model-supplied approval claims. Using binding_id
requires idempotency_key and approval of the exact plan. Input digests and
the binding file identity are checked; changing the operator bindings revokes
access to the resulting artifact. Documents and reports remain subject to
session TTL, quotas, parent release and sensitive-result disclosure policy.
Native lineage graphs¶
lineage_track takes graph_id and an event with action equal to source,
transformation, validation or output. It calls the corresponding native
LineageTracker.track_* method and returns a new graph handle, retaining the
original and its ownership chain. All events take name, optional description,
owner, tags and JSON properties (which cannot override named arguments).
Sources take source_type, location, and schema_spec; transformations take
sources, operation, location, schema_spec, and column_mapping whose values
are lists of [source_node, source_column] pairs. Validations take sources,
validators and result_summary; outputs take sources, output_type and
location. Locations are metadata: nothing is opened, executed or published.
Native node replacement and omission of edges from missing source IDs are
preserved, not silently reinterpreted. Native-generated timestamps are retained.
This is manual tracking, not automatic execution history, remote emission or a
claim that the described validation actually ran.
The additional dependency event takes only source_id, target_id, and
edge_type (default depends_on), calling LineageTracker.add_dependency.
Missing nodes and cycles fail without modifying the original graph.
lineage_query also accepts view: "tracked_lineage" with node_id and
direction (upstream, downstream, or both, the default), or
view: "tracked_path" with source_id and target_id. These call the native
tracker query methods without reinterpreting their edge-direction behavior;
an absent path, including a missing endpoint, is JSON null, not an empty graph.
All resulting artifacts retain parent ownership and revocation checks.
Large relation schemas can be provided through approved operator validator
bindings; the 64 KB tool-request limit is unchanged. Recursive relation traversal
can raise a native RecursionError on deep schemas. MCP preserves that execution
failure and canonical evidence rather than reporting a successful validation.
Parity tests normalize only repeated traceback frame counts that differ with
interpreter call-stack depth; exception types, messages and frames remain checked.
lineage_load accepts an approved local .json path and optional native
config (without persist_path). It captures an immutable, size-bounded file
snapshot before calling LineageGraph.load; imported graphs are limited to
1,000 node records and 2,000 edge records. Invalid/empty native files fail rather
than becoming successful empty graphs. Both loading and exporting require
sensitive-result permission. lineage_export takes graph_id and returns an
owned application/json artifact with the exact UTF-8 bytes produced by
LineageTracker.export_to_json. This is native JSON, not the tagged-value
format of lineage_query. Use paged artifacts_read, Base64-decode and concatenate
the bytes; preserve them when writing a file for later native/MCP re-entry.
There is no caller-selected output path or implicit filesystem publication.
Origin permission revocation and parent release invalidate derived access.
lineage_catalog describes all native config/node/edge fields and rich record
definitions. lineage_create accepts config, nodes and edges; caller
persist_path is rejected. lineage_edit takes graph_id and a discriminated
edit: add_node, remove_node, add_edge, remove_edge or clear. Edits
produce a new graph handle and leave the source immutable.
lineage_query takes a discriminated query containing graph_id, view and
view-specific fields. Views are graph, nodes, edges, roots, leaves,
topological, node, has_node, node_edges, upstream and downstream.
Traversal depth and edge direction are explicit inputs.
Impact tools take graph_id, node_id and optional native impact_rules.
lineage_impact adds max_depth and include_validations;
lineage_schema_impact adds new_schema. lineage_dependency_chain instead
takes source_id and target_id. All queries/analyses retain full native
artifacts. Hypothetical deletion never deletes the graph or external data.
lineage_impact defaults to view="full". view="summary" delegates to
native ImpactResult.summary(), critical to get_critical_nodes(),
by_level requires level=none|low|medium|high|critical, and by_type requires
the native node_type (for example validation). Selectors not applicable to
the chosen view are rejected. Each call analyzes the captured graph anew with
the same depth, validation-inclusion and impact-rule settings; it does not
filter a previously stored result. Text and node lists remain owned sensitive
artifacts, and releasing the source graph revokes them. For example,
{"graph_id":"graph_<token>","node_id":"a","view":"by_level","level":"high"}.
lineage_render takes graph_id, renderer=d3|cytoscape|graphviz|mermaid,
closed native RenderConfig fields in settings, optional root_node_id and
direction=upstream|downstream|both. For subgraphs, settings.max_depth controls
the traversal. It returns an owned JSON (D3/Cytoscape) or text (DOT/Mermaid)
artifact. Partial graphs preserve native graph insertion order across processes.
For example, {"graph_id":"graph_<token>","renderer":"mermaid","settings":{"orientation":"LR","theme":"dark"}}.
output_format="native" preserves these JSON/text outputs and supports the rooted
subgraph selectors above. Whole-graph output_format="html" delegates to native
D3, Cytoscape or Mermaid HTML. Mermaid also supports output_format="markdown"
and optional title. Graphviz supports output_format="svg"|"png"; install
.[mcp,lineage-visualization] and a system dot executable. Other format/renderer
pairs and rooted HTML/image requests are rejected. Width and height for document
and image requests are bounded to 1–4096; retention uses the existing byte budget.
No caller output path or browser launch is accepted. Graphviz images execute the
local native renderer; the server does not fetch remote resources. HTML includes
interactive client code and public library URLs which a browser may fetch when
an operator opens it. Artifacts require the sensitive-result grant, and releasing
the graph revokes their reads. Labels remain data and distinct node identities
are preserved across diagram formats. Native D3 fixed layouts retain deterministic
positions; native Cytoscape honors selected sizes. Subgraph depth inherits native
RenderConfig.max_depth unless explicitly overridden; full-graph documents do
not select a subgraph. Tracker context/history sessions and OpenLineage delivery use their separately
approved owners described below.
Persistent contexts and operator approval¶
suites_create retains a version-1 intent, not executable Python. Each check has
a unique check_id, concrete validator, closed settings, optional name,
category, tags and metadata. schema_id may reference a retained schema;
schema-only suites are supported and reserve check ID schema. Releasing that
schema revokes its dependent suites and results. evidence_policy and
severity_policy use the native kernel policies. Execute with suites_validate
and asset_id/suite_id; these are explicit kernel suites, not auto-suite
baseline inference. Persistent execution additionally requires context_id,
an idempotency key and exact operator approval.
contexts_execute takes context_id, idempotency_key and a discriminated
operation object. Supported action values include resolve_source_key,
resolve_fingerprint, track_asset, get_or_create_schema_baseline,
read_applied_suite, write_applied_suite, persist_run, persist_docs,
metric_get, metric_put, metric_append_history, list_runs, list_docs,
list_baselines and list_applied_suites. Use capability_describe for the
exact action fields. Even reads require approval: constructing the native
context can create directories or quarantine corrupt state. Persistence accepts
only owned canonical validation artifacts; applied-suite actor metadata does not
grant approval. Releasing a context handle does not delete its user workspace.
Workspace permission is separate from input permission. Start the server with
--workspace-dir /absolute/project --control-dir /absolute/private-control as
well as --allow-dir. Create the control directory outside both data and
workspace roots; on POSIX it must be user-owned with mode 0700.
contexts_open reads project configuration or an explicit typed config, without
creating .truthound. Open a context, then request contexts_initialize with
context_id and an idempotency_key of 8–128 characters. Alternatively supply
both fields to assets_validate to execute and persist a real Core project run.
The response initially contains an awaiting_approval plan. A human operator,
outside the agent's MCP tools, reviews the exact request and then approves it:
truthound-mcp review-plan --control-dir /absolute/private-control --plan-id plan_<returned-token>
truthound-mcp approve-plan --control-dir /absolute/private-control --plan-id plan_<returned-token>
The dedicated CLI also provides revoke-plan. Approval binds operation, request,
session, grants, workspace state, effects and expiry. Retry the identical tool
request only after approval. Changed grants/workspace or forged consent fail
closed. The control directory contains private approval/signing state, not a
public artifact; do not share it or expose it through MCP. It is an application
authority boundary, not protection from a hostile process running as your user.
Persistent validation calls the real Core context, including configured catalog,
baseline, run, metric-history and docs effects. A committed intent replays its
receipt rather than writing again. If its old session artifact expired, the
receipt explicitly reports artifact_available=false; it does not recreate the
run. Timeout/cancellation after effects begin is outcome_unknown, never a
successful rollback or permission to retry. Automated crash reconciliation is
not yet implemented. Retained validation's aggregate quality remains unknown
until complete target/skip accounting is established; canonical Core outcomes
remain available in the artifact.
Reviewed native validator bindings¶
An operator may place bindings.json in the protected control directory (on
POSIX, user-owned mode 0600). This is executable configuration, not an agent
tool or a file in an admitted data directory. For example:
Use checks=[{"validator":"regex","binding_id":"capital-a"}] and an
idempotency_key with assets_validate. The agent cannot override fixed
settings or approve the resulting plan. Review and approve with the CLI above.
Requests/receipts retain constructor digests rather than raw SQL or settings.
Approval binds the exact template bytes, grants, session and effects. A changed
or revoked binding denies dependent artifact access. Replay preserves the
authorization/configuration identity without granting new disclosure rights.
Non-JSON inputs use installed Core plugins, not pickle or imported model text.
The operator declares a plugin's installed distribution, exact version,
truthound.plugins entry-point name, file sha256, public capability mapping
and optional plugin settings in plugins within the same protected file.
Obtain its digest without importing plugin code:
Validator templates can supply typed callable fields with
{"operator_capability":"approved-capability-id"}. Their separate
object_parameters map can bind a constructor field to an approved native
capability object such as a complete Polars expression, dtype mapping or
reference frame. Fixed JSON, object parameters and caller settings cannot
overlap. Ordinary JSON objects are never interpreted as executable references.
No extension imports occur before approval. An owned worker verifies the
distribution/version/files again and uses Core PluginManager load, activation
and shutdown. Discovery/automatic installation are disabled. Package changes
block execution; uncertain effects are not replayed as success. Editable
packages and binary-only entry points are unsupported. Approved extensions run
with the host user's privileges; this is not an OS sandbox or a transitive
dependency lock. Review extension code and data/network access before approving.
Approval does not make an object type-correct for its constructor field.
Ordinary malformed JSON settings are rejected before execution; an approved
native object can instead be accepted by a Core constructor and fail during
validation. A captured Core execution failure remains core_execution_failed,
with its canonical result preserved; retrying its idempotency key does not
execute the extension again. This is distinct from constructor rejection or
uncertain effects, and approved-object error coverage does not establish
ordinary-input algorithm error coverage.
Discovery reports full_core_parity=false: the implemented operations and
approved native services below have scoped regression evidence, rather than
exhaustive algorithm, configuration and provider equivalence. Native tracker
contexts/history/rendering, checkpoint services, streams, stores, plugin lifecycle
and durable jobs are exposed through their documented contracts. Read each
service's configuration, persistence, recovery and cleanup limits; a registered
operation does not establish every provider's behavior. Live external services
and recovery after forced owner loss require their own evidence. No new remote
server, credentials or paid calls are introduced.
Parity evidence separates constructor rejection, native execution failure and
exceptions caught by an algorithm. For example, Core query/expression validators
may encode a query exception as a critical quality issue rather than an
ExecutionIssue; MCP preserves that native distinction and the complete issue.
Constructor rejection across every registry entry does not prove every
algorithm-specific runtime error branch.
Evidence also distinguishes native exceptions swallowed without a reported issue. Tests observe the real exception in the exact native method, with a nonexceptional control and no injected failure, then compare complete Core/MCP results. Such behavior is not relabelled as an execution failure or used to claim data quality passed; aggregate quality remains unknown. This is separate from exceptions returned as critical quality issues.
Claude Desktop extensions¶
Desktop ordinary chat and Claude Code have separate installations. Install a verified Local or Core .mcpb bundle, then select an existing, persistent data directory in its configuration. Avoid temporary directories that may disappear after a restart. Core workspace, operator control and sensitive-result grants are optional and remain off unless explicitly approved.
The 3.3.2 bundle builder uses distinct extension IDs (truthound and truthound-core) so Local and Core can coexist, and includes a compact PNG icon. Older bundles may share the same ID. Previously published 3.3.1 files remain unchanged. Start at Styxis Plugins for the current Desktop bundle links.
After installation, restart Desktop and check both the connector's tool list and an actual call against approved synthetic data. A successful MCP handshake alone does not prove that Desktop accepted the tool schemas. If Core tools fail to load, check that every advertised input schema has an object root, including recursive model definitions. Keep their nested validation constraints intact.
Remove a duplicate manual mcpServers entry only after identifying the same connector. Do not uninstall the Codex or Claude Code plugin to repair Desktop.
Blank optional workspace/control settings may arrive as literal Desktop placeholders. The launcher treats only those exact placeholders as absent grants; an unresolved required data directory is rejected.
Plugin ownership and permission storage¶
The Truthound repository owns Local/Core packages, runtime, permissions and product tests. Styxis Plugins is the user catalog. The former product catalog is an empty compatibility marker; development/CI installs from an ephemeral catalog generated by the verifier.
Local permissions live at ~/.truthound-openai/permissions.json, Core permissions at ~/.truthound-core/permissions.json. Updating does not grant new access. Change grants through the packaged setup skill and reconnect Core after changes. Paths, column names and aggregates may reach the host model provider. Neither full Core parity nor official directory approval is claimed.
Registry metadata¶
The official MCP Registry entry io.github.seadonggyun4/truthound provides server discovery metadata. A listing does not grant directory access or guarantee client support. Install in Codex CLI and Claude Code through Styxis Plugins. Registry publication and Python package release are product-maintainer operations.
Extending the adapter¶
The MCP CI matrix keeps its 15-minute job limit and partitions the entire required test collection into four deterministic, disjoint Linux Python 3.11 shards. Other selected OS/Python pairs run platform boundary tests, with two disjoint partitions on Windows and one elsewhere. Their evidence is aggregated before installation and cannot substitute for the full semantic collection. Packaging starts only after every shard succeeds. The aggregate gate compares the full collection and source hashes, checks all setup/call/teardown phases and JUnit results, and rejects missing, overlapping, skipped or failed executions. Shard completion alone does not establish complete validation.
Keep policy in truthound.mcp.policy, summaries in projection, Core invocation
in worker, bounded request execution in service, and SDK registration in
server. Introduce new tools with explicit input/result contracts and fault
tests; add providers separately rather than accepting arbitrary URLs, Python or
SQL through a file tool. See the design rationale.
Approved native streaming stores¶
In the Core profile, streaming_store_services_open uses an approved installed capability returning StreamingStoreRuntime(store, runs={alias: StreamingRun(run_id, data_asset, metadata)}). The store must be a native StreamingValidationStore; this group is separate from general BaseStore bindings. Add its alias-to-capability mapping under streaming_store_services in operator bindings. Backend URLs, credentials, codecs and physical run identities stay in the factory.
streaming_store_services_execute explicitly provides inspect, session_create/open/resume/status, writer_open/write/write_run/flush/close/abort/status, reader_open/read/reset/close/status, chunks_list, chunk_get, chunks_delete, finalize, stats and filesystem-only storage_size commands (use their underscore spellings, such as session_create and writer_write_run). Every operation requires approval and returns a bounded private artifact. run_id selects a fixed run alias; session/writer/reader/chunk IDs are owner-local references. writer_write accepts at most 1,000 typed persistence validator records; writer_write_run consumes a pinned canonical validation artifact and rejects runs with execution issues that a verdict-only stream cannot preserve. Writer/reader creation accepts asynchronous=true for native async wrappers. Reads and chunk listings are paged with a bounded limit; listings also accept offset.
Explicit writer_close and finalize flush pending records; writer_abort stops without flushing. Release, expiry or input revocation first aborts open writers, so cleanup does not write new records. It cannot undo an already-started I/O or recover a buffer never written to storage. A fresh approved owner can session_open the same fixed run and explicitly session_resume an incomplete stream. Native inconsistent recovery state fails closed and is retained; no automatic adoption or replay is offered. Finalization returns the native persistence result projection and can materialize the full result in memory. Installed local factory tests exercise filesystem and actual SQLite round trips; real S3/other database services remain unverified.
Filesystem and file-SQLite recovery_status(run_id=alias) returns an owner-local recovery_id and confirmed/unconfirmed/missing chunk counts instead of sensitive paths or file hashes. Separately approved recovery_reconcile(recovery_id=..., resolution="adopt"|"discard") checks the retained fingerprint under the native lock and applies only that explicit selection. Success invalidates earlier session/chunk references; reopen with session_open. A changed or corrupt confirmed prefix is never reported as recovered.
File-SQLite recovery holds the real run lease and BEGIN IMMEDIATE while committing row deletion and session updates together. Changed fingerprints or inconsistent confirmed chunks fail closed; a failed update or process loss rolls back the transaction. Recovery inspection applies the session max_memory_mb bound. session_release, chunk_release and recovery_release free owner-local references without deleting persisted data.
Native S3 streaming uses conditional manifest ETags, create-only chunk requests, receipt checks before writes and owned multipart aborts. Paths, ETags and upload IDs stay private. Local protocol doubles cover uncertain completion receipts and cleanup failures; live S3 and actual SDK deployment verification remain separate.
writer_status observes buffer/closed/metric state without flushing. reader_status metrics describe I/O records/bytes and can include prefetch, so they may exceed the returned page count. A serialized or encoded chunk exceeding the session read-memory bound is rejected before backend writes, retaining the buffer.
The same opaque streaming-store recovery_status/recovery_reconcile contract also delegates to approved S3 factories. Model requests never receive object keys, ETags, SHA fingerprints or multipart IDs. Failed S3 discard retains a blocking manifest journal and needs a fresh recovery reference and explicit discard. A whole-run deletion marked pending_resolution=delete needs a new approved chunks_delete call. Actual filesystem/SQLite process-loss tests and worker replacement over a synthetic S3 object protocol remain distinct from live S3 verification.
General database-store bindings expose pool_status, pool_recycle and root shutdown through store_services_execute. Pool status projects native state and numeric metrics without connection URLs, SQL or provider messages. Recycling uses the selected native pool. Approved shutdown stops owned background work and closes the factory before acknowledging; failure or expiry cannot become teardown success. After shutdown, the same approval key can replay its result, other commands fail with invalid_handle, and the service resource can be released. Actual SQLite pooled/legacy factories persist data across recycle, shutdown and a fresh approved owner. SQL read failures remain failed/unknown operations rather than successful empty artifacts.
tenant_services_execute(command="isolation_apply") requires a fixed tenant_id
alias, pinned asset_id, optional selected columns, and bounded offset/limit.
It delegates to TenantManager.apply_isolation and returns a private row page;
returned_rows describes that page and truncated indicates another row. Only
shared/row-level strategies apply to arbitrary captured assets; schema/database
provenance cannot be inferred from those assets. Tenant activity is enforced.
These operations require sensitive-result consent and exact approval; tenant
administration does not enlarge the asset's data grant. Input release revokes
the service and derived artifacts.
Factories may supply TenantRuntime.policies mapping aliases to native
RowLevelPolicy callbacks. policy_catalog, policy_add, policy_remove and
policy_update(policy_patch={enabled, priority}) select the tenant's native
row-level strategy; policy edits never accept model code. A nonempty update may
change either field, with priority bounded to ±1,000,000. Policy names and physical
tenant IDs stay out of metadata views. Policies are owner-local configuration,
not SQLite tenant metadata persisted by these commands. Default Core policies
deny nonboolean verdicts and policy errors, and run after the tenant predicate
but before projection/page limits. The module guide documents the explicit
native fail_closed=False behavior.
For operator-owned custom rules, an approved profiler factory may expose native
SchemaCompatibilityAnalyzer instances through ProfilerRuntime.schema_analyzers.
profiler_services_execute(command="schema_compare", analyzer_id=..., baseline_id=...,
current_id=...) pins both asset inputs and delegates to the selected analyzer.
An analyzer-only factory is supported. Callbacks, strict mode and declared
nullability configuration remain factory-owned; requests cannot import rules.
The result preserves the four native compatibility fields. Failed rules produce
no compatible result, and input release revokes the resident owner and derived
artifacts. Execution guards run before normalization and around each native rule.
Native ML monitor lifecycle¶
ml_services_open selects an approved MLRuntime factory through private
ml_services bindings. Fixed model aliases select native configuration and
asset-column mappings; fixed rule/handler aliases select native code and delivery
configuration. ml_services_execute provides registration, pinned-asset
prediction/reference/feedback, periodic start/stop, manual collect/evaluate,
metrics/dashboard, rule/handler add/remove, explicit uncertain-buffer discard
and acknowledged shutdown. The model monitoring guide
documents every command, collector window and failure contract.
Every operation requires approval and sensitive disclosure consent. Starting
includes future periodic collection, alert evaluation and retention deletion
until stopped or revoked. Inputs and derived artifacts share the owner's grant
lifetime. Outputs use public model/rule/feature aliases and finite metric fields;
native IDs, arbitrary alert messages and handler endpoints are withheld. A failed
native_monitor_incomplete artifact does not prove external effects absent and
is never automatically resent. Default buffers/history remain in memory; process
loss recovery is not claimed. Local installed tests use native in-memory storage
and local handlers; actual external services remain unverified.
Native storage codec and backpressure utilities¶
store_utilities_open resolves private store_utilities bindings to an approved
StoreUtilityRuntime factory. Native compression/encryption pipelines and
backpressure strategies remain factory-configured; keys, providers, stage code
and physical slots are not caller inputs. store_utilities_execute explicitly
captures pinned asset/artifact bytes, retains/processes/reverses/exports/imports
payloads, saves/loads/deletes fixed private slots, controls native pressure
metrics/admission/release/reset and acknowledges shutdown. The store utility
guide documents commands,
budgets, binary format, persistence and failure semantics.
All operations require approval and sensitive disclosure consent. Native stage guards prevent later execution after revocation, and input release invalidates the owner and derived artifacts. Incomplete pressure acquisition remains a failed artifact and cannot be retried until explicit native reset; denial is a valid boolean decision. Encryption restore rejects selected-plan/header mismatches before stages execute. Payloads and metric projections exclude key material and provider diagnostics. Local native tests exercise actual codecs and cryptography; no live external key store or provider verification is claimed.
Ray factory configuration now applies concurrency through a native task pool,
use_actors/actor_pool_size through an actual callable-class actor pool, and
target_max_block_size to the selected Dataset's context. Actor mode rejects a
simultaneous task-concurrency setting. Batches, prefetch and pool/block sizes
validate their numeric bounds and actual boolean mode. This does not reconfigure
an already initialized shared Ray cluster's CPU/GPU capacity. Querying native
num_partitions on a lazy Ray child materializes that selected dataset to observe
its real blocks; it does not return a planned estimate. Partition observation can
therefore execute the factory-selected source plan. Local SDK tests exercise
actual actors and task overlap; remote cluster behavior remains unverified.
Storage utility key bindings now support native create/get-or-create, redacted status/history, versioned rotation/deletion, selected-cipher application and manager-wide expired-key cleanup. Key material and physical key IDs never enter replies. Applied keys are revalidated before codec use; unknown effects latch the manager aliases without automatic replay. See the key persistence contract.
Distributed plans and validator execution¶
ExecutionRuntime factories may bind plans (aliases for native
AggregationPlan objects), validators (aliases for Core Validator objects)
and a fixed adapter_config (AdapterConfig). inspect lists these aliases.
aggregate_plan selects aggregation_plan_id; aggregate_stream additionally
accepts batch_size (1–100,000) and the bounded limit. validate selects
validator_ids (1–32 aliases) and optional columns on a distributed engine.
Caller-authored plans, SQL filters, validator constructors and callbacks are not
wire inputs. Existing approval, disclosure consent and owner lifetime apply.
Plans execute on local Polars/Pandas/Arrow, Dask, Ray or Spark data. Core's registered aggregators merge native worker states; averages and standard deviations merge accumulator state rather than partition summaries. Filters are fixed SQL expressions evaluated by Polars inside each worker batch. Global results map aliases to values. Grouped results retain every typed key:
aggregate_stream returns independent cumulative snapshots and complete.
complete: false means the response limit truncated the stream; its last
snapshot is not the final aggregate. The driver checks owner authority before
requesting another batch and after observing it. Previously submitted native
work may already be running. State memory still grows with group cardinality;
batch_size is not a total memory sandbox. Distinct counts store exact values,
including colliding hashes, up to max_sample; exceeding that bound raises an
error instead of returning a truncated count. Null values are excluded from
column distinct/count; count("*") includes every row. Empty mean/std results
follow the registered Core aggregators (0.0). Duplicate result aliases fail.
DistributedValidatorAdapter uses exact built-in NullValidator and
NotNullValidator native counts and their Core issue builders. Detailed failure
samples/counts/complete rows use the bounded Arrow-to-Polars bridge when needed.
A custom validator can explicitly implement
validate_distributed(engine, columns=None) -> list[ValidationIssue]. Names and
categories never authorize substituting validation logic. AUTO runs other
validators through full conversion when row count is at most native_threshold,
and sampling above it. Forced NATIVE rejects a validator without a native port.
FULL_CONVERSION and SAMPLE_AND_LOCAL run the actual validator. Requested
columns apply to both. Parallel execution honors max_parallel, preserves
validator order and drains submitted work before propagating failure; use
parallel_validators=False for validators requiring serial access.
Sample issues retain observed counts and structured percentages. When sampling
omits rows, each issue adds sampling: {total_rows, sample_rows, seed}; counts are
not extrapolated. A sampled child is released even when validation fails, while
a sample that reuses the root does not take ownership of it.
Dask sampling returns exactly min(n, row_count) rows, using distributed random
ranks and a seed tied to partition number. Dask applies the configured scheduler
to compute/persist, honors disabled result caching, and performs Arrow conversion
once. An engine-created distributed client is closed on context exit; a borrowed
client remains caller-owned. Child engines borrow the parent's client and must
finish before that parent closes. Selected persist_intermediate persists on
construction. Ray's nullable grouped counts use a copied Dataset with native
hash shuffle, preserving typed null equality and the caller's shuffle context.
Actual local Ray 2.48 and current-SDK checks cover this compatibility path;
external clusters and services remain unverified.
Storage utilities also select native adaptive, chunked_compression and
chunked_encryption codecs, with codec_analyze/codec_recommend for fixed
adaptive configurations and codec_range for selected plaintext byte ranges.
Header/index/size/checksum failures propagate; v2 encrypted chunk indices are
authenticated before plaintext is returned. See native codec configuration and
format contracts.
Incremental stream_open/write/read/flush/finalize/abort/status/release commands
use approved StoreCompressionStreamBinding/StoreEncryptionStreamBinding
factories. Failures and unconfirmed native context cleanup fence further work;
shutdown aborts unfinished streams. See incremental codec sessions.
Ordinary filesystem store factories honor JSON/safe-YAML and the selected native
codec, capture config objects and publish records/indexes atomically as separate
files. Corruption is an error; rebuild_index validates records before explicit
reconciliation. Memory factories honor capacity/copy/default metadata and remain
owner-local across shutdown. See the local store contracts
for single-writer boundaries, identity rules, startup checks and legacy names.
Pandas engines borrow the supplied mutable frame. After changing it, call
clear_cache() to invalidate the materialized Polars conversion as well as
computed results. cache_results=False converts the current frame on every call.
Ray get_stats() computes row/valid counts, mean, sample standard deviation
(ddof=1) and extrema with one native multi-aggregation, including all-null and
typed empty columns. NaN behavior follows the native SDK. A missing reduction
result for a non-empty dataset fails instead of being cached as zero statistics.
clear_cache also invalidates native Ray/Spark row-count memoization, and
cache_results=False bypasses Core result caching. Ray global COUNT/SUM/MEAN/MIN/MAX/STD/VAR/NULL_COUNT reductions
without explicit per-operation parameters reuse exact cached column statistics
and memoize results, including a shared MIN/MAX pair. clear_cache()
invalidates both. Explicit parameters such as ddof=0, grouped/filtered plans, FIRST/LAST,
distinct counts and the bounded median conversion retain their own execution; COUNT(*) remains a
row count and cached null results stay null. Native dataset persistence is
separate from this cache. AggregationPlan.to_spec() captures independent
aggregation parameters, group columns and the fixed SQL filter; grouped/filtered
specs use the same Core state aggregation as plans. Dask plans/specs retain the
selected engine scheduler; standalone DistributedAggregator also accepts a
dask_scheduler keyword for an explicit scheduler/client callable. Local Polars
and Arrow table plans do not require pandas.
Distributed engines apply a positive num_partitions at initial construction.
An explicit child repartition/coalesce keeps its requested partition count;
sampling, filtering and projection do not reapply the root default. Partition
counts reject booleans, zero and negative values. Spark captures the supplied
configuration and applies both True and False Arrow/adaptive flags to its
selected session; extra Spark settings override these fields.
An approved AggregationPlan(scope=AggregationScope.PARTITION) and native
AggregationSpec with that scope finalize each native partition separately:
{"partitions": [{"partition_id": 0, "result": {"x_sum": 3}}]}. IDs are observed
iteration ordinals, not durable identities across executions or repartitioning.
Filters and optional groups apply within each partition; an empty partition has
the registered aggregators' empty result. Ray partitions are native blocks;
Ray's batch API
uses whole blocks when batch_size=None. Internal batches still merge states
inside each block. Partition-plan streams return independent cumulative lists
of completed partition results, and the usual complete truncation contract
applies. Factories select these plans for MCP through the existing plan aliases.
Approved ordinary S3 factories now apply native format/compression, metadata and
object tags, conditional record/index receipts, error-preserving queries and
explicit rebuild_index. Shutdown closes the owned client. See S3 recovery and
provider contracts;
local synthetic/botocore checks do not establish live cloud verification.
ExecutionMode.EAGER materializes the selected native graph during engine
construction and applies to derived engines too. Dask persists it and, with a
distributed client, waits for its selected futures and propagates task errors
without gathering full partitions into the driver; timeout_seconds bounds this
initial wait. This wait does not prove remote task interruption on timeout.
persist_intermediate alone retains Dask's usual asynchronous submission behavior.
Ray retains a native MaterializedDataset; context exit drops the engine's own
materialized reference, while borrowed materialized datasets remain caller-owned.
Spark persists and counts immediately, then unpersists only cache created by this
engine on exit, with blocking cleanup. An already persisted input stays owned by
the caller. Explicit unpersist releases the selected cache. Default LAZY keeps
deferred graphs. MCP inspect.config includes selected execution mode, initial
partition default, Arrow batch size and metrics flag without cluster addresses.
Factory-owned distributed configuration now applies partition_strategy on
initial partitioning and explicit repartition. ROW_HASH uses the selected
partition_columns, or all columns when none are supplied. COLUMN requires
explicit keys and hashes equal keys together. ROW_RANGE uses ordered native
ranges in Spark/Ray; Dask supports one range column, plans bounded per-partition
key samples using the selected scheduler, and distributes/sorts rows in native
Dask tasks without dropping nulls. Its driver samples grow with input partition
count times requested partition count. Spark may produce fewer range partitions
for small keyspaces; num_partitions reports the observed count.
ROUND_ROBIN starts at zero within each input partition/block and distributes
rows cyclically; it is not a durable global row ordering. CUSTOM requires an
operator-owned partitioner(data, count) returning the same native data type and
schema and the requested partition count. Invalid outputs fail. MCP never accepts
caller partitioner code. Ordinary projection/filter children keep their native
partitioning. coalesce uses each backend's native reduction independently of
the shuffle strategy. Range planning and native schema/partition inspection may
read source keys or execute native graphs even in LAZY mode. Native Ray
partition aggregation uses per-block Arrow references so empty blocks keep their
empty results; raw block values stay in Ray workers.
Streaming S3 recovery_status also reports durable multipart intent/receipt
counts. multipart_reconcile consumes a retained recovery_id and optionally
receipt_id, an alias in factory-owned StreamingStoreRuntime.multipart_receipts
(private exact-key→upload-ID mappings). Unknown creation receipts require that
operator-verified alias; known receipts can be cleaned without it. Inspect lists
aliases only. The approved command aborts scoped uploads, preserves unconfirmed
cleanup, returns a new recovery reference and counts, and withholds provider keys,
upload IDs and fingerprints. It does not adopt/discard completed chunks. See
durable multipart ownership.
Approved ordinary GCS factories now use native generation receipts and raw stored-byte reads through the shared Core object-store state, including explicit index rebuild and actual client shutdown. See GCS generation and recovery contracts.
Approved ordinary Azure factories use the same Core inventory/recovery path with
native conditional ETags and stored-byte decoding. Blob-header metadata and
persisted record_metadata have separate roles. get_access_tier reads the
selected record's observed tier/status; set_access_tier requires access_tier
(Hot, Cool, Cold, Archive) and returns acknowledged plus the observed
state. This is not a rehydration-completion receipt. These commands require an
unwrapped selected Azure store; client input cannot supply credentials, endpoints
or executable callbacks. Shutdown closes the owned client and default credential.
See Azure recovery and ownership.
Native engine checkpoints and Arrow cursors¶
For Dask and Ray, checkpoint_dir selects an operator-owned local directory.
checkpoint() writes native Parquet into a unique staging directory, flushes the
files and SHA-256 inventory, and publishes a completed snapshot by directory
rename. The returned child exposes its local checkpoint_path in Python.
Ray creates its local destination directory and explicitly selects Arrow's local
filesystem for native writes and reads, including Windows drive paths.
The execution-ray, all and dev extras exclude Ray 2.59.0 because that SDK's
local-path resolver probes an unavailable optional Azure filesystem on Windows.
This dependency restriction also applies outside CI; the native computation and
checkpoint assertions are unchanged.
DaskExecutionEngine.from_checkpoint(path, config, client=...) and
RayExecutionEngine.from_checkpoint(path, config) validate the complete inventory
before opening the native reader. Pending directories, corrupt files and wrong
backend receipts fail. Failed writes retain their pending directory for inspection;
closing an engine does not remove checkpoint data. Treat completed snapshots as
immutable while readers use them. The manifest detects accidental corruption;
it is not an authentication signature. This local protocol does not establish
remote filesystem or object-store transaction guarantees.
checkpoint_enabled=True now requires a nonempty checkpoint_dir and executes
checkpointing at root construction, after selected repartitioning. Derived
projection/filter engines do not automatically checkpoint again. Dask/Ray reopen
the committed native data; the source graph need not rerun. Spark delegates to
its native reliable DataFrame checkpoint and selected SparkContext checkpoint
directory. Spark's checkpoint is part of its native context lifetime, not the
portable Dask/Ray manifest format. MCP checkpoint retains the native child for
all three engines; physical paths stay in the installed factory. A subsequent
factory can explicitly reopen a selected Dask/Ray snapshot after worker restart.
See Dask native Parquet publication.
iter_arrow_batches(batch_size=None) yields typed native batches and supports
explicit iterator close(). Dask computes one partition at a time, Ray uses
native batch iteration with configured prefetch, and Spark uses its local row
iterator without partition prefetch. Empty Dask streams preserve their schema;
Ray normalizes null-only batches to the dataset schema. STREAMING selects this
path for Dask/Spark Arrow conversion; Ray uses it in every mode. APIs returning a
complete Arrow table or Polars frame still retain that complete result. Native
partition allocation can exceed a batch's row count, and this mode does not start
an unbounded broker or Spark Structured Streaming query. See the
Ray iterator lifetime
and Spark local iterator.
Approved MCP arrow_stream_open accepts batch_size and returns an opaque
stream_id. arrow_stream_read requires that ID and the same engine_id, applies
limit to rows, and returns rows and complete. A page ending exactly at the
last row may need one more read to observe exhaustion. arrow_stream_close
releases the cursor; releasing its child engine or shutting down also attempts
cleanup. At most 16 cursors can remain open. Reads check the binding before and
after native iteration and enforce the private artifact byte budget. Cursors are
owner-local; a failed or lost consumed read is not replayed as a fresh iterator.
Native computation entry points share a cooperative timeout_seconds deadline
across nested calls. Late results raise DistributedOperationTimeout and clear
result caches. Arrow cursors check the deadline around every batch and close on
failure or early exit. A blocking SDK call can continue until it returns; this
policy does not kill borrowed workers or promise a hard wall-clock limit.
retry_count allows additional attempts only when RetryableExecutionError
propagates from code explicitly declaring the computation safe to repeat.
Ordinary exceptions, timeouts and uncertain side effects are not automatically
retried. Native frameworks can wrap worker exceptions and have their own task
retry policies. Checkpoint/write/cluster-lifecycle operations retain their native
failure semantics. memory_fraction bounds each accepted Arrow batch against that fraction of
available driver memory observed at iterator start. Oversized batches raise
MemoryError before being yielded. This cooperative transfer check occurs after
native allocation, does not reserve memory, and does not cap worker heaps, native
partition buffers, prefetch or complete collected results. Configure those through
the selected backend. The execution extras include psutil for this observation.
Selected resolver contexts and verified SQL sources¶
TenantRuntime.contexts binds aliases to private context dictionaries. The worker
copies them on entry and on each resolution. resolver_catalog lists aliases;
resolve(context_id) invokes the native resolver and returns an allowed tenant
alias or resolved: false. A resolution outside the tenant alias map fails.
resolve_or_create(context_id, tenant_id, default_name) resolves once and checks
the selected native ID before lookup or creation through Core's
TenantManager.resolve_or_create(..., expected_tenant_id=...). Header resolver
required=True rejects missing headers; malformed IDs, including trailing
newlines, are rejected. Headers, tokens and resolver code are not wire inputs.
TenantRuntime.sources binds aliases to factory-owned
truthound.multitenancy.sources.TenantSQLSource. source_catalog lists aliases;
source_read(tenant_id, source_id, columns?, offset?, limit?) reads a selected
source and returns a bounded page under sensitive-result consent and exact
approval. The native wrapper borrows a SQLiteDataSource or DuckDBDataSource, binds one
native tenant ID, and verifies the actual reading connection before and after
fetching rows. SQLite uses a savepoint. DuckDB uses a transaction on the actual
reading cursor because its cursor is an independent connection; temporary table
shadowing is rejected. DATABASE isolation compares the actual database file
with the tenant's absolute database_name; SCHEMA isolation requires both the
native schema name and the wrapper's explicit absolute namespace_database.
For PostgreSQL, DATABASE compares current_database() to the tenant database
name; SCHEMA additionally requires the factory-pinned namespace_database name.
Metadata and bounded ONLY table reads share one repeatable-read, read-only
transaction on an idle factory-owned connection. Views, foreign tables, temporary
relations and inheritance parents are rejected. An existing caller transaction is
rejected rather than committed or rolled back. The factory must close connections.
MySQL treats schemas as database namespaces: both isolation modes compare the
actual DATABASE() and configured database with the native namespace. SCHEMA
also requires a pinned namespace_database. Only ordinary InnoDB tables are
allowed. The actual cursor uses a read-only repeatable-read transaction and pins
table metadata before reading; views, temporary-table shadowing, other storage
engines and existing caller transactions fail closed. Rollback covers owned
read transactions only. Disposable test servers are initialized without system
configuration, and only their own processes/data directories are cleaned up.
SQLite query_timeout bounds native SQLite VM work during each borrowed connection scope, including execute and fetch. Expiry raises the native interrupted-query error; cleanup removes the progress callback before returning the connection to its pool. A new operation receives a fresh deadline. SQLite lock waits still use timeout; Python UDF execution cannot be preempted by this callback. This does not establish timeout or server-side cursor support for other SQL providers.
MySQL selects the native unbuffered SSDictCursor when use_server_side_cursor=True; default reads retain DictCursor. Exhaust or close each cursor before reusing its connection. query_timeout is rounded up to milliseconds and sets the session max_execution_time at connection creation. This is MySQL’s read-only SELECT execution limit, not a general write, stored-procedure or network timeout. Native tests verify cancellation on a synthetic table query; table-free SLEEP/BENCHMARK expressions did not raise cancellation and are not covered by this guarantee. Invalid or out-of-range values are rejected; unsupported servers fail connection setup instead of silently ignoring the setting.
PostgreSQL applies query_timeout to each newly created native connection as statement_timeout, rounded up to milliseconds. Nonfinite, nonpositive and out-of-range values are rejected. A timed-out statement raises the native cancellation error; autocommit connections remain reusable. Existing pooled connections retain the timeout selected at creation, so recreate the source to change it.
SQL Server uses a dedicated native connection with SQLServerConfig(autocommit=True, dbapi_driver="pymssql"). The automatic driver preference remains available as auto. Physical tenant reads require an idle connection and effective SELECT-only table permissions. The wrapper checks the actual database and schema, rejects indirect tables, acquires a serializable transaction with TABLOCK, HOLDLOCK, and restores its transaction isolation and lock timeout after rollback. Existing caller transactions are rejected without rollback. Local emulation has exercised native reads, MCP reopen and SDK shutdown; native x86 CI and the latest installed-wheel verification remain required. This does not establish cloud SQL or complete configuration parity.
Oracle applies query_timeout to the native connection call_timeout, rounded up to milliseconds. The limit covers each database round trip, not the total duration of multiple fetch calls or client-side work. Cancellation errors retain their native identity; the pool checks the native connection health before lending it. An invalid connection is closed and replaced within the original pool wait deadline, without replaying the failed statement. If configuring the timeout fails, the newly opened connection is closed.
Oracle uses an idle, non-autocommit application connection and a native read-only
transaction. DATABASE compares the actual CON_NAME PDB with the tenant database;
SCHEMA also requires the exact source owner and a factory-pinned
namespace_database PDB. Simple application identifiers and ordinary tables are
required. A SHARE ... NOWAIT table lock fences definition changes, including
ADD COLUMN, which Oracle permits under ROW SHARE. This also blocks concurrent
writes during the bounded read. Dictionary identity and native
DBMS_UTILITY.NAME_RESOLVE identity must agree before and after reading.
Views, synonyms, external/temporary tables, SYS/SYSDBA connections and existing
caller transactions fail closed. Only the wrapper's transaction is rolled back.
Recent/concurrent DDL can produce Oracle snapshot errors; these are not silently
retried. The integration fixture waits for its own seeded DDL's server-time
boundary before testing ordinary reads; product requests retain their deadline.
The actual thin-driver path is tested with oracledb==4.0.1 and a disposable
Oracle Free PDB, including SDK stdio approval, owner reopen and frontend shutdown.
Disposable local PostgreSQL, MySQL and Oracle tests cover native fencing and MCP owner restart; this does not verify a production server or another SQL provider.
This also supports operator-attached SQLite databases. The factory owns connection
cleanup; source paths and namespaces stay private. Suspended tenants, another
tenant ID, query mode, views, virtual tables, in-memory databases and unverified
remote providers fail closed. Ordinary table reads are bounded by the source's
materialization_row_limit; exceeding it fails before returning any rows. Page
limits do not replace that bound. This is a materialized read snapshot, not ongoing
revocation of data already returned. Caller-provided Arrow assets still cannot
establish schema/database provenance. Installed-worker tests cover SQLite and DuckDB owner
restart; they do not establish remote database provider support.
Profiler services now expose bound quality_scorers and quality_rules through
quality_score, quality_compare and quality_cache_clear. Inputs are pinned
assets with an optional aligned Boolean label_column; sampling/configuration
and custom rule code remain factory-owned. Quality score results include the native recommendation and usage decision, alongside metrics and the public rule alias. See quality scoring.
Bound profiler services also expose native sampling snapshots, local type-inference feedback/model persistence, and complete-profile HTML export to budgeted artifacts or fixed slots. See sampling, ML inference and visualization for configuration and limits.
Plugin owners also accept plugin_services_execute(command="shutdown") without an alias. The approved command waits for plugin and reload cleanup plus factory exit before acknowledging success. A failed cleanup remains failed/uncertain and is not replayed as a fresh teardown.
Concurrent store factories also honor captured JSON/safe-YAML, registered compression, metadata defaults and the selected expectation-store lock strategy. query.order_by="name" is available for expectation stores; tag filters and pagination apply natively. Invalid records fail queries and index rebuild rather than disappearing from successful results. Index operations are validated before WAL publication; this does not promise atomicity across record files and index files.
Approved benchmark workloads¶
Private benchmark_services bindings resolve to BenchmarkRuntime in
truthound.mcp.benchmark_services. Its workloads map contains
BenchmarkWorkload(runner, kind, target, parameters) values; kind selects
single, suite, all, category, or parity. The operator pins native runners,
suites, framework choices, parameters and artifact destinations in the factory.
The wire accepts aliases, never arbitrary imports, paths or runner keyword arguments.
Optional comparators and thresholds maps hold native comparators and thresholds.
benchmark_services_open and benchmark_services_execute use approval and
idempotency receipts. Execute repeats binding_id, service_id and
idempotency_key. Commands are inspect, run (workload_id), result,
release, report (result_id, optional format: json/markdown/html),
compare (result_id, baseline_id, comparator_id), threshold
(result_id, threshold_id) and shutdown. Comparisons require matching native
result kinds. Parity reports currently support JSON only.
Results retain native timings and verdicts. Failed measurements and empty suites
produce native_benchmark_incomplete with a retained result artifact, rather than
a successful execution receipt. Threshold checks reject failed or empty
measurements. Parameters, tags, tracebacks, detailed parity observations and
operator artifact paths are excluded from returned reports; ordinary benchmark
environment metadata remains sensitive and requires disclosure approval. Owners
retain at most 16 results within the serialized native-result budget; release removes a
result. Cancellation joins the owned native thread before normal cleanup. Forced
process loss remains an unknown outcome, with no automatic replay.
Native BenchmarkConfig rejects invalid iteration counts and nonfinite budgets.
Time and process RSS limits are checked around setup and iterations; cooldown
respects the remaining time budget. These checks do not preempt a blocking
iteration or detect every transient allocation peak. Teardown still runs on
failure. This source implementation has local synthetic workload evidence, not
a fresh release parity benchmark. Complete parity-provider verification remains outstanding.
Approved API documentation generation¶
Private documentation_services bindings return
truthound.mcp.documentation_services.DocumentationRuntime(jobs={alias: job}).
Each DocumentationJob(generator, source, kind="source") pins a native
DocumentationGenerator, its renderer/configuration and source in the installed
factory. kind="module" uses native module extraction/rendering;
kind="info" uses an operator-provided ModuleInfo or PackageInfo. Requests
cannot choose source paths, module imports, renderers or output paths.
documentation_services_open and documentation_services_execute require
approval and idempotency receipts. Execute repeats binding_id, service_id,
idempotency_key and selects inspect, generate (job_id), result
(result_id), read (result_id, file_id), release (result_id) or
shutdown. A generation retains a manifest of file IDs, relative names, sizes
and SHA-256 digests. read returns the exact generated bytes as an artifact.
The native JSON, MkDocs, Sphinx and HTML formats are selected by the factory.
Module-only output is retained as docs/module.txt without changing its content.
Generation uses an owned temporary directory, including MkDocs' sibling
configuration file. Partial native failure retains already generated files with
native_documentation_incomplete; original error messages are redacted. Empty
output is incomplete. Symlinks and nonregular files are rejected. Retention is
bounded by the service byte budget, 256 files per generation and 16 generations;
release or owner shutdown removes retained results. Cancellation joins owned
native work before cleanup. This is an in-memory artifact lifecycle, without
cross-owner recovery or automatic site publication.
Documentation content may include source text, docstrings and source links. Operators must select sources intended for disclosure; approval covers that content, not just the manifest. The byte budget limits retained artifacts after native generation; it is not a hard disk, CPU or memory sandbox for a renderer. The factory remains trusted installed code. Generated sites have not been built, installed or deployed merely because generation succeeded.
Benchmark factories can also supply BenchmarkRuntime.histories, mapping aliases
to native RegressionDetector instances with operator-selected private history
roots. history_load and history_inspect require history_id;
history_baseline restores the baseline as a retained result (or returns
available: false). history_save, history_check, history_report and
history_thresholds require history_id and a suite result_id.
history_threshold adds an already bound threshold_id. threshold,
history_check and history_report accept an optional retained baseline_id.
History comparisons with no baseline, missing benchmarks or failed measurements
produce an incomplete receipt. History reports return native text artifacts.
Saving is an approved write to the factory's private root; a new owner can restore
that baseline, without exposing the root path. The two baseline/history file
writes are individually atomic, not a multi-file transaction; uncertain writes
must not be blindly replayed. Corrupt or legacy summary-only evidence is rejected
instead of being treated as absent or successfully restored.
Pinned benchmark workloads also accept kind="generator" or "generator_lazy":
runner is a native DataGenerator, target is a captured GeneratorConfig, and
parameters must be empty. run retains the resulting frame and returns schema
and row-count metadata. data_page accepts result_id, nonnegative offset and
limit from 0 to 1000; report(format="ipc") exports exact native Arrow IPC bytes.
JSON reports describe the frame, while Markdown/HTML apply to benchmark results.
Generated frames share the owner's 16-result and memory-estimate budget, with
release/shutdown cleanup. This byte accounting does not cap transient native
generation memory. Seeded output and IPC are verified with local synthetic data;
no real PII or external baseline cohort is used by that verification.
kind="scaffold" pins a native BaseScaffold and ScaffoldConfig; generated
files use an owned output root. All paths are preflighted before writing:
absolute paths, traversal, duplicates and symlinks are rejected. Each file write
is atomic; the entire scaffold is not a multi-file transaction. Manifests also
include the executable flag. Templates require custom logic; unimplemented
datasource/action behavior raises an error.
kind="validate" pins a DocumentationGenerator and source for native
validate_docstrings. strict=True is supported only for this kind. Native
issues are retained in docs/docstrings.json; validation failure retains the
artifact with native_documentation_incomplete. These files can contain selected
source/docstring content and require the same disclosure approval as generation.
Benchmark compare and history comparisons return native_benchmark_incomplete when suite names are missing or duplicated; they do not silently compare only the intersection.
Scaffold jobs preserve native generation failures for invalid settings. The retained reporter files execute against canonical Core results: passed-check projection is shared with Core, and a zero issue limit suppresses detail rows without changing summaries.
Approved native tracker sessions and context history¶
Private lineage_services bindings return
LineageRuntime(tracker, jobs={alias: LineageJob(callback, name, operation_type)},
files={alias: absolute_operator_path}) from truthound.mcp.lineage_services.
The factory owns a native LineageTracker, its configuration and file aliases.
Callbacks are pinned synchronous (tracker, context) -> None functions; requests
cannot supply code, imports or file paths. The tracker must have no active context
when the factory yields it.
lineage_services_open and lineage_services_execute require operator approval,
a control directory and sensitive-result consent. Execute repeats binding_id,
service_id, idempotency_key and a closed command:
inspectreturns state counts and job/file aliases without private paths.tracktakes the same typedeventaslineage_track, withoutgraph_id.run(job_id)executes the callback inside nativetracker.track(...)on one owned thread. Nested/current-tracker semantics and native auto tracking apply.history(offset, limit)pages native contexts, including completed flags and redacted failures; limit zero returns no rows.snapshotreturns the graph.lineage(node_id, direction)andpath(source_id, target_id)use native queries.save(file_id),load(file_id)use pinned private graph files;clearclears graph and context history. Graph serialization does not persist history.exportreturns native graph JSON;render(rendering={...})reuses the closed renderer/output-format settings above. A native rendering failure is a failed JSON artifact, not a mislabeled image, and leaves the tracker available.shutdownacknowledges only after the owner and factory have finished.
Each response is an immutable session artifact and approval-key replay does not repeat the callback or filesystem effects. Completed history entries are snapshots of normal structured metadata. Opaque native metadata can be unsupported by the transport. Callback failures retain native partial graph changes and failed contexts; they do not imply rollback. Native graph persistence uses version fences and verifies published bytes. Explicit primary saves cannot overwrite a newer tracker. Reloading the authoritative primary file confirms its version and can resolve an unconfirmed publication; loading a separate export does not resolve primary uncertainty. A fresh owner reloads configured graph state with empty native context history. Cancellation joins the owned native thread; this is not a hard CPU sandbox or durable callback/history journal. Forced process loss remains an unknown outcome with no automatic job replay.
Expression-based validation retains each reported column's own failure samples,
value counts and full rows. Passing earlier columns and checks suppressed by
mostly do not shift evidence onto a later failure; standalone and batched Core
execution use the same association.
Core plugin¶
Core 0.2.6 pins published Core 3.3.4 with a frozen dependency lock. Use the Styxis installation and Core setup skill above. Existing grants are retained; individual execution plans still require separate trusted operator approval.
Windows Ray 2.58 owned local startup queries its existing SDK node client for the exact ALIVE node and valid manager port, avoiding the temporary GlobalStateAccessor.GetNode path that produced a native access violation. The scoped hooks are restored on success or failure; other threads and external clusters retain SDK behavior, and the startup deadline is not extended.
Owned local Windows Spark 4.2 startup uses the installed SDK launcher JAR to generate NUL-delimited JVM arguments and launches that real JVM without shell evaluation. This removes the GUID PowerShell process and the wildcard launcher class search; Spark still creates and authenticates its gateway, reads the original configuration, and performs native computation. The original 30-second reply deadline remains. External/borrowed sessions, other SDK versions and operator startup scripts/configuration keep their SDK path; scoped hooks restore on failure and do not affect other threads. Partial startup also unregisters only its captured SDK exit callback before the outer owner reaps descendants.
Native worker processes keep their JSON protocol on a separate, non-inheritable stream. OS stdout/stderr descriptors and Windows CRT/Win32 standard handles point to the private sink before an operator factory imports an SDK. Native extension, subprocess and SDK shutdown output cannot write into the protocol; deadlines and process ownership remain unchanged.
The protocol descriptor inheritance flag is explicitly cleared, including Windows standard-stream duplicates.
Owned resident reply readers poll pipe availability and stop on cancellation without waiting for another writer to close. The existing reply-size and timeout limits still apply.
On Windows byte-mode pipes the zero-size availability check uses the byte count, including when the writer stays open.
Distribution catalog¶
Styxis Plugins is the installation entry for Local and Core. It maintains the current client commands, plugin versions and fixed source commits. Claude Desktop extensions are separate from Codex and Claude Code plugins and are linked from the same entry.
Refresh the Styxis catalog, update the selected plugin and reconnect. Updates preserve existing grants. Approve data directories through the setup skill; installation alone grants no access. The Local tool names remain check_file, profile_file and scan_pii.
Execution uses local stdio. First installation may download the pinned runtime; offline use requires those dependencies to be available already. Verification of tool discovery and calls does not establish Agent selection accuracy or full Core parity.
The MCP Registry provides discovery metadata. Its publication status is separate from Git push and plugin installation. See the 3.3.1 release history and 3.3.4 release status for package and verification details.