30 lines
5.9 KiB
Markdown
30 lines
5.9 KiB
Markdown
# 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.
|
|
|
|
```text
|
|
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.
|