docs: document internal semantic infrastructure

This commit is contained in:
2026-08-08 20:52:33 +02:00
parent bff21507df
commit 22c3512ac8
12 changed files with 349 additions and 197 deletions
@@ -0,0 +1,61 @@
# Task 11 report
Status: completed on 2026-08-08.
## Scope delivered
- Updated operator-facing documentation for the internal Qdrant + Ollama architecture.
- Tightened documentation contract tests to require the current four-service-plus-init topology,
CPU-first/GPU-override guidance, fixed internal model/dimensions, schema-v3 migration wording,
one-collection-per-workspace ownership, and Qdrant backup/restore safety.
- Updated stable repo guidance in `AGENTS.md` and the current snapshot in `PROJECT_STATE.md`.
- Rewrote the workspace diagnostic protocol to the schema-v3/internal-semantic-service contract.
- Updated the memory guide to describe Qdrant as the derived persistent index.
- Updated the runtime secret-bundle guide to remove active vector/embedding secret guidance.
## Files changed
- `README.md`
- `AGENTS.md`
- `PROJECT_STATE.md`
- `docs/install/local-workspace-registry.md`
- `docs/install/server-workspace-registry.md`
- `docs/installazione-docker-4-contesti.md`
- `docs/workspace-diagnostic-protocol.md`
- `docs/gestione-memory.md`
- `deploy/secrets/README.md`
- `scripts/verify-workspace-install-docs.sh`
- `scripts/test-verify-workspace-install-docs.sh`
## Verification
Fresh successful runs:
```sh
./scripts/test-verify-workspace-install-docs.sh
./scripts/verify-workspace-install-docs.sh --fixtures-only
git diff --check
```
Key outcomes:
- internal semantic infrastructure documentation contract passed
- all existing install/manual fixture contracts still passed
- diff hygiene passed with no whitespace/errors
## Self-review notes
- The updated docs now match the code-backed Compose topology: `frontend`, `core`, `qdrant`,
`embedding`, and `embedding-model-init`.
- Active manuals no longer instruct operators to configure external vector or embedding runtime
endpoints/secrets.
- Qdrant backup/restore wording now matches the helper scripts' exact confirmation and rollback
behavior.
- Legacy descriptor handling is documented as explicit schema-v3 migration only; no silent
semantic-data migration is claimed.
## Residual concerns
- The broader repository still contains historical design/spec material that references older
pgvector/external-embedding architecture; this task intentionally updated operator/current-state
documentation and the corresponding contract tests, not historical planning documents.
+1 -4
View File
@@ -11,10 +11,7 @@ detail. Design history lives in `docs/superpowers/specs/` and `docs/superpowers/
## Commands ## Commands
The repo has three independently-built layers. Run the local Docker stack with The repo has three independently-built layers. Run the local Docker stack with `./scripts/run-stack.sh` after creating `deploy/env/local.env`; it starts the base+local Compose profile with `frontend`, `core`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`. The core image contains Pi. Qdrant and Ollama are internal Compose services; DWH and LLM remain external configuration endpoints.
`./scripts/run-stack.sh` after creating `deploy/env/local.env`; it starts the base+local Compose
profile, whose core image contains Pi. DWH, vector DB, embedding, and LLM remain external
configuration endpoints.
**harness/** (Python `tht` CLI + Pi gate extension) **harness/** (Python `tht` CLI + Pi gate extension)
- Install: `cd harness && python -m venv .venv && pip install -e ".[dev]"` (puts `tht` on PATH) - Install: `cd harness && python -m venv .venv && pip install -e ".[dev]"` (puts `tht` on PATH)
+30 -1
View File
@@ -1,8 +1,37 @@
# ThothII — Project State # ThothII — Project State
> Starting-point snapshot for new sessions. Last updated: 2026-08-05 (Task 13 fix round 3/5). > Starting-point snapshot for new sessions. Last updated: 2026-08-08 (Task 11 documentation and state update).
> Point a fresh session here ("read PROJECT_STATE.md") before substantial work. > Point a fresh session here ("read PROJECT_STATE.md") before substantial work.
## Internal Qdrant + Ollama semantic infrastructure — LIVE 2026-08-08
- **Compose topology.** The mandatory application stack is `frontend`, `core`, `qdrant`,
`embedding`, and the one-shot `embedding-model-init`. Startup is CPU-first by default; Linux
hosts may opt into GPU exposure with `THOTH_ENABLE_EMBEDDING_GPU=1`. Qdrant is private on the
Compose network and persists `/qdrant/storage` in `qdrant-data`. Ollama persists its local model
cache in `embedding-models`, and `embedding-model-init` blocks `core` until
`qwen3-embedding:0.6b` is present.
- **Semantic contract.** Internal semantic indexing is fixed to `qwen3-embedding:0.6b`,
`1024` dimensions, and cosine distance. Schema-v3 descriptors are operational; schema-v1/v2 descriptors remain `migration_required` until an explicit reviewed migration writes schema version 3. One workspace owns one Qdrant collection, and schema, Evidence, and Memory records
coexist inside that collection with payload `kind` separation.
- **Boundary and persistence.** Only DWH and LLM remain external runtime application endpoints.
There are no active external vector or embedding endpoint instructions, bindings, or secrets in
the supported operator manuals. Qdrant remains a derived but persistent semantic index: the
canonical sources of truth stay the workspace Git descriptors, phase artifacts, and memory
registry/ledger. The Ollama model cache is recoverable for offline startup but is not the
canonical source of semantic content.
- **Backup and recovery.** `./scripts/vector-backup.sh --project-name <name> --output <file>`
archives exactly one labeled `<project>_qdrant-data` volume and preserves the prior `qdrant`
running state. `./scripts/vector-restore.sh --project-name <name> --input <file>
--confirm-project <name>` requires the exact repeated project confirmation, validates manifest
and archive safety before stopping `qdrant`, stages rollback content, restores in place, and
restarts `qdrant` only if it was previously running. Restore does not migrate legacy workspace
descriptors, rename collections, or repair a semantic-index incompatibility.
- **Verification recorded for this docs/state update.** The installation-manual contract tests
now require the four-service-plus-init topology, the fixed internal model/dimensions, explicit
schema-v3 migration messaging, Qdrant collection ownership, Qdrant backup/restore safety, and
the absence of active external vector/embedding operator bindings from current manuals.
## Unified deployment release gate — Task 13 (2026-08-05) ## Unified deployment release gate — Task 13 (2026-08-05)
- **Release coverage.** `scripts/unified-deployment-smoke.sh` gates the two-service render/build, - **Release coverage.** `scripts/unified-deployment-smoke.sh` gates the two-service render/build,
+15 -7
View File
@@ -6,8 +6,7 @@ external in this profile, except for the mandatory internal semantic services bu
## Docker Compose: local startup ## Docker Compose: local startup
Requirements: Docker Engine with Compose v2. The mandatory stack is `frontend`, `core`, Requirements: Docker Engine with Compose v2. The mandatory stack is `frontend`, `core`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`. DWH and LLM remain external,
`qdrant`, `embedding`, and the one-shot `embedding-model-init`. DWH and LLM remain external,
configurable endpoints—even when they are co-located with ThothII. configurable endpoints—even when they are co-located with ThothII.
From a fresh clone, run these commands from the repository root: From a fresh clone, run these commands from the repository root:
@@ -44,9 +43,11 @@ loopback port).
Credentials and certificates are local protected files. Do not put them in environment examples, Credentials and certificates are local protected files. Do not put them in environment examples,
workspace YAML, URLs, or Compose interpolation values. workspace YAML, URLs, or Compose interpolation values.
Application state is split across the named `settings`, `pi-state`, `workspace-registry`, and Application state is split across the named `settings`, `pi-state`, `workspace-registry`,
`sessions` volumes. `docker compose down` keeps them. Only an explicit destructive command such `sessions`, `qdrant-data`, and `embedding-models` volumes. `docker compose down` keeps them.
as `docker compose down --volumes` removes them. `qdrant-data` is a derived but persistent index store; `embedding-models` is an Ollama model
cache for `qwen3-embedding:0.6b` with fixed `1024`-dimension embeddings. Only an explicit destructive command such as `docker compose
down --volumes` removes them.
The frontend depends on the core health check and proxies `/health` and `/api/*` to it. The The frontend depends on the core health check and proxies `/health` and `/api/*` to it. The
application health endpoint intentionally checks process readiness only; external dependency application health endpoint intentionally checks process readiness only; external dependency
@@ -69,6 +70,11 @@ every revision referenced by an open, closed, or failed unarchived session. It r
single local installation list or from a server administrator's complete session list, never from single local installation list or from a server administrator's complete session list, never from
a remote user's partial list. a remote user's partial list.
Schema-v3 is the operational descriptor contract. Schema-v1/v2 descriptors remain
`migration_required` until an explicit reviewed migration writes schema version 3. One workspace
owns one Qdrant collection; schema, Evidence, and Memory records share that collection and stay
separated by indexed payload `kind`.
Connector `ssh_tunnel` bindings are diagnostic-only in this release: their bounded probe always Connector `ssh_tunnel` bindings are diagnostic-only in this release: their bounded probe always
cleans up the loopback forward and returns `workspace_not_activatable`; session creation is rejected cleans up the loopback forward and returns `workspace_not_activatable`; session creation is rejected
before persistence. Git registry access over SSH is unaffected. Use direct or REST connector before persistence. Git registry access over SSH is unaffected. Use direct or REST connector
@@ -211,7 +217,7 @@ archive path.
Restore targets that same exact project-scoped `qdrant-data` volume. Because restore replaces the Restore targets that same exact project-scoped `qdrant-data` volume. Because restore replaces the
persistent Qdrant data in place, it requires an explicit confirmation that exactly repeats the persistent Qdrant data in place, it requires an explicit confirmation that exactly repeats the
Compose project name: Compose project name by passing `--confirm-project`:
```sh ```sh
./scripts/vector-restore.sh \ ./scripts/vector-restore.sh \
@@ -223,7 +229,9 @@ Compose project name:
The restore script stops `qdrant`, validates the exact labeled target, stages the current volume The restore script stops `qdrant`, validates the exact labeled target, stages the current volume
contents for rollback, extracts the requested archive into the volume, and then returns the contents for rollback, extracts the requested archive into the volume, and then returns the
service to its prior running state. After restore, run the backend health checks and a known service to its prior running state. After restore, run the backend health checks and a known
retrieval query before reopening write traffic. retrieval query before reopening write traffic. Restore does not migrate schema-v1/v2 workspace
descriptors, does not rename collections, and does not reconcile an incompatible collection
contract; those remain explicit reviewed recovery steps outside the helper.
## Production trust boundary and secrets ## Production trust boundary and secrets
+9 -5
View File
@@ -9,10 +9,14 @@ chmod 600 deploy/secrets/thothii.secrets
``` ```
The file uses strict `KEY=VALUE` lines (comments and blank lines are allowed). The supported The file uses strict `KEY=VALUE` lines (comments and blank lines are allowed). The supported
keys are `THT_MODEL_API_KEY`, `THT_DWH_API_KEY`, `THT_CA`, `THT_SSL_CA`, and keys are `THT_MODEL_API_KEY`, `THT_DWH_API_KEY`, `THT_CA`, and `THT_SSL_CA`. Values must be
`PI_PROVIDER_API_KEY`. Values must be non-empty and contain no whitespace. Do not put secrets non-empty and contain no whitespace. Do not put secrets
in the root `.env`, workspace YAML, URLs, logs, or rendered Compose output. in the root `.env`, workspace YAML, URLs, logs, or rendered Compose output.
Do not add vector or embedding endpoint credentials to the bundle. Active operator manuals use
internal Qdrant and Ollama services, so vector/embedding runtime endpoint secrets are not part of
the supported installation contract.
Compose mounts the bundle read-only as `/run/secrets/thothii.secrets`. The host file must be a Compose mounts the bundle read-only as `/run/secrets/thothii.secrets`. The host file must be a
regular non-symlink file with mode `0600` or `0400`; Docker's normal `0444` mode is accepted regular non-symlink file with mode `0600` or `0400`; Docker's normal `0444` mode is accepted
only for the runtime mount beneath `/run/secrets`. The core runs as UID 10001. Verify the mount only for the runtime mount beneath `/run/secrets`. The core runs as UID 10001. Verify the mount
@@ -37,9 +41,9 @@ and only then deleting the old files. The old variables remain a compatibility p
upgrades, but the documented and tested default is an absolute `THT_SECRETS_FILE` path to the upgrades, but the documented and tested default is an absolute `THT_SECRETS_FILE` path to the
protected bundle. protected bundle.
Hosted Pi providers must use a single provider key. Compound providers (Bedrock, Azure OpenAI Hosted Pi providers must use a single model key through `THT_MODEL_API_KEY`. Compound providers
Responses, Cloudflare Workers AI/Gateway) fail closed until a provider-specific credential (Bedrock, Azure OpenAI Responses, Cloudflare Workers AI/Gateway) fail closed until a
adapter is implemented. provider-specific credential adapter is implemented.
## User-owned session database secrets ## User-owned session database secrets
+9 -9
View File
@@ -16,7 +16,7 @@ decisione concept_clarified nel ledger della sessione
F8: il reviewer decide se promuoverla F8: il reviewer decide se promuoverla
│ │
├── registro globale registry.jsonl ├── registro globale registry.jsonl
└── indice semantico pgvector └── indice semantico Qdrant
│ │
▼ ▼
F2 di una sessione futura F2 di una sessione futura
@@ -33,7 +33,7 @@ Implementazione principale: [harness/tht/memory.py](../harness/tht/memory.py:14)
| --- | --- | --- | | --- | --- | --- |
| Ledger della sessione | `concept_clarified`, `memory_promoted`, `memory_promotion_declined` | Audit e stato della singola sessione | | Ledger della sessione | `concept_clarified`, `memory_promoted`, `memory_promotion_declined` | Audit e stato della singola sessione |
| Registro globale | Record `mem-XXXX` in `registry.jsonl` | Archivio canonico attuale delle memory | | Registro globale | Record `mem-XXXX` in `registry.jsonl` | Archivio canonico attuale delle memory |
| Indice pgvector | Embedding e metadati derivati dal registro | Ricerca semantica | | Indice Qdrant | Embedding e metadati derivati dal registro | Ricerca semantica |
Il ledger contiene la provenienza e le decisioni umane. Il record globale contiene il testo riutilizzabile. L'indice vettoriale è una proiezione per la ricerca, non il posto in cui il workflow registra direttamente le decisioni. Il ledger contiene la provenienza e le decisioni umane. Il record globale contiene il testo riutilizzabile. L'indice vettoriale è una proiezione per la ricerca, non il posto in cui il workflow registra direttamente le decisioni.
@@ -114,9 +114,9 @@ Il registro attuale è:
La scrittura viene fatta tramite file temporaneo e `os.replace`, quindi la sostituzione del registro è atomica. L'idempotenza della promozione è basata sulla coppia `session_id + decision_seq`: la stessa decisione della stessa sessione non genera due record globali. La scrittura viene fatta tramite file temporaneo e `os.replace`, quindi la sostituzione del registro è atomica. L'idempotenza della promozione è basata sulla coppia `session_id + decision_seq`: la stessa decisione della stessa sessione non genera due record globali.
### pgvector ### Qdrant
Dopo la promozione, `save-one` costruisce un solo `VectorRecord` e lo invia all'indice pgvector. Il testo indicizzato include: Dopo la promozione, `save-one` costruisce un solo `VectorRecord` e lo invia all'indice Qdrant. Il testo indicizzato include:
- tipo e soggetto; - tipo e soggetto;
- dettaglio; - dettaglio;
@@ -124,13 +124,13 @@ Dopo la promozione, `save-one` costruisce un solo `VectorRecord` e lo invia all'
- domanda di contesto; - domanda di contesto;
- eventuali concetti e mapping. - eventuali concetti e mapping.
Il record vettoriale usa l'id `memory:mem-XXXX`, mentre i metadati conservano `subject`, `detail`, `rationale`, `tables` e `concepts`. L'hash SHA-256 del contenuto impedisce di ricalcolare embedding e upsert quando il testo non è cambiato. Il record vettoriale usa l'id `memory:mem-XXXX`, mentre i metadati conservano `subject`, `detail`, `rationale`, `tables`, `concepts` e il discriminante `kind`. L'hash SHA-256 del contenuto impedisce di ricalcolare embedding e upsert quando il testo non è cambiato.
Il comportamento è implementato in [harness/tht/memory.py](../harness/tht/memory.py:253) e [harness/tht/memory.py](../harness/tht/memory.py:305). Il comportamento è implementato in [harness/tht/memory.py](../harness/tht/memory.py:253) e [harness/tht/memory.py](../harness/tht/memory.py:305).
### Fonte canonica attuale ### Fonte canonica attuale
Oggi il registro JSONL è ancora la fonte canonica applicativa e pgvector è l'indice derivato. Il commento iniziale di [memory_cmd.py](../harness/tht/cli/memory_cmd.py:1) segnala un debito tecnico: l'architettura futura prevista sarebbe usare direttamente il vector DB come archivio unico, ma questa migrazione non è ancora completata. Oggi il registro JSONL è ancora la fonte canonica applicativa e Qdrant resta un indice derivato ma persistente. Il workflow non registra direttamente le decisioni nel vector DB: usa Qdrant come proiezione interrogabile del registro e del ledger effettivo.
## Riutilizzo in F2 ## Riutilizzo in F2
@@ -209,10 +209,10 @@ Questo evita che una singola modifica al prompt o a un solo componente reintrodu
Il salvataggio segue sostanzialmente questa sequenza: Il salvataggio segue sostanzialmente questa sequenza:
```text ```text
registro JSONL → pgvector → marker memory_promoted nel ledger registro JSONL → Qdrant → marker memory_promoted nel ledger
``` ```
Se pgvector non è disponibile, il registro può contenere una memory non ancora ricercabile; il comando segnala che sarà necessario reindicizzare. Se Qdrant non è disponibile, il registro può contenere una memory non ancora ricercabile; il comando segnala che sarà necessario reindicizzare.
Se il marker del ledger fallisce dopo il salvataggio nel vector DB, la memory può risultare globalmente presente ma senza audit completo nella sessione. Il gate restituisce un comando di recupero manuale. Se il marker del ledger fallisce dopo il salvataggio nel vector DB, la memory può risultare globalmente presente ma senza audit completo nella sessione. Il gate restituisce un comando di recupero manuale.
@@ -232,4 +232,4 @@ Vecchi record `table_promoted` o `table_excluded` possono ancora esistere in art
La gestione attuale è coerente con il requisito funzionale: una memory è una conoscenza concettuale riutilizzabile, non una scelta di schema-linking. La gestione attuale è coerente con il requisito funzionale: una memory è una conoscenza concettuale riutilizzabile, non una scelta di schema-linking.
La parte più solida è la difesa multilivello del tipo `concept_clarified`. Il principale debito tecnico riguarda invece la convivenza del registro JSONL con pgvector e l'assenza di una transazione unica tra archivio globale, indice semantico e ledger della sessione. La parte più solida è la difesa multilivello del tipo `concept_clarified`. Il principale debito tecnico riguarda invece la convivenza del registro JSONL con Qdrant e l'assenza di una transazione unica tra archivio globale, indice semantico e ledger della sessione.
+9 -2
View File
@@ -130,8 +130,11 @@ before creating sessions. Git pull/push over SSH remains fully supported and is
## Bootstrap, first pull, and diagnostics ## Bootstrap, first pull, and diagnostics
Use the repository's canonical `compose.yaml` plus `deploy/compose.local.yaml`; they always start Use the repository's canonical `compose.yaml` plus `deploy/compose.local.yaml`; they always start
the mandatory `frontend` and `core` services. Do not copy or maintain a standalone application `frontend`, `core`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`. This profile
Compose file. Copy [the bindings env example](examples/workspace-bindings.env.example) into an is CPU-first. Add `THOTH_ENABLE_EMBEDDING_GPU=1` only on a Linux host that intentionally exposes a
supported GPU device to Docker. Qdrant is a derived but persistent index, while Ollama keeps a
local model cache for `qwen3-embedding:0.6b` (`1024` dimensions, cosine distance). Do not copy or
maintain a standalone application Compose file. Copy [the bindings env example](examples/workspace-bindings.env.example) into an
untracked operator directory and create a protected operator env file from untracked operator directory and create a protected operator env file from
`deploy/env/local.env.example`. It must contain absolute `PI_AUTH_FILE`, `deploy/env/local.env.example`. It must contain absolute `PI_AUTH_FILE`,
`THT_SECRETS_FILE`, `THT_WORKSPACE_BINDINGS_ENV_FILE`, and connector `*_SOURCE` paths. `THT_SECRETS_FILE`, `THT_WORKSPACE_BINDINGS_ENV_FILE`, and connector `*_SOURCE` paths.
@@ -172,6 +175,10 @@ Use `POST /workspace-registry/pull` to fetch later revisions. Run workspace diag
required DWH bindings are mounted. Schema-v3 diagnostics probe the internal Qdrant/Ollama required DWH bindings are mounted. Schema-v3 diagnostics probe the internal Qdrant/Ollama
services through backend config; ordinary diagnostics are read-only. services through backend config; ordinary diagnostics are read-only.
Schema-v3 is the only operational descriptor format. Schema-v1/v2 descriptors remain
`migration_required` until an explicit reviewed migration writes schema version 3. One workspace owns one Qdrant collection; schema, Evidence, and Memory records share that collection and remain
isolated by payload `kind`.
To migrate an existing legacy descriptor, create/clone an empty private remote, set the absolute To migrate an existing legacy descriptor, create/clone an empty private remote, set the absolute
`THT_SOURCE_ROOT`, transform with absolute paths, review the schema-v1 result, explicitly produce `THT_SOURCE_ROOT`, transform with absolute paths, review the schema-v1 result, explicitly produce
the reviewed schema-v3 contract, then commit/push. The transformer never imports `${ENV}` values the reviewed schema-v3 contract, then commit/push. The transformer never imports `${ENV}` values
+33 -3
View File
@@ -32,8 +32,9 @@ targets with runtime ownership without copying secret or tracked file contents i
state. Rerun it after a restore and before Compose or `thothctl` startup; it is idempotent and does state. Rerun it after a restore and before Compose or `thothctl` startup; it is idempotent and does
not overwrite existing targets. not overwrite existing targets.
Permit outbound TCP only to approved Git/Gitea, DWH, Qdrant, embedding, and bastion endpoints. Permit outbound TCP only to approved Git/Gitea, DWH, LLM, and optional bastion endpoints.
Allow inbound traffic only from the reverse proxy/Docker network. Do not give the runtime service Qdrant and Ollama run inside the Compose stack. Allow inbound traffic only from the reverse
proxy/Docker network. Do not give the runtime service
account Gitea administration, database-superuser rights, or a shell in the Git host. account Gitea administration, database-superuser rights, or a shell in the Git host.
## Gitea and remote Git setup ## Gitea and remote Git setup
@@ -151,7 +152,11 @@ The Git registry itself may still use SSH normally.
## Same-origin reverse proxy, bootstrap, and health ## Same-origin reverse proxy, bootstrap, and health
Use the repository's canonical `compose.yaml` plus `deploy/compose.server.yaml`; they always Use the repository's canonical `compose.yaml` plus `deploy/compose.server.yaml`; they always
start the mandatory `frontend` and `core` services. Do not copy or maintain a standalone start `frontend`, `core`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`.
Startup is CPU-first; use `THOTH_ENABLE_EMBEDDING_GPU=1` only when the server intentionally
exposes a supported GPU device to Docker. Qdrant is a derived but persistent index, and the
Ollama model cache persists the exact `qwen3-embedding:0.6b` model (`1024` dimensions, cosine
distance) for offline reuse. Do not copy or maintain a standalone
application Compose file. Copy `docs/install/examples/thothii-installation.server.yaml` to the application Compose file. Copy `docs/install/examples/thothii-installation.server.yaml` to the
protected operator directory and preserve its required session-server overlay, exactly one Git protected operator directory and preserve its required session-server overlay, exactly one Git
transport override, and generated connector-secret override. transport override, and generated connector-secret override.
@@ -221,6 +226,10 @@ Its schema-v1 output is `migration_required`; explicitly supply collection ident
and the reviewed v3 contract before commit. Never import `${ENV}` values or and the reviewed v3 contract before commit. Never import `${ENV}` values or
copy secret files. copy secret files.
Schema-v3 is the only operational descriptor contract. Schema-v1/v2 descriptors remain
`migration_required` until an explicit reviewed migration writes version 3. One workspace owns one Qdrant collection; schema, Evidence, and Memory records share it and stay separated by payload
`kind`.
After valid bootstrap, Git outage retains the active snapshot with `degraded: true`. Repair After valid bootstrap, Git outage retains the active snapshot with `degraded: true`. Repair
egress/DNS/CA/credentials, pull, and confirm healthy status. Roll back a bad descriptor through a egress/DNS/CA/credentials, pull, and confirm healthy status. Roll back a bad descriptor through a
reviewed Git revert/release branch, advance the remote through normal policy, pull it, and confirm reviewed Git revert/release branch, advance the remote through normal policy, pull it, and confirm
@@ -248,3 +257,24 @@ revision and monitor status. If snapshots are missing or corrupt, stop the servi
newest verified registry backup, start it privately, verify status, and then reopen proxy traffic. newest verified registry backup, start it privately, verify status, and then reopen proxy traffic.
A first-bootstrap failure has no fallback: repair remote trust rather than creating an unreviewed A first-bootstrap failure has no fallback: repair remote trust rather than creating an unreviewed
runtime checkout. runtime checkout.
## Qdrant backup/restore and cache recovery
Use the repository helpers for Qdrant backup/restore:
```sh
./scripts/vector-backup.sh --project-name thothii --output /secure/backups/thoth-qdrant-2026-08-08.tar
./scripts/vector-restore.sh --project-name thothii --input /secure/backups/thoth-qdrant-2026-08-08.tar --confirm-project thothii
```
Qdrant backup/restore targets exactly one labeled `qdrant-data` volume for the named Compose
project. Restore requires the exact repeated project confirmation, validates the archive before
stopping `qdrant`, stages rollback content, and restores in place only for that project-scoped
volume. It does not migrate schema-v1/v2 workspaces, rename collections, or resolve semantic-index
incompatibilities.
The Ollama model cache is a recoverable local cache, not the canonical semantic source of truth.
You may back up `embedding-models` for faster offline recovery, but a cache loss is recoverable by
re-pulling `qwen3-embedding:0.6b` through `embedding-model-init`.
Only the Git remote, DWH, LLM, and optional bastion endpoints stay external.
+2 -2
View File
@@ -8,8 +8,8 @@ ThothII usa una topologia Compose unica:
- `embedding` - `embedding`
- `embedding-model-init` - `embedding-model-init`
Qdrant e Ollama embedding sono servizi interni obbligatori del progetto Compose. Restano Qdrant e Ollama embedding sono servizi interni obbligatori del progetto Compose. Restano esterni solo DWH e LLM. Il modello fissato è `qwen3-embedding:0.6b` con 1024 dimensioni e distanza
esterni solo DWH e LLM. coseno; `embedding-model-init` lo prepara prima dell'avvio di `core`.
## Comando standard locale ## Comando standard locale
+70 -164
View File
@@ -1,44 +1,47 @@
# Workspace diagnostic protocol # Workspace diagnostic protocol
This is the operator contract for testing a workspace on one ThothII installation. The This is the operator contract for testing a workspace on one ThothII installation. The Git-shared
Git-shared descriptor declares *what* can be checked; the installation supplies the selected descriptor declares what can be checked; the installation supplies only the selected DWH
transport and the local bindings. No secret value, certificate content, SSH key, or response body transport and local secret-file bindings. No secret value, certificate content, SSH key, or
belongs in the descriptor, this document, a generated `.env.example`, or diagnostic output. response body belongs in the descriptor, generated `.env.example` files, or diagnostic output.
## Scope and safety rules ## Scope and safety rules
- The descriptor is schema version 2. Its vector `database` and `schema` are required identity - The operational descriptor is schema version 3.
fields; they are not copied from the DWH, even when both services share PostgreSQL. - Schema-v1/v2 descriptors are readable only and remain `migration_required` until an explicit
- Version 1 descriptors are readable only and have `migration_required` status. An explicit reviewed migration writes schema version 3.
migration supplies `semantic_index.vector_store.database` and `.schema`, writes version 2, and - One workspace owns one Qdrant collection.
must never infer either from `dwh`. - Qdrant and Ollama are internal services. Operators do not bind external vector or embedding
- Each diagnostic is bounded by the configured workspace diagnostic timeout. Redirects are transports for active manuals or supported diagnostics.
rejected, response bodies stay inside the adapter, and browser-visible errors are limited to - Each diagnostic is bounded by the configured timeout. Redirects are rejected, response bodies
`binding_missing`, `connector_unavailable`, and `semantic_index_incompatible`. stay inside the adapter, and browser-visible errors are limited to `binding_missing`,
- A REST path is descriptor-declared, origin-relative, starts with one `/`, and has no query or `connector_unavailable`, and `semantic_index_incompatible`.
fragment. The client may use only the declared method, path, auth mode, and response-field names.
- `auth: none` sends no credential; `auth: bearer` reads a local file and sends
`Authorization: Bearer <file-content>`; `auth: x-api-key` sends `x-api-key: <file-content>`.
The resolver, rendered runtime endpoint, and diagnoser do not require or read an API-key file
for an `auth: none` diagnostic. File content is never logged or returned.
## Canonical descriptor additions ## Canonical descriptor contract
```yaml ```yaml
workspace:
schema_version: 3
id: psd-clinical
name: PSD Clinical
language: it
dwh:
engine: postgres
database: warehouse
schema: datawarehouse
supported_transports: [postgres_direct, rest_api, ssh_tunnel]
semantic_index: semantic_index:
vector_store: vector_store:
engine: pgvector engine: qdrant
database: vector_database collection: psd-clinical
schema: vectors dimensions: 1024
collection: clinical_documents
dimensions: 768
distance: cosine distance: cosine
supported_transports: [pgvector_direct, rest_api, ssh_tunnel]
vector_writer: {} # optional: declares a separately bound writer capability
embedding: embedding:
provider: ollama_compatible provider: ollama_internal
model: nomic-embed-text-v2-moe model: qwen3-embedding:0.6b
dimensions: 768 dimensions: 1024
diagnostics: diagnostics:
dwh_rest: dwh_rest:
@@ -46,35 +49,25 @@ diagnostics:
path: /rpc/ping path: /rpc/ping
auth: bearer auth: bearer
response: { database: database, schema: schema } response: { database: database, schema: schema }
vector_rest:
metadata:
method: GET
path: /vector/metadata
auth: bearer
response: { collection: collection, dimensions: dimensions, distance: distance }
reversible_probe:
method: POST
path: /vector/diagnostic-probe
auth: bearer
response: { operation: operation }
embedding:
method: GET
path: /models
auth: none
response: { model: model, dimensions: dimensions }
``` ```
`diagnostics.dwh_rest` requires DWH `rest_api`; `diagnostics.vector_rest` requires vector The semantic-index contract is fixed:
`rest_api`. `reversible_probe` is optional, but when present it must be authenticated `POST` and
declare the response field that echoes the requested `operation`. Response-map values are JSON - `engine: qdrant`
object field names, not values to be put in Git. - collection name equals the workspace-owned portable identifier
- `qwen3-embedding:0.6b`
- `1024` dimensions
- cosine distance
If any active collection reports a different model pairing, dimension, or distance, diagnostics
must return `semantic_index_incompatible` rather than silently rewriting data.
## Installation-local variable contract ## Installation-local variable contract
Replace `<NAMESPACE>` with the immutable workspace ID converted to upper case with hyphens changed Replace `<NAMESPACE>` with the immutable workspace ID converted to upper case with hyphens changed
to underscores. For example, `psd-clinical` becomes `PSD_CLINICAL`. Set only the variables for the to underscores. For example, `psd-clinical` becomes `PSD_CLINICAL`. Set only the variables for the
selected transport. Every `*_FILE` value is an absolute path to a regular, readable file inside an selected DWH transport. Every `*_FILE` value is an absolute path to a regular, readable file
approved local secret root; it is never the secret itself. inside an approved local secret root; it is never the secret itself.
| Connector and transport | Required local variables | | Connector and transport | Required local variables |
| --- | --- | | --- | --- |
@@ -82,25 +75,9 @@ approved local secret root; it is never the secret itself.
| DWH `postgres_direct` | `THT_WS_<NAMESPACE>_DWH_HOST`, `THT_WS_<NAMESPACE>_DWH_PORT`, `THT_WS_<NAMESPACE>_DWH_USER`, `THT_WS_<NAMESPACE>_DWH_PASSWORD_FILE`; optional `THT_WS_<NAMESPACE>_DWH_TLS_CA_FILE` | | DWH `postgres_direct` | `THT_WS_<NAMESPACE>_DWH_HOST`, `THT_WS_<NAMESPACE>_DWH_PORT`, `THT_WS_<NAMESPACE>_DWH_USER`, `THT_WS_<NAMESPACE>_DWH_PASSWORD_FILE`; optional `THT_WS_<NAMESPACE>_DWH_TLS_CA_FILE` |
| DWH `rest_api` | `THT_WS_<NAMESPACE>_DWH_BASE_URL`; `THT_WS_<NAMESPACE>_DWH_API_KEY_FILE` only for `bearer`/`x-api-key`; optional `THT_WS_<NAMESPACE>_DWH_TLS_CA_FILE` | | DWH `rest_api` | `THT_WS_<NAMESPACE>_DWH_BASE_URL`; `THT_WS_<NAMESPACE>_DWH_API_KEY_FILE` only for `bearer`/`x-api-key`; optional `THT_WS_<NAMESPACE>_DWH_TLS_CA_FILE` |
| DWH `ssh_tunnel` | `THT_WS_<NAMESPACE>_DWH_USER`, `THT_WS_<NAMESPACE>_DWH_PASSWORD_FILE`, `THT_WS_<NAMESPACE>_DWH_SSH_HOST`, `THT_WS_<NAMESPACE>_DWH_SSH_PORT`, `THT_WS_<NAMESPACE>_DWH_SSH_USER`, `THT_WS_<NAMESPACE>_DWH_SSH_PRIVATE_KEY_FILE`, `THT_WS_<NAMESPACE>_DWH_SSH_KNOWN_HOSTS_FILE`, `THT_WS_<NAMESPACE>_DWH_SSH_TARGET_HOST`, `THT_WS_<NAMESPACE>_DWH_SSH_TARGET_PORT`; optional `THT_WS_<NAMESPACE>_DWH_TLS_CA_FILE` | | DWH `ssh_tunnel` | `THT_WS_<NAMESPACE>_DWH_USER`, `THT_WS_<NAMESPACE>_DWH_PASSWORD_FILE`, `THT_WS_<NAMESPACE>_DWH_SSH_HOST`, `THT_WS_<NAMESPACE>_DWH_SSH_PORT`, `THT_WS_<NAMESPACE>_DWH_SSH_USER`, `THT_WS_<NAMESPACE>_DWH_SSH_PRIVATE_KEY_FILE`, `THT_WS_<NAMESPACE>_DWH_SSH_KNOWN_HOSTS_FILE`, `THT_WS_<NAMESPACE>_DWH_SSH_TARGET_HOST`, `THT_WS_<NAMESPACE>_DWH_SSH_TARGET_PORT`; optional `THT_WS_<NAMESPACE>_DWH_TLS_CA_FILE` |
| Vector selection | `THT_WS_<NAMESPACE>_VECTOR_TRANSPORT` |
| Vector `pgvector_direct` | `THT_WS_<NAMESPACE>_VECTOR_HOST`, `THT_WS_<NAMESPACE>_VECTOR_PORT`, `THT_WS_<NAMESPACE>_VECTOR_USER`, `THT_WS_<NAMESPACE>_VECTOR_PASSWORD_FILE`; optional `THT_WS_<NAMESPACE>_VECTOR_TLS_CA_FILE` |
| Vector `rest_api` | `THT_WS_<NAMESPACE>_VECTOR_BASE_URL`; `THT_WS_<NAMESPACE>_VECTOR_API_KEY_FILE` only for `bearer`/`x-api-key`; optional `THT_WS_<NAMESPACE>_VECTOR_TLS_CA_FILE` |
| Vector `ssh_tunnel` | `THT_WS_<NAMESPACE>_VECTOR_USER`, `THT_WS_<NAMESPACE>_VECTOR_PASSWORD_FILE`, `THT_WS_<NAMESPACE>_VECTOR_SSH_HOST`, `THT_WS_<NAMESPACE>_VECTOR_SSH_PORT`, `THT_WS_<NAMESPACE>_VECTOR_SSH_USER`, `THT_WS_<NAMESPACE>_VECTOR_SSH_PRIVATE_KEY_FILE`, `THT_WS_<NAMESPACE>_VECTOR_SSH_KNOWN_HOSTS_FILE`, `THT_WS_<NAMESPACE>_VECTOR_SSH_TARGET_HOST`, `THT_WS_<NAMESPACE>_VECTOR_SSH_TARGET_PORT`; optional `THT_WS_<NAMESPACE>_VECTOR_TLS_CA_FILE` |
| Optional vector writer | `THT_WS_<NAMESPACE>_VECTOR_WRITER_API_KEY_FILE` |
| Embedding service | `THT_WS_<NAMESPACE>_EMBEDDING_BASE_URL`; optional `THT_WS_<NAMESPACE>_EMBEDDING_API_KEY_FILE`, `THT_WS_<NAMESPACE>_EMBEDDING_TLS_CA_FILE` |
For the example workspace, the optional writer name is exactly There are no supported `THT_WS_<NAMESPACE>_VECTOR_*` or
`THT_WS_PSD_CLINICAL_VECTOR_WRITER_API_KEY_FILE`. It must resolve to a different local file from `THT_WS_<NAMESPACE>_EMBEDDING_*` installation bindings in the active operator contract.
`THT_WS_PSD_CLINICAL_VECTOR_API_KEY_FILE`; a reader key is never substituted for a writer key.
### Private-CA REST limitation
The current REST diagnostic adapters use the platform `fetch` implementation and cannot load a
per-request private CA. Therefore a REST diagnostic with any `*_TLS_CA_FILE` binding is refused
rather than silently disabling certificate verification. Use an HTTPS endpoint trusted by the
runtime trust store, use direct or SSH transport where the native PostgreSQL client can validate
the local CA file, or arrange TLS termination at a trusted boundary. This limitation applies to
DWH REST, vector metadata/write REST, and embedding REST diagnostics.
## DWH diagnostic ## DWH diagnostic
@@ -113,15 +90,7 @@ SELECT current_database() AS database, current_schema() AS schema
Both returned values must equal the descriptor's DWH database and schema. Both returned values must equal the descriptor's DWH database and schema.
`*_TLS_CA_FILE` is optional for direct and SSH PostgreSQL diagnostics. When provided, it is used For REST, the descriptor-declared request is for example:
with certificate verification; when absent, the native client still requires a valid certificate
chain from the runtime system trust store. Absence never disables TLS verification.
An SSH tunnel changes only the TCP peer to loopback. The forwarded PostgreSQL TLS connection sets
its server name to `<ROLE>_SSH_TARGET_HOST`, so certificate hostname validation remains against the
declared remote target rather than `127.0.0.1`.
For REST, the descriptor above declares the exact ping:
```text ```text
POST <THT_WS_<NAMESPACE>_DWH_BASE_URL>/rpc/ping POST <THT_WS_<NAMESPACE>_DWH_BASE_URL>/rpc/ping
@@ -129,87 +98,27 @@ Authorization: Bearer <content of DWH_API_KEY_FILE>
``` ```
It has no request body. A 2xx response must be a JSON object whose declared `database` and It has no request body. A 2xx response must be a JSON object whose declared `database` and
`schema` fields equal `dwh.database` and `dwh.schema`. For the sample response map, that is: `schema` fields match the descriptor.
```json ## Internal semantic-service diagnostic
{ "database": "warehouse", "schema": "datawarehouse" }
```
The values are illustrative resource identities, not credentials. A different response-field map Schema-v3 workspace diagnostics also verify the internal semantic infrastructure through backend
is valid only when the descriptor declares it. configuration:
## Vector metadata diagnostic - Qdrant must be reachable at the installation-owned internal URL.
- The workspace-owned collection must exist or be creatable with `1024` dimensions and cosine
distance.
- Ollama must provide `qwen3-embedding:0.6b`.
- A bounded embed probe must return exactly `1024` dimensions.
Direct and SSH vector checks connect to the *vector* `database` and `schema`, not the DWH These checks use the private Compose services and never require operator-supplied vector or
identity. They inspect the declared collection's vector column and index and must find the exact embedding URLs, transports, or credentials.
collection, integer `dimensions`, and `distance` (`cosine`, `l2`, or `inner_product`) declared in
`semantic_index.vector_store`.
Their optional `*_TLS_CA_FILE` follows the same verified private-CA-or-system-trust rule as the
DWH diagnostic. For an SSH tunnel, their TLS server name is likewise the declared vector
`SSH_TARGET_HOST`, not the loopback listener.
For REST, the exact descriptor-declared request is, for example:
```text
GET <THT_WS_<NAMESPACE>_VECTOR_BASE_URL>/vector/metadata
Authorization: Bearer <content of VECTOR_API_KEY_FILE>
```
It has no request body. A 2xx JSON object must supply the declared `collection`, `dimensions`, and
`distance` fields. All three values must exactly match the vector-store contract; an integer
dimension is required. Metadata from a similarly named collection, a different metric, or a
different dimension makes the semantic index incompatible.
## Reversible vector writer probe
Ordinary validation is reader-only. A write probe runs only when all of the following are true:
1. The operator explicitly requests it.
2. The descriptor has `semantic_index.vector_writer: {}`.
3. The descriptor declares an authenticated `diagnostics.vector_rest.reversible_probe` with an
`operation` response field.
4. The selected vector transport is `rest_api`.
5. `THT_WS_<NAMESPACE>_VECTOR_WRITER_API_KEY_FILE` exists locally and is distinct from the reader
API-key file.
The probe uses the declared `POST` endpoint twice, with the same generated ID and the writer key:
```json
{ "operation": "create", "id": "diagnostic:<random UUID>", "collection": "<declared collection>", "dimensions": 768 }
```
then:
```json
{ "operation": "remove", "id": "diagnostic:<same UUID>", "collection": "<declared collection>" }
```
Both requests require a 2xx JSON response whose declared `operation` field equals the requested
`create` or `remove` operation. Cleanup is attempted in `finally`, including after a write timeout
or error. The endpoint must implement both operations as a bounded, reversible diagnostic
operation; an upsert-only endpoint is prohibited. It must not retain, index, or expose diagnostic
records. If the writer capability or its local binding is absent, validation remains reader-only
and no write request is sent.
## Embedding dimensions diagnostic
The embedding request is descriptor-declared, for example:
```text
GET <THT_WS_<NAMESPACE>_EMBEDDING_BASE_URL>/models
```
It has no body and uses the declared authentication mode. A 2xx JSON object must contain the
declared model field equal to `semantic_index.embedding.model` and a declared dimensions field that
is an integer. That integer must equal both `semantic_index.embedding.dimensions` and
`semantic_index.vector_store.dimensions`.
## SSH host verification and tunnel lifecycle ## SSH host verification and tunnel lifecycle
For either DWH or vector `ssh_tunnel`, the known-hosts file is mandatory and is verified before a For DWH `ssh_tunnel`, the known-hosts file is mandatory and is verified before a connection is
connection is accepted. The tunnel is a short-lived loopback forward for the diagnostic only. The accepted. The tunnel is a short-lived loopback forward for the diagnostic only. The effective
effective OpenSSH constraints are: OpenSSH constraints are:
```text ```text
-N -v -N -v
@@ -224,19 +133,16 @@ effective OpenSSH constraints are:
``` ```
The local listener is `127.0.0.1` only. The process is terminated in cleanup after the direct The local listener is `127.0.0.1` only. The process is terminated in cleanup after the direct
probe, on timeout, or on failure. There is no accept-new mode, no disabled host-key checking, and probe, on timeout, or on failure.
no persistent forwarding.
In this release, `ssh_tunnel` is therefore a diagnostic-only connector transport. A successful In this release, `ssh_tunnel` remains a diagnostic-only DWH transport. A successful probe is
probe is followed by `workspace_not_activatable`, and `POST /sessions` rejects the workspace before followed by `workspace_not_activatable`, and `POST /sessions` rejects the workspace before
persisting a manifest or starting Pi. Use direct PostgreSQL/pgvector or REST for runtime sessions persisting a manifest or starting Pi. This restriction does not apply to SSH transport for the
until the backend owns a tunnel for the full runtime lifecycle. This restriction does not apply to workspace Git remote.
using SSH as the transport for the workspace Git remote.
## Reader-only fallback ## Reader-only fallback
A workspace may be fully valid in Git but non-activatable locally when a required reader binding, A workspace may be fully valid in Git but non-activatable locally when a required DWH binding,
secret file, host verification, TLS check, or declared diagnostic fails. That state does not alter secret file, host verification, TLS check, or declared DWH diagnostic fails. That state does not
the shared descriptor and does not permit a new session on that installation. It may still be alter the shared descriptor and does not permit a new session on that installation. It may still
published and activated elsewhere with valid local bindings. Missing optional writer capability is be published and activated elsewhere with valid local bindings.
not a reader failure: it leaves the workspace in reader-only mode and suppresses the writer probe.
@@ -11,6 +11,7 @@ trap 'rm -f "$output" "$verifier_functions"; rm -rf "$negative_root"' EXIT HUP I
"$root/scripts/verify-workspace-install-docs.sh" --fixtures-only >"$output" "$root/scripts/verify-workspace-install-docs.sh" --fixtures-only >"$output"
for fixture in \ for fixture in \
"internal semantic infrastructure documentation contract" \
"local installation guide contract" \ "local installation guide contract" \
"source update fail-closed semantics" \ "source update fail-closed semantics" \
"Windows line-ending recovery guide contract" \ "Windows line-ending recovery guide contract" \
@@ -35,6 +36,25 @@ for fixture in \
} }
done done
grep -Fq '## Internal Qdrant + Ollama semantic infrastructure' "$root/PROJECT_STATE.md" || {
echo "PROJECT_STATE.md does not record the internal Qdrant/Ollama snapshot" >&2
exit 1
}
grep -Fq 'Qdrant and Ollama are internal Compose services' "$root/AGENTS.md" || {
echo "AGENTS.md does not record the stable internal semantic-service guidance" >&2
exit 1
}
grep -Fq 'Do not add vector or embedding endpoint credentials to the bundle.' \
"$root/deploy/secrets/README.md" || {
echo "secret bundle guide still permits vector/embedding runtime secrets" >&2
exit 1
}
if rg -n 'engine: pgvector|provider: ollama_compatible|THT_WS_<NAMESPACE>_VECTOR_TRANSPORT|THT_WS_<NAMESPACE>_EMBEDDING_BASE_URL' \
"$root/docs/workspace-diagnostic-protocol.md"; then
echo "workspace diagnostic protocol still documents external vector or embedding contracts" >&2
exit 1
fi
server_guide="$root/docs/install/server.md" server_guide="$root/docs/install/server.md"
grep -Fq 'scripts/prepare-server-pi-state.sh /srv/thothii/pi-state 10001 10001' "$server_guide" || { grep -Fq 'scripts/prepare-server-pi-state.sh /srv/thothii/pi-state 10001 10001' "$server_guide" || {
echo "server guide does not initialize nested Pi-state targets before Compose" >&2 echo "server guide does not initialize nested Pi-state targets before Compose" >&2
+90
View File
@@ -43,6 +43,18 @@ verify_path_variable_values() {
done <"$source" done <"$source"
} }
require_absent() {
local source="$1" label="$2"
shift 2
local forbidden
for forbidden in "$@"; do
if grep -Fq -- "$forbidden" "$source"; then
echo "$label contains forbidden text: $forbidden" >&2
return 1
fi
done
}
require_headings() { require_headings() {
local source="$1" label="$2" local source="$1" label="$2"
shift 2 shift 2
@@ -67,6 +79,80 @@ require_text() {
done done
} }
verify_internal_semantic_infrastructure_docs() {
local readme="$root/README.md"
local agents="$root/AGENTS.md"
local project_state="$root/PROJECT_STATE.md"
local local_manual="$root/docs/install/local-workspace-registry.md"
local server_manual="$root/docs/install/server-workspace-registry.md"
local compact_manual="$root/docs/installazione-docker-4-contesti.md"
local diagnostics="$root/docs/workspace-diagnostic-protocol.md"
local memory="$root/docs/gestione-memory.md"
local secrets="$root/deploy/secrets/README.md"
require_text "$readme" "README" \
'mandatory stack is `frontend`, `core`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`' \
'`qwen3-embedding:0.6b`' \
'`1024`' \
'`qdrant-data`' \
'`embedding-models`' \
'`--confirm-project`' || return 1
require_text "$agents" "AGENTS.md" \
'Run the local Docker stack with `./scripts/run-stack.sh` after creating `deploy/env/local.env`; it starts the base+local Compose' \
'Qdrant and Ollama are internal Compose services' \
'DWH and LLM remain external configuration endpoints.' || return 1
require_text "$project_state" "PROJECT_STATE.md" \
'## Internal Qdrant + Ollama semantic infrastructure' \
'Schema-v3 descriptors are operational; schema-v1/v2 descriptors remain `migration_required` until an explicit reviewed migration writes schema version 3.' \
'One workspace owns one Qdrant collection' || return 1
for manual in "$local_manual" "$server_manual"; do
require_text "$manual" "$(basename "$manual")" \
'`frontend`, `core`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`' \
'`qwen3-embedding:0.6b`' \
'`1024`' \
'`migration_required`' \
'One workspace owns one Qdrant collection' || return 1
done
require_text "$local_manual" "local workspace manual" \
'CPU-first' \
'THOTH_ENABLE_EMBEDDING_GPU=1' \
'Qdrant is a derived but persistent index' || return 1
require_text "$server_manual" "server workspace manual" \
'Only the Git remote, DWH, LLM, and optional bastion endpoints stay external.' \
'Ollama model cache' \
'Qdrant backup/restore' || return 1
require_text "$compact_manual" "four-context install note" \
'Qdrant e Ollama embedding sono servizi interni obbligatori del progetto Compose. Restano esterni solo DWH e LLM.' \
'qwen3-embedding:0.6b' \
'1024 dimensioni' || return 1
require_text "$diagnostics" "workspace diagnostic protocol" \
'schema version 3' \
'One workspace owns one Qdrant collection.' \
'`semantic_index_incompatible`' || return 1
require_absent "$diagnostics" "workspace diagnostic protocol" \
'engine: pgvector' \
'provider: ollama_compatible' \
'THT_WS_<NAMESPACE>_VECTOR_TRANSPORT' \
'THT_WS_<NAMESPACE>_EMBEDDING_BASE_URL' || return 1
require_text "$memory" "memory guide" \
'Indice Qdrant' \
'Qdrant resta un indice derivato ma persistente' \
'`kind`' || return 1
require_absent "$memory" "memory guide" \
'Indice pgvector' \
'save-one costruisce un solo `VectorRecord` e lo invia all''indice pgvector.' || return 1
require_text "$secrets" "deploy secrets guide" \
'`THT_MODEL_API_KEY`, `THT_DWH_API_KEY`, `THT_CA`, and `THT_SSL_CA`' \
'Do not add vector or embedding endpoint credentials to the bundle.' || return 1
require_absent "$secrets" "deploy secrets guide" \
'PI_PROVIDER_API_KEY' || return 1
}
verify_local_guide() { verify_local_guide() {
local guide="$root/docs/install/local.md" local guide="$root/docs/install/local.md"
[[ -f "$guide" ]] || { [[ -f "$guide" ]] || {
@@ -1459,6 +1545,8 @@ NODE
case "$mode" in case "$mode" in
--fixtures-only) --fixtures-only)
[[ $# -eq 1 ]] || { echo "usage: $0 --fixtures-only" >&2; exit 2; } [[ $# -eq 1 ]] || { echo "usage: $0 --fixtures-only" >&2; exit 2; }
verify_internal_semantic_infrastructure_docs
echo "internal semantic infrastructure documentation contract passed"
verify_local_guide verify_local_guide
verify_windows_line_endings_guide verify_windows_line_endings_guide
verify_pi_management_guide verify_pi_management_guide
@@ -1476,11 +1564,13 @@ case "$mode" in
[[ $# -eq 2 && "$profile" =~ ^(local|server)$ ]] \ [[ $# -eq 2 && "$profile" =~ ^(local|server)$ ]] \
|| { echo "usage: $0 --profile {local|server}" >&2; exit 2; } || { echo "usage: $0 --profile {local|server}" >&2; exit 2; }
if [[ "$profile" == local ]]; then if [[ "$profile" == local ]]; then
verify_internal_semantic_infrastructure_docs
verify_local_guide verify_local_guide
verify_windows_line_endings_guide verify_windows_line_endings_guide
verify_pi_management_guide verify_pi_management_guide
verify_local_installation_example verify_local_installation_example
else else
verify_internal_semantic_infrastructure_docs
verify_server_guide verify_server_guide
verify_reverse_proxy_nginx_guide verify_reverse_proxy_nginx_guide
verify_reverse_proxy_caddy_guide verify_reverse_proxy_caddy_guide