Files
ThothII/docs/contracts/workspace-preprocessing-cli.md
T

5.9 KiB

Workspace preprocessing CLI contract

thothctl workspace is a closed native host interface. Only the seven paths below are representable; there is no passthrough, shell, arbitrary child argv, or host-selected bootstrap run ID.

workspace inspect --workspace ID [--json]
workspace preprocess dwh --workspace ID [--resume RUN] [--json]
workspace schema suggest-fks --workspace ID [--from-sql FILE]... [--assume COLUMN=TABLE]... [--output FILE] [--json]
workspace schema check --workspace ID --resume RUN [--annotations FILE --reviewed-candidates sha256:HEX] [--json]
workspace index-schema --workspace ID [--json]
workspace preprocess evidence --workspace ID [--dry-run] [--resume RUN] [--json]
workspace preprocess run --workspace ID [--resume RUN] [--json]

IDs are lowercase [a-z][a-z0-9-]{2,62} and outer run IDs are exactly 32 lowercase hexadecimal characters. --from-sql accepts at most 32 canonical regular non-symlink files, each at most 1 MiB and 16 MiB total. --assume accepts at most 256 values of 256 bytes, each matching column=table. Annotations are one canonical UTF-8 file at most 16 MiB. Existing output, links, hardlinks, directories, replacement races, and reparse points are refused with a generic unsafe-file error.

The bounded schema-v1 stdin envelope is exact: omitted fields are not equivalent to explicit zero, empty, or null fields. Its fields are, per operation (in addition to the always-required schemaVersion:1, operation, and workspaceId):

  • inspect: no additional fields.
  • dwh: resume only when --resume RUN was supplied.
  • run: resume only when --resume RUN was supplied.
  • evidence: resume only when supplied, and dryRun only when --dry-run was supplied.
  • suggest-fks: sql only when one or more --from-sql files were supplied, and assume only when one or more --assume values were supplied. Each sql item is exactly {basename, contentBase64, sha256}.
  • check: resume is required; annotations and reviewedCandidates are either both present or both absent. annotations is exactly {basename, contentBase64, sha256}.
  • index-schema: no additional fields.

No other fields, duplicate JSON value, host path, raw SQL, or raw annotation content are accepted. SQL and annotations use a logical basename, base64 bytes, and a declared sha256:<hex> digest. The request envelope is independently capped at 24 MiB (after JSON/base64 encoding), enough to carry the frozen 1 MiB-per-file/16 MiB aggregate raw ingress bounds; every supplied field/value must exactly match the command-derived envelope. Candidate responses may carry the internal hostExport (mediaType, sha256, contentBase64) only for suggest-fks; its object is strict (unknown fields rejected) and is always verified, even without --output: YAML media type (application/yaml or text/yaml), UTF-8, digest, and decoded size at most 700 KiB. It is removed from the public result and written exclusively only after result/run/identity validation. Output publication uses restrictive mode 0600 and refuses existing leaves, links, hardlinks, directories, replacement races, and reparse points. On Linux, publication uses an anonymous O_TMPFILE inode and linkat(..., AT_EMPTY_PATH); the link operation is the final commit, and no post-commit check can turn success into a not-published error. On Darwin, the named-stage implementation is a trusted-parent mode: the target parent and ancestors must remain namespace-stable and same-UID stage mutation is explicitly outside the threat model. It verifies the stage identity/link count before using renameatx_np(..., RENAME_EXCL) as the final no-replace commit. It does not claim protection against a same-UID hostile hard-linker. On Windows, the stage is held open with DELETE|WRITE and zero sharing, then renamed atomically with SetFileInformationByHandle(FileRenameInfo) rooted at the retained parent handle; replacement is disabled and the rename is final.

The public result has schema version 1 and only these fields: status, code, workspace/revision/descriptor/operation identities, optional run and child run IDs, completed stages, counts, artifact identities, and warnings. Revisions/descriptors are 40-hex; run IDs are 32-hex; artifact digests are sha256:<hex>. Allowed statuses are succeeded, unchanged, dry_run, blocked, and failed. Allowed codes are ok, workspace_not_found, workspace_not_activatable, binding_missing, preprocessing_conflict, preprocessing_resume_mismatch, manual_review_required, evidence_materialization_required, effective_config_mismatch, semantic_index_incompatible, annotation_invalid, egress_policy_refused, and registry_bootstrap_recovery_conflict. succeeded, unchanged, and dry_run require ok and child exit 0. blocked requires one of manual_review_required, evidence_materialization_required, preprocessing_conflict, preprocessing_resume_mismatch, or registry_bootstrap_recovery_conflict, and child exit 3. failed requires a non-ok operational code other than those blocked-only codes, and child exit 1. A nonzero child exit is never accepted for another status/code combination. The public thothctl exit mapping is fixed independently of child details: 0 for succeeded/unchanged/dry-run, 3 for an expected blocked result, 2 for command grammar or unsafe host-file failures, and 1 for operational failures (including invalid child envelopes, output-limit failures, and child execution failures). Stdout is capped at 1 MiB after final human/JSON encoding and stderr at 64 KiB after sanitization; output is never allowed to exceed those bounds. In human mode, a registry_bootstrap_recovery_conflict result prints exactly Bootstrap recovery is ambiguous or corrupt; inspect the installation registry jobs. and prints neither a candidate export nor any run/candidate ID. Compose is invoked only as compose run --rm --no-deps --no-TTY workspace-maintenance ...; output never includes child stderr or secrets.