docs: focus public documentation on product usage
This commit is contained in:
@@ -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.
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user