docs: focus public documentation on product usage

This commit is contained in:
2026-08-26 10:15:07 +02:00
parent 23bc2f6555
commit a54d4769dd
67 changed files with 290 additions and 9963 deletions
-245
View File
@@ -1,245 +0,0 @@
# `tht pi` lifecycle contract
`tht` is the only component that drives Docker lifecycle operations. The `core` container
does not mount a Docker socket, and Pi is never updated in a running container.
## Inspection and configuration
```text
tht pi status
tht pi doctor
tht pi test
tht pi logs
tht pi configure
```
When `--installation` is omitted, `tht` first uses `THOTHII_INSTALLATION` and otherwise
discovers one valid `thothii-installation.yaml` in the current project tree, including an immediate
`deploy/*` directory. Use `--installation /absolute/path/thothii-installation.yaml` as an explicit
override when the descriptor is outside that tree or more than one installation is available.
`status` executes the image-bundled `pi --version`. `doctor` compares that value with both the
container's `PI_VERSION` contract and the `io.thothii.pi.version` image label; a merely nonempty
version is not sufficient. `doctor` and `test` also require a healthy core, a successful Pi smoke,
valid settings, and an exact selected provider/model pair from the backend's available model
entries. `pi check` remains an alias for `pi test`. Logs are always a bounded, sanitized 200-line
snapshot; there is no follow mode.
On a TTY, `pi configure` presents numbered provider, model, and thinking choices. Providers and
models come from the backend's closed model list, and the model choices are restricted to the
selected provider. In non-interactive use, all choices must be explicit:
```text
tht pi configure \
--provider zai --model glm-5.2 --thinking medium
```
The helper snapshots exact settings-file existence and raw bytes, applies the new values atomically,
and verifies the readback and rendered-configuration digest. A helper, readback, or digest failure
restores those exact bytes when the prior file existed; on a clean installation it removes the new
file and verifies the absent/default state. Empty prior files are supported. The command reports
the actual host path from `PI_AUTH_FILE`; credentials remain in that protected host file and must
never be passed as flags.
Installation-managed Pi provider/model configuration is declarative only. Any JSON value beginning
with `!` is rejected recursively in the complete `models.json` before it can supply management
choices, and the exact selected provider/model and credential payload is checked again before the
isolated smoke files are written. The API returns only the fixed
`Pi provider/model configuration is invalid` message; rejected commands, paths, and secrets are
never included. Use `$NAME`/`${NAME}` environment references in `models.json`, or omit `apiKey` and
provide the selected credential through the protected `PI_AUTH_FILE`, `THT_MODEL_API_KEY_FILE`, or
`THT_SECRETS_FILE` contract. A literal leading exclamation mark uses Pi's `$!` escape. Direct
secret-file references are not a `models.json` feature: ThothII converts its managed key source to
the provider-native child environment, while `PI_AUTH_FILE` is mounted as Pi's protected credential
store.
## Supported Compose entry points and current image
Use `tht start`, `stop`, `status`, `logs`, and `doctor` for ordinary installation lifecycle
operations. All `tht` Compose commands automatically include the installation-specific
durable selector when it exists:
```text
<projectDirectory>/.tht/<installation-id>/current-image.yaml
```
This selector is part of the supported installation state: it keeps a verified Pi image selected
across a fresh `tht` process, stop/start, reconcile, and source checkout whose base image is
digest-pinned. Do not delete or hand-edit it. Direct raw `docker compose` lifecycle commands bypass
this protection and are unsupported. Advanced documented Compose rendering must use
`scripts/compose-with-preflight.sh` and include the same selector with `-f` when present; connector
secret overrides must never bypass that preflight wrapper.
## Reloading Pi configuration
Configuration reload is a separate lifecycle operation from an image update:
```text
tht pi restart --yes [--drain]
```
`--yes` is required after reviewing the planned core recreation. Restart activates the durable
maintenance gate before it checks sessions. Without `--drain`, active open sessions refuse the
command. With `--drain`, the command polls the authenticated session inventory until no active
sessions remain; it never terminates sessions and the wait is bounded.
Restart retains the exact current image and never builds, pulls, or upgrades an image. Before any
core mutation, it tags the captured running image ID with a transaction-scoped reference and
selects that reference through a lifecycle-only Compose override. A configured mutable tag moving
after capture therefore cannot change the restarted image. It recreates only `core` with
`--no-deps --force-recreate --no-build --pull never`; `frontend` and named volumes are not
recreated. Before reopening admission, it verifies health, the unchanged Pi version, the
provider/model/settings smoke, unchanged non-secret rendered configuration, the captured image
identity, and the complete persistence-mount fingerprint.
Restart and update keep separate recovery state:
```text
<projectDirectory>/.tht/<installation-id>/restart-state.json
<projectDirectory>/.tht/<installation-id>/update-state.json
```
The files are mode `0600` and share one installation lifecycle lock, so restart, update, and
rollback cannot race. Every mutating lifecycle command checks both files. Malformed or non-terminal
restart recovery state blocks update and rollback; malformed or incomplete update recovery state
blocks restart. A verified terminal restart state is cleaned up safely before a later mutation.
After core mutation, a restart failure leaves admission gated and preserves both
`restart-state.json` and its exact-image override; the operator must use status/logs and maintenance
recovery rather than deleting recovery material.
## Updating Pi
The normal update uses the repository's pinned version and build source automatically:
```text
tht pi update
tht pi update --version 0.81.0
```
With no `--version`, the command reads the single default `ARG PI_VERSION=<version>` from
`docker/core.Dockerfile` in the selected project. The normal path confirms the explicit update
command, drains active sessions without terminating them, builds the candidate, recreates only
`core`, verifies it, and promotes it transactionally.
Advanced registry updates remain available and require an immutable digest:
```text
tht pi update \
--version 0.81.0 --source build --yes --drain
tht pi update \
--version 0.81.0 --source pull \
--image registry.example.invalid/thothii-core@sha256:<64-lowercase-hex-digits> --yes
```
`--source build` rebuilds only `core` with `PI_VERSION=<version>`. `--source pull` requires an
immutable digest reference; mutable tags, URL forms, and credential-bearing references are
rejected. `--source` is never inferred.
Before inventory, update activates the durable maintenance gate. Activation writes
`/data/settings/maintenance.json` in the mounted settings volume, closes admission, and waits for
all leases. A recreated candidate reads that marker at startup and therefore starts gated. The
loopback-only control endpoints cannot be reached through the frontend proxy and do not depend on
the configured authentication principal mode. Lost activation/deactivation responses are resolved
by querying gate status only when the original result is unknown. An explicit file or directory
durability failure is never converted to success by matching readback: the control API reports
`maintenance_durability_failed`, keeps or restores the safest durable marker state, and requires
recovery.
Open, unarchived sessions stop an update. After an operator has completed or otherwise drained
their work, `--drain` makes the command poll the authenticated bare-array
`GET /sessions?scope=all` response until no active sessions remain.
The configured `core.image` is never retagged or mutated. Each installation transaction creates
unique candidate and previous tags, including when two installations share a configured tag or
the configured image is digest-pinned. A temporary lifecycle-only Compose override selects those
tags for build, recreate, and rollback. Candidate build, pull, or candidate-tag failures happen
before `mutation_started` and therefore never recreate or roll back core. After verification, the
temporary candidate selector is atomically promoted to the durable current-image override.
Rollback atomically promotes the previous selector. Terminal cleanup removes only transaction
files and never deletes the durable selector.
Only `core` is recreated, with `--no-deps --force-recreate`; `frontend` is not recreated and no
volume-replacement flags are used. Verification checks health; exact requested Pi version at all
three declared boundaries (the candidate executable, `PI_VERSION` environment, and
`io.thothii.pi.version` image label); the provider/model/settings smoke; unchanged
non-secret rendered configuration; and the complete persistence-mount fingerprint.
## Recovery, rollback, and maintenance cleanup
Recovery state and lock diagnostics live under:
```text
<projectDirectory>/.tht/<installation-id>/update-state.json
<projectDirectory>/.tht/<installation-id>/restart-state.json
<projectDirectory>/.tht/<installation-id>/*.lock.owner.json
```
Each recovery file is mode `0600`. Update state records transaction-scoped image identities, mount
fingerprints, target version/source, configuration digest, phase, and timestamp; restart state
records the retained image and its verification inputs. Neither file contains credentials, endpoint
values, secret paths, Compose output, or logs. A cross-platform OS advisory file lock serializes
both lifecycle operations; a crashed owner releases the lock automatically. Owner metadata is
diagnostic only and cannot wedge acquisition if empty, partial, or stale.
Any post-candidate failure explicitly confirms or reactivates maintenance and rescans sessions
before compensation. Automatic rollback selects the transaction's previous image through the
lifecycle override and clears maintenance only after the previous image, configuration, mounts,
health, Pi smoke, and terminal recovery write are verified. Ambiguous compensation remains gated.
If the candidate core is stopped and cannot serve the maintenance endpoint, rollback proves that
state with Compose and writes the marker through a one-off previous-image `core` container sharing
the settings volume. It does not require the failed candidate, a host Node runtime, or the Docker
socket inside a container. The restored core is then recreated, verified, and rescanned before the
gate can open.
For a failed update with `update-state.json`, first run:
```text
tht pi rollback --yes
```
Rollback restores the image recorded in update state, but it checks restart state before making any
change. A failed, pending, or malformed restart state rejects rollback. A failed restart retains its
captured image and has no candidate image to roll back; first inspect status and logs, repair the
reported problem, then use maintenance recovery.
Inspect and clean a stale durable gate with:
```text
tht pi maintenance status
tht pi maintenance recover --yes
```
`maintenance recover` restores the captured restart image pin and lifecycle override when needed,
verifies and removes interrupted restart recovery material, and only then processes update state.
It completes an interrupted verified-image promotion, safely finalizes a preparation interrupted
before core mutation, and refuses other pending mutations. For terminal or absent recovery state,
it removes only a stale transaction override, verifies the running installation when the gate is
active, and only then removes the durable marker and reopens admission. It never removes
`current-image.yaml`. If rollback or recovery fails, leave the marker in place, preserve the
relevant recovery state and override, repair the reported Docker/configuration issue, and rerun
rollback or maintenance recovery.
Missing confirmation, invalid arguments, active sessions, and an interrupted transaction exit
`2`. Docker and verification failures exit nonzero with concise, redacted guidance. Direct
read-only/log commands preserve the original Docker child exit code.
## Go dependency security boundary
The supported toolchain is Go `1.26.5`, released 2026-07-07, with module language version
`1.26.0`. The Docker builder is pinned by both patch tag and the multi-platform manifest-list
digest:
```text
golang:1.26.5-bookworm@sha256:1ecb7edf62a0408027bd5729dfd6b1b8766e578e8df93995b225dfd0944eb651
```
That manifest provides both `linux/amd64` and `linux/arm64/v8` builders. Go's official release
history is the authority for the patch level (`https://go.dev/doc/devel/release`); the Docker
Official Image is the authority for the builder (`https://hub.docker.com/_/golang`).
`golang.org/x/sys`, used by the Windows durable-replace implementation, is pinned to `v0.47.0`.
The directly used `github.com/sirupsen/logrus` is pinned to `v1.9.1`, which removes
GO-2025-4188 from the imported package set. The build contract verifies the exact toolchain,
dependencies, digest, and all five supported target builds (Windows amd64, Darwin amd64/arm64, and
Linux amd64/arm64). `go mod verify`, tests including the race detector, `go vet`, and
`govulncheck` are release gates.
@@ -1,155 +0,0 @@
# Workflow observable baseline
This contract freezes the externally observable behavior that the conservative modular
refactoring must preserve. It describes what callers and reviewers can observe; it does not
prescribe the internal location of the implementation.
Changing an expectation in this baseline is a behavior change and requires an explicit product
decision. Moving code between Workflow core, Disambiguation, Memory, and Evidence must keep the
baseline green without weakening its assertions.
```mermaid
stateDiagram-v2
[*] --> F1
state "F1 Clarification" as F1
state "F2 Memory" as F2
state "F3 Question rewrite" as F3
state "F4 Evidence" as F4
state "F5 Schema linking" as F5
state "F6 SQL drafting" as F6
state "F7 Validation" as F7
state "F8 Promotion" as F8
F1 --> F2
F2 --> F3
F3 --> F4
F4 --> F5
F5 --> F6
F6 --> F7
F7 --> F8
F7 --> F6: correction
F8 --> [*]
```
## Automated seams
### Pi gate
From `harness/`, run `npm test` with the repository's supported Node 24 runtime.
The gate suite fixes:
- the complete registered Pi tool schemas, including nested types and enum-like constraints;
- the semantic workflow definition and the exact injected session skill bytes;
- widget descriptors and reviewer response semantics;
- F1 clarification and explicitly accepted open ambiguity;
- F2 Memory applied, deselected, and absent;
- F3 rewritten question and assumptions, including mutation failure ordering;
- F4 Evidence used, accepted, rejected, and legacy-without-corpus projections;
- F8 Memory promotion accepted, declined, and absent, including mutation failure ordering;
- a newly folded phase is announced once in RPC mode even when it requires no human gate;
- resume reconstruction for the touched F1, F2, F3, F4, and F8 states;
- artifact payload compatibility, anti-bypass behavior, and final phase closing.
The baseline intentionally checks widget structure and domain content without freezing the
pre-existing Italian chrome emitted by the gate. Repository policy requires UI chrome and labels
to migrate to English in their owning workstream; this contract must not turn that mismatch into a
new compatibility requirement.
`harness/.pi/skills/tht-sessione/SKILL.md` is a committed projection. Its authoritative
Disambiguation and Memory fragments live under `modules/`; from `harness/`, run
`python -m tht.pi_skill_projection --write` to regenerate it or `--check` to detect drift.
Composition uses a static ordered tuple and never directory discovery.
### Harness CLI and persistence
Run the default pytest suite from the harness package. The suite fixes:
- pristine JSON output, human output separation, exit codes, and CLI error behavior;
- decision ledger folding, retraction, reopen ordering, and current-phase reconstruction;
- question, schema-linking, CTE, SQL, validation, and session-document projections;
- Evidence source, corpus, search, citation, and legacy-without-active-corpus behavior;
- Memory search, promotion, solved-question, and vector-write behavior;
- filesystem session persistence and PostgreSQL repository parity.
The default pytest configuration excludes only tests marked `l2`. Tests marked `l0` require a
working local Docker daemon and remain part of the default suite when Docker is available.
### Backend bridge
From `backend/`, the passing automated baseline is:
```sh
npx vitest run test/tht-runner.test.ts test/pi-process-manager.test.ts \
test/session-bridge.test.ts test/sse-hub.test.ts test/sse-route.test.ts \
test/routes-sessions.test.ts test/e2e-f1.test.ts \
test/workspace-preprocessing-service.test.ts \
test/workspaces/evidence/materialization.test.ts \
test/workspaces/evidence/preprocessing.test.ts \
test/workspaces/evidence/boundary.test.ts
npx tsc --noEmit -p .
npm run build
```
These suites fix:
- CLI argument ordering and JSON/error propagation across the runner boundary;
- new-session versus resume Pi prompts;
- refusal to resume finalized, archived, foreign, unavailable, or read-only sessions;
- Pi RPC to client event mapping, SSE replay/reset behavior, and runtime replacement ordering;
- reserved Pi phase notifications mapped to sanitized `phase_started` client events;
- failure persistence and sanitization before a client-visible response.
### Frontend client
From `frontend/`, the passing automated baseline is:
```sh
npx vitest run src/store/sessionStore.test.ts src/stream/useSessionStream.test.tsx \
src/widgets/registry.test.tsx src/widgets/SelectWidget.test.tsx \
src/widgets/MultiselectWidget.test.tsx src/widgets/ArtifactWidget.test.tsx \
src/shell/f1-loop.test.tsx src/shell/SessionDocumentsPanel.test.tsx
npx tsc -b
npm run build
```
These suites fix:
- widget registry and gate response payloads;
- `ui_request`, `text_delta`, activity, usage, and lifecycle event reduction;
- phase progress and the active workflow dot advancing on `phase_started` without a `ui_request`;
- stream replacement, cursor reset, reconnection, and pending-text flush behavior;
- session document projections shown to the reviewer.
## Mutation ordering
The following sequences are part of the observable failure contract:
1. F3 writes the rewritten question, appends `question_rewritten` to the ledger, then advances.
A failure stops the remaining operations.
2. F8 saves one reusable Memory vector, appends its `memory_promoted` marker, advances F8, then
finalizes. A failed vector write leaves no marker; a failed marker after a successful vector
write returns the manual recovery instruction and does not finalize.
3. A declined F8 candidate writes only `memory_promotion_declined`; an absent candidate writes no
Memory decision and still closes F8.
## Environment-dependent acceptance
Real-model and remote-DWH tests remain opt-in through the `l2` marker. The live journey from a new
question to finalization, followed by resume verification, belongs to the final live-acceptance
ticket. If its environment or credentials are unavailable, it must remain recorded as a pending
manual gate rather than being reported as passed.
## Full-suite diagnostic exceptions
Every command defined above as part of the automated baseline exits successfully. Running the
broader backend and frontend suites is still useful as a diagnostic, but those full suites are not
the executable acceptance gate for this ticket because two unrelated failures reproduce unchanged
on the source commit from which this branch was created:
- the backend authentication runtime-projection suite currently rejects ten positive fixtures
with its fail-closed public error;
- one frontend application-shell authentication test does not render the expected trusted-upstream
display name.
These two exceptions must remain visible until their owning workstream resolves them; they must not
be used to relax any workflow assertion or to describe a nonzero command as a passing baseline.
-7
View File
@@ -218,10 +218,3 @@ tht config check -c <path>
```
Stop after validation. P2/P6 later owns preprocessing and materialization.
## Acceptance states
These gates are independent and are not implied by this documentation contract.
automated integration: PENDING
manual acceptance: PENDING