Compare commits

12 Commits
Author SHA1 Message Date
User 2f53512e4d docs: record server release and hand off remaining acceptance checks
Publish documentation / publish (push) Successful in 32s
2026-09-27 00:41:37 +02:00
Codex 497ab84031 docs: pin server handoff to released main revision
Publish documentation / publish (push) Successful in 33s
2026-09-26 16:42:36 +02:00
Codex 0d2e573e0d fix(ui): reset session view on stop and exit 2026-09-26 16:40:37 +02:00
Codex bd416f7327 Fix new-question landing and question-language HITL
Publish documentation / publish (push) Successful in 34s
Reset the activity panel when starting a new question so the landing navigation is restored. Detect and persist the original question language, pass it through runtime and widget descriptors, and scope HITL controls to that language.

Validated with gate, session, backend and frontend tests, TypeScript checks, Ruff and strict docs build. Rebuilt and restarted local core/frontend; both healthy and serving HTTP successfully.
2026-09-21 19:47:22 +02:00
Codex 23e52c80de Record verified Qwen documentation publication
Publish documentation / publish (push) Successful in 23s
2026-09-21 16:27:10 +02:00
Codex 84084bba37 Fix Qwen session tool calls and expose thinking compatibility
Publish documentation / publish (push) Successful in 30s
2026-09-21 16:23:51 +02:00
pinoricci1956 efd7d788d9 correzione scroller verticale pagina di configurazione catalogo 2026-09-16 11:59:51 +02:00
Codex b1c510a097 fix(docs): preserve theme assets in deny-by-default publication
Publish documentation / publish (push) Successful in 36s
2026-09-16 09:36:30 +02:00
Codex 5f3680a0fb docs: record verified live manual publication
Publish documentation / publish (push) Successful in 28s
2026-09-15 14:39:46 +02:00
Codex 4ff91e8d6e docs: consolidate historical records and verify public manual publication
Publish documentation / publish (push) Successful in 29s
2026-09-15 14:37:29 +02:00
Codex 5f3a7f5975 docs: record stale public site publication blocker
Publish documentation / publish (push) Successful in 23s
2026-09-15 10:28:49 +02:00
Codex 043ffdfad6 docs: separate public manual from internal project documentation
Publish documentation / publish (push) Successful in 27s
2026-09-15 10:26:35 +02:00
126 changed files with 3380 additions and 6882 deletions
+4
View File
@@ -30,3 +30,7 @@ coverage/
data/ data/
sessions/ sessions/
workspace-registry/ workspace-registry/
.tht/
deploy/local/
+20 -4
View File
@@ -7,6 +7,11 @@ on:
paths: paths:
- "docs/**" - "docs/**"
- "mkdocs.yml" - "mkdocs.yml"
- "scripts/build-docs.sh"
- "scripts/verify-public-docs.py"
- "scripts/test-verify-public-docs.py"
- "scripts/verify-auth-docs.py"
- "scripts/test-verify-auth-docs.py"
- "docs/requirements.txt" - "docs/requirements.txt"
- ".gitea/workflows/publish-docs.yml" - ".gitea/workflows/publish-docs.yml"
workflow_dispatch: workflow_dispatch:
@@ -34,14 +39,25 @@ jobs:
with: with:
python-version: "3.x" python-version: "3.x"
cache: pip cache: pip
cache-dependency-path: docs/requirements.txt cache-dependency-path: docs/requirements.lock
- name: Install MkDocs dependencies - name: Install MkDocs dependencies
run: python -m pip install -r docs/requirements.txt run: python -m pip install -r docs/requirements.lock
- name: Test public documentation boundary
run: python scripts/test-verify-public-docs.py
- name: Test current authentication documentation
run: |
python scripts/verify-auth-docs.py auth
python scripts/verify-auth-docs.py dwh
python scripts/test-verify-auth-docs.py auth
python scripts/test-verify-auth-docs.py dwh
- name: Build documentation - name: Build documentation
# Some documented source files intentionally live outside docs/. run: |
run: mkdocs build mkdocs build --strict
python scripts/verify-public-docs.py
- name: Publish generated site to the pages branch - name: Publish generated site to the pages branch
working-directory: site working-directory: site
+101 -704
View File
@@ -1,704 +1,101 @@
# ThothII — Project State # Project state
Last updated: 2026-09-14. Updated: 2026-09-27. This is a current snapshot, not a release diary. Stable commands
and invariants are in [AGENTS.md](AGENTS.md); prior snapshots remain in Git.
This file is the short operational snapshot. Stable commands and the architecture mental model
live in `AGENTS.md`; current design and runtime contracts live under `docs/architecture/`, ## Current contracts
`docs/contracts/`, `docs/adr/`, and `docs/evidence.md`. Superseded plans and reports are
available from Git history rather than duplicated in the working tree. - React supports full/embedded rendering independently of local/OIDC/upstream auth,
with EN/IT UI and immutable session interaction language. See
The guarded server migration from a legacy checkout to the schema-v2 installation, Gitea source, [application shell](docs/architecture/application-shell.md) and
Authentik, internal catalog/embedding services, and the PSD workspace repository is documented in [localization](docs/operations/shell-and-localization.md).
`docs/operations/server-upgrade-gitea-workspace-v2.md`. Treat its operator gates and rollback - PostgreSQL Metadata Catalog owns database identity, binding, schema, descriptions,
requirements as mandatory; do not replace the running server stack in place. sensitivity and relationships for all core consumers. Workspace schema v4 contains
identity and optional Evidence only. Installation schema v2 is the authored model
## Session review layout deployed — 2026-09-14 catalog source. See [overview](docs/architecture/overview.md) and
[model configuration](docs/general/pi-configuration.md).
Session-only dialogs now use the visible app bounds: artifact/column review grows - The harness owns workflow persistence; chat is not the durable session record.
up to 80rem wide and the available height; short confirmations grow to 40rem. Memory uses PostgreSQL authority and derived Qdrant dense/BM25 search. Editable
Existing primary actions appear above and below session forms and review content, Evidence has local archive authority and manual consolidation. File save, search
with shared handlers, validation and pending state. Stop/delete focus Cancel; activation and Git publication have distinct outcomes. See
rename focuses the name field. Administration surfaces are unchanged. The activity [Memory](docs/gestione-memory.md), [Evidence](docs/contracts/curated-evidence-v4.md)
shortcut is relabelled To Administration / Vai all’Amministrazione, retaining its and [consolidated release evidence](docs/reports/knowledge-archives-release.md).
workspace-management destination. 778 frontend tests, five responsive browser - Reference preprocessing must not clear Memory. Use the installation-scoped
scenarios, typecheck, translations and the production build passed. The owner `tht --installation /absolute/path/thothii-installation.yaml workspace preprocess run`
authorized deployment and the server frontend was recreated at 18:09 CEST with tag `b1723c34-session-dialogs-20260914`. Frontend is healthy, and its [contract](docs/contracts/workspace-preprocessing-cli.md).
Omics serves the new assets, and doctor passes 13/13 checks. Core and Omics web - DWH sessions are read-only. SSH tunnels support database-management diagnostics
were not restarted. See DESIGN.md for the layout contract and and metadata synchronization, not NL→SQL session creation; use direct or REST
`docs/reports/2026-09-14-session-dialogs-release.md` for provenance and rollback. transport for sessions.
## Session composer and empty Memory fix deployed — 2026-09-14 ## Installation and workspace boundaries
The embedded shell now caps its height at the portal mount height, keeping steering Fresh standalone installations follow the manual terminal procedures in
and Stop & save visible. Empty authoritative Memory archives return zero results [Italian](docs/install/standalone-manual-it.md) or
without requiring embedding/BM25; SQL-rule embedding is lazy and shared. The live [English](docs/install/standalone-manual-en.md), without an installer or launcher.
empty PSD archive reproduces 503 with the current image and succeeds with the The [Compose reference](docs/operations/compose-reference.md) is for maintainers,
candidate. 54 Memory tests, 90 frontend tests, five browser scenarios and both image not another quick start. Catalog and Memory migrations are explicit.
builds passed. The owner authorized the restart and core/frontend were recreated on
2026-09-14 at 17:18 CEST with tags `49333a2d-session-memory-fix`, from code committed Descriptors, authentication, provider credentials, certificates and runtime bindings
as `d6cdffea`. Both are healthy; production Memory search returns `[]`, Omics serves stay in protected installation-local paths. Do not copy secrets into examples or
the corrected CSS, native doctor passes 13/13 checks, and admissions are reopened. workspace Git. Legacy runtime snapshots may use absolute paths and protected
Session inventory is preserved. Backups and rollback images are retained. Native `harness/.env`; do not silently relocate them.
CLI diagnostics must run as installation owner UID 10001 with access to Compose;
see `docs/reports/2026-09-14-session-layout-memory-fix.md` for exact commands and PSD authoring is separate at `/Users/mp/projects/tht-workspace-psd`. Its GitHub
the remaining interactive browser acceptance. repository was copied to private Gitea
[workspace_psd](https://git.tylconsulting.it/mptyl/workspace_psd), preserving both
## Full/embedded shell and bilingual interface branches. It is a copy, not automatic synchronization. Running installations were
not repointed to a different workspace remote.
Full/embedded shell, EN/IT UI, immutable session interaction language, dark theme,
fullscreen and full-mode logout are implemented. The local Mac descriptor explicitly ## Recorded deployments and server authority
sets `shell.mode: full` and `shell.defaultLocale: en`; the native `tht` and local
core/frontend images were updated on 2026-09-13. Omics uses embedded with server-side Use the ordered [server handoff](docs/operations/server-codex-handoff.md) for
identity verification and a replaceable presentation-only PortalAdapter; this does coordinated ThothII/Omics upgrades. Omics integration uses GitHub
not mean this branch was verified on the production server. Its changes are `Dallavilla-Tiziano/omics_portal`, with no Gitea relay prerequisite. Omics uses
in Omics commit `95154e1`; production deployment remains pending. embedded/upstream identity, not another ThothII OIDC login. Read
See `docs/operations/shell-and-localization.md` for integration and installation [upstream authentication](docs/install/authentication-upstream.md) before changes.
instructions and `docs/reports/2026-09-13-full-shell-implementation.md` for tests,
independent reviews, local browser checks and rollback details. Last recorded application deliveries (not a fresh runtime attestation):
### Documentation handoff before branch closure — 2026-09-13 - [Coordinated ThothII/Omics release](docs/reports/2026-09-26-server-release-execution.md):
core/frontend `497ab840-preflight`, Omics proxy fix `928f7e9f` with existing web
Current rendering architecture is in `docs/architecture/application-shell.md`; image retained. Automated acceptance passed; on September 27 the operator confirmed
the exact portal identity/proxy contract is in `docs/install/authentication-upstream.md`. browser access, UI controls and session start/stop/resume. Functional browser
`docs/operations/server-codex-handoff.md` is the current server delivery/deploy acceptance passed; remaining extended checks are handed off in the
runbook, including Omics source integration from GitHub, configuration, tests and [server acceptance follow-up](docs/reports/2026-09-27-server-acceptance-handoff.md).
rollback. It supersedes the earlier Omics repository-relay instructions. The acceptance matrix is - [Session dialogs](docs/reports/2026-09-14-session-dialogs-release.md):
`docs/testing/authentication-manual-acceptance.md`. README, documentation navigation, `b1723c34-session-dialogs-20260914`, frontend-only.
user/installation/authentication guides and descriptor examples point to these - [Session layout/Memory fix](docs/reports/2026-09-14-session-layout-memory-fix.md):
paths. Local examples explicitly use full/en; the projected server example is `49333a2d-session-memory-fix`, core/frontend.
for standalone OIDC, not Omics upstream. No runtime configuration or deployment
was changed by that documentation pass. The 2026-09-14 delivery is prepared for Keep those reports and rollback instructions while operator gates remain open.
promotion to main; the actual merge is recorded in Git. PSD acceptance remains pending. A later deployment does not prove every earlier acceptance item passed.
### Navigation readiness and session accordions — 2026-09-13 ## Remaining acceptance and design gates
The Workspace navigation button now carries an accessible green/red readiness - Fresh-machine Mac, Windows/WSL2 and Linux installation acceptance, including real
dot instead of a separate text row. Green requires a selected workspace and a DWH/model endpoints, remains a separate operator exercise.
successful, current `ready` response; checking, unavailable and other states are - Real IdP/portal login, logout, embedded interaction and PSD semantic acceptance
red, with the state exposed through the tooltip and accessible description. follow the [manual matrix](docs/testing/authentication-manual-acceptance.md) and
The backend readiness gate is unchanged. delivery reports; synthetic tests do not close them.
- [Security hardening](docs/plans/2026-09-08-security-hardening-prd.md) is a draft.
Active sessions (non-archived) and Archive both start collapsed. Their adjacent Revalidate SEC01–12 and obtain design approval before implementation or real
headers are the only visible content below the scope tabs: no Sessions heading server/IdP/DWH mutation.
or external selection toolbar. Each nonempty panel owns its Select all and - Optional NER remains opt-in; labeled Italian quality, benchmark and licensing
bulk-delete controls, scoped to that list and preserving the other's selection. acceptance are not implied by document cleanup.
As of 2026-09-14, only one section can be open at a time, and either can be - Semantic aliases, value descriptions, synonyms/concepts, dialect and multi-schema
collapsed. Empty lists show only "No sessions yet." The open section uses the extensions remain explicit design work. Current sensitivity delivery follows the
remaining sidebar height, with scrolling content capped at `min(18rem, 35dvh)`. The mobile Catalog contract; additional policies require their own acceptance.
navigation dialog also provides a bounded height. Keyboard controls and labels - Legacy database UI fallback (`?db-ui=legacy`, dev/staging) and prototype removal
are retained. See `DESIGN.md` and `docs/guida-utente.md` for the UI contract. remain subject to owner acceptance.
Verification: 768 frontend unit tests, 20 browser scenarios (including 80 mocked ## Documentation maintenance
sessions at 390/1280px), TypeScript and the production frontend build passed.
Only the Mac frontend was recreated; it is healthy at `127.0.0.1:8080`. MkDocs publishes only 20 product/operator pages and five approved assets.
Core, catalog, Qdrant and embedding containers were not changed. The prior Architecture, contracts, ADRs, plans, research, tests and release evidence are
frontend image is retained as excluded from HTML and search. The repository itself is public: editorial exclusion
`thothii-frontend:before-single-session-accordion-20260914` for rollback. is not confidentiality.
### Current Omics delivery and server handoff — 2026-09-14 The [cleanup record](docs/maintenance/2026-09-15-documentation-cleanup.md) records
retired sources and retained gates. Main contains source; Actions generates the
The owner corrected the delivery requirement: Omics is obtained from GitHub, `pages` branch. The live site requires the separate explicit deployment described
not relayed to another repository as part of this deployment. Any optional in [public manual publication](docs/operations/public-docs-publication.md).
server-side repository copy is solely the owner's separate concern. This
supersedes the 2026-09-13 relay agreement, including historical delivery notes
in the Omics branch. Do not make another remote publication a prerequisite.
GitHub branch `codex/thothii-embedded-shell` at
`https://github.com/Dallavilla-Tiziano/omics_portal.git` was reverified at
`fca10901a73666ca257d8f4cc4b77066295c400a` (functional commit `95154e1`).
The server operator integrates it with the current code in the confirmed Omics
checkout, normally `/home/chirone/omics_portal`, preserving later server changes.
`docs/operations/server-codex-handoff.md` is the authoritative ordered procedure:
inventory, source verification, native CLI and installation projections,
embedded/upstream auth, Omics templates/static assets/proxy, coordinated rollout,
acceptance and rollback. Server deployment and real IdP acceptance remain pending.
## Current product shape
### Shared Memory/Evidence typography — 2026-09-13
Both detail readers now share Manrope and fixed reading roles: 24px card title,
20px section headings, 16px/1.65 narrative text, and 14px metadata/code. Authored
Markdown subheadings remain subordinate to field headings; inline/fenced code no
longer shrinks cumulatively. Full-width layout, paragraph separation and isolated
FAKE examples are retained. All 762 frontend tests and 18 browser scenarios pass,
including computed typography checks at 390/1280/2400px; typecheck, i18n and Docker
build pass. Only Mac frontend was recreated: `87fca0dc5e19`, image `19f1704b6bb2`,
healthy. Core and data services are unchanged. Rollback image:
`thothii-frontend:before-knowledge-typography-20260913`. See
`docs/reports/2026-09-13-knowledge-typography.md`. No PSD/Omics deployment.
### Knowledge reading and isolated fake Memory examples — 2026-09-13
Memory/Evidence details now use all available width, with display-only paragraph
splitting for long plain prose and preserved code/Markdown/source data. Evidence
provenance renders Markdown; copy controls are accessible two-sheet icons.
Memory's separate Formatting examples section contains four FAKE cards (one per
family), automatically expanded for an empty archive. These client-side examples
cannot be edited/saved/indexed and are never submitted to the model or Memory API.
No real archive content was changed. 762 tests, 18 browser scenarios, typecheck,
i18n and Docker build pass. Only the Mac frontend was recreated: `fc4286b5406d`,
image `cbe0fe0dce55`, healthy; other services unchanged. Rollback image:
`thothii-frontend:before-knowledge-reading-20260913`. Details in
`docs/reports/2026-09-13-knowledge-reading.md`; no PSD/Omics deployment.
### Unified Session entry and complete tab borders — 2026-09-13
The sidebar has one Session/Sessione button: return to the current unfinished
session (including pending creation) without resetting/reconnecting it; otherwise
prepare a new question with existing readiness and dirty-edit guards. Creation
still requires submitting a question. A cold document panel is not a running session.
Session tabs now have 11px horizontal/3px vertical padding, at least 38px height,
and matching 1px borders on every side (gray inactive, red active), with no shared
baseline or overlapping bottom border. This supersedes the earlier border removal.
757 frontend tests, 15 browser scenarios, typecheck, i18n and Docker build pass.
Mac frontend `2844778d311c`, image `d58dccfee3f9`, is healthy; only that service
was recreated. Core/data services are unchanged, with no Omics or server deploy.
Rollback image: `thothii-frontend:before-session-navigation-20260913`.
### Login copy and language-selector focus — 2026-09-13
Removed the redundant login eyebrow/icon and installation-account explanation;
the main sign-in heading remains. The full-header language select no longer
shows an outer focus ring after pointer interaction; keyboard focus remains
visible, including after returning with Tab. EN/IT login and both themes are
covered by 36 targeted tests and 14 browser scenarios; typecheck/build pass.
Only the Mac frontend was rebuilt/recreated: `204efd40d3eb`, image `7dd0752ff823`,
healthy. Other containers are unchanged. Rollback image:
`thothii-frontend:before-login-focus-20260913`. No production deployment.
### Full-header and layout refinements — 2026-09-13
The owner's four visual adjustments are implemented: full header uses Omics
`#CB333B` in both themes with a light complete wordmark/controls; Database status
has 16px clearance below its top divider; workspace/model/Done share a compact
desktop row; session-scope tabs have no bottom border. Embedded has no extra header.
Verified with 71 targeted frontend tests, 11 browser scenarios, typecheck and
Docker production build. The Mac frontend was recreated alone and is healthy
(`8a1608ad7fc6`, image `a2f489ebe9de`); Core/data-service containers are unchanged.
Full/en is retained. Rollback image: `thothii-frontend:before-header-layout-20260913`.
See `docs/reports/2026-09-13-header-layout-refinements.md` for validation and rollback.
### Visual review integrated with the full/embedded shell
At the owner's request, the seven commits through `a59624a6` from
`codex/ui-visual-review` are integrated with the shell/i18n work in this checkout,
`codex/prototype-administration-pages`. Both branches started at `2d1b714e`; the
first full-shell build omitted that lateral branch and regressed the installed UI.
The integrated source retains bundled Manrope, shared type roles, catalog/context
alignment, wordmark sizes and composer autosizing alongside full/embedded and EN/IT.
Local Docker was updated at 12:54 UTC on 2026-09-13: frontend image `605a6e6bba68`,
healthy; Core and all data-service containers were unchanged. Mac remains full/en.
The immediate pre-merge frontend is retained as
`thothii-frontend:before-visual-shell-merge-20260913`.
Verification: 755 frontend tests, 11 Playwright visual/interaction scenarios at five
widths, translation-catalog checks, typecheck and production build. See
`docs/reports/2026-09-13-visual-shell-integration.md` for delivery, provenance and
rollback details. The separate visual-review worktree and its rollback images are
retained. No merge to `main`, push or production deployment is part of this delivery.
Gitea #28–#31 follow-up is implemented in this checkout: Workspace's four tabs,
Database list-first entry without the preparation footer, full-height Pi instructions
with installation-host OS selection, and short Admin navigation labels. Core and
session behavior are retained. At the owner's request, these changes were rebuilt into
local Docker on 2026-09-12 at 18:31 UTC. Core/frontend are healthy and the UI at
`http://127.0.0.1:8080` serves the updated bundle. The host projection now supplies
`THT_HOST_PLATFORM=darwin`, so Pi selects macOS rather than the container's Linux OS.
Persistent dependency containers and volumes were unchanged. Rollback images are tagged
`thothii-core:before-admin-28-31-20260912` and
`thothii-frontend:before-admin-28-31-20260912`. See
`docs/reports/2026-09-12-admin-issues-28-31.md` for verification and deployment details.
The latest context-shelf A and five Administration pages are implemented locally.
At the owner's request, local Docker project `thothii-18998cca7b0a` was rebuilt and
its core/frontend recreated on 2026-09-12. The real UI at `http://127.0.0.1:8080`
serves shelf A; both services and their existing dependencies are healthy.
Runtime model projections were regenerated as schema v2 with the single
`zai/glm-5.3` interaction default. Persistent services/volumes were not recreated.
The local launcher `/private/tmp/thothii-memory-preview.sh` now adds
`/private/tmp/thothii-context-a.compose.yaml` last, building from this checkout
instead of the earlier Memory worktree. Previous images are retained under
`thothii-core:before-context-a-20260912` and
`thothii-frontend:before-context-a-20260912`. Remote server deployment remains pending.
Core/session behavior is retained; one independently remembered workspace/model
pair controls both Core and Admin. Unsaved Admin changes block navigation and
context changes; operation activity locks the selectors. See
`docs/reports/2026-09-12-context-shelf-a-implementation.md` for verification and the
remaining server/Omics integration gate. Prototype alternatives remain untouched.
ThothII is a human-in-the-loop datamart builder with three independently built layers:
```text
frontend (React/SSE) → backend (Fastify) → pi --mode rpc → tht/harness → DWH (read-only)
```
The harness owns the deterministic eight-phase NL→SQL workflow and all session persistence.
The backend remains a process/RPC/SSE bridge for sessions and now also owns an isolated PostgreSQL
metadata catalog for administrative database configuration. The frontend renders the review gates
and keeps the live transcript in memory. See
`docs/architecture/components.md` for the detailed component and data-flow map.
## Memory M1–M3, Evidence E1–E3 and joint workflow repair X1 implemented
Browser authorization now derives permissions from validated session roles using the current
catalog. This fixes Memory/Evidence navigation remaining disabled for administrators whose
remembered login predates those permissions; refreshing the page loads the updated permissions.
The owner requested two independent administration projects: Memory management and Evidence
management. Their navigation entries must sit immediately after Database management, as peers;
neither page belongs to Database management. Both require complete browsing, filtering, and CRUD
without an active core session. Shared requirements and the two project briefs are linked from
`docs/plans/2026-09-08-memory-evidence-administration.md`. Memory administration is implemented;
Evidence administration is implemented through external file editing and explicit consolidation.
Evidence editing requires an explicit evolution of the current authoring/publication contract.
The M1 specification is at `docs/plans/2026-09-08-memory-m1-spec.md` and published as
[M1 — Archivio autorevole e amministrazione delle Memory Card](https://git.tylconsulting.it/mptyl/ThothII/issues/27)
with the `ready-for-agent` and `enhancement` labels.
It covers the authoritative store, administrative CRUD, projection recovery, and current
Memory/exemplar producer integration. Its accepted test boundaries are the public harness
service, Fastify APIs, and the AppShell page, joined by a focused real-stack browser path.
The owner confirmed those boundaries and authorized publication on 2026-09-08.
M1 is implemented locally: PostgreSQL authority in `thoth_memory`, versioned installation
migrations, admin CRUD for all four families, structured dependencies and links, explicit
projection recovery, verified recall and current workflow producers. The page requires no
active session or DWH binding. The existing `catalog-migrate` preparation service now runs
Memory migrations as well. Existing installations need that preparation before using M1;
the initial implementation did not deploy or migrate the owner's stacks.
The integrated browser check passed with real authentication, Fastify, ThtRunner, harness,
PostgreSQL and Qdrant; embeddings were deterministic and unrelated Pi/session activity used
test fixtures. See `docs/plans/2026-09-08-memory-m1-validation.md` for results and commands.
M2 is implemented locally: Memory dense/BM25 fusion, physical and business scope filters,
bounded outgoing-link expansion and joint ranking, all resolved against current PostgreSQL
authority. Migration `002_hybrid_projection.sql` makes old dense projections pending until
explicit retry/rebuild; Reference remains separate. The real retrieval check uses the configured
`qwen3-embedding:0.6b` model, a separate Ollama process with a read-only model-volume mount,
and isolated PostgreSQL/Qdrant resources. See `docs/plans/2026-09-09-memory-m2-validation.md`.
M3 is implemented: editable F8 summary grounded in effective approved decisions, explicit
updates with concurrent-edit protection, selected-card/link transactions and durable review
receipts. Finalization no longer saves exemplars implicitly. SQL rules and explained errors
are consulted in the existing F4/F6/F7 gates. Successful Catalog physical synchronization
performs dependency cleanup, preserving the original removals for recovery across restarts.
Migration `003_review_receipts.sql` is required. Validation includes a real GLM 5.3 generation
case against synthetic PostgreSQL data; see `docs/plans/2026-09-09-memory-m3-validation.md`.
Evidence administration and the joint X1 conflict-repair increment are now implemented.
The same local preview was subsequently rebuilt with M3 and migration 003 applied.
Core and frontend now include the final Memory review and physical dependency cleanup.
On 2026-09-09, at the owner's request, the local PSD Docker installation
`thothii-18998cca7b0a` was updated from this worktree. Core/frontend images were rebuilt,
Catalog and Memory migrations completed, and all five services became healthy. The UI is at
`http://127.0.0.1:8080`, using the existing local authentication and persistent volumes.
The `psd-clinical` authoritative Memory archive is initially empty (no legacy import).
The existing installation configuration remains in `/Users/mp/projects/ThothII/deploy/psd/`;
`/private/tmp/thothii-memory-preview.sh` invokes its Compose files with a final build-context
and migration-command override from this worktree. A future build from the main checkout
will use that checkout's code, so retain the worktree override until the changes are integrated.
Memory revision history and compatibility with existing development sessions are not requirements.
The agreed Memory scope includes reusable domain clarifications, SQL construction rules, solved
questions, and explained, approved mistakes to avoid. This extends the current runtime's
`concept_clarified`-only reusable Memory contract. The owner also approved an editable final summary
for proposed additions/updates and Memory consumption in the relevant existing review gates.
Further agreed behavior includes persistent corrections for Memory/Evidence conflicts through
explicit choices, deletion of Memory with invalid dependencies after successful physical schema
synchronization, and bounded functional/regression tests instead of a general quality benchmark.
In-house Memory evolution is approved, including hybrid Qdrant retrieval and explicit card links
traversed in core, without a dedicated graph database. Both capabilities belong to the current
scope. PostgreSQL is the agreed authority for Memory Cards, links, and schema dependencies;
Qdrant is a rebuildable index. Links are reviewed with cards and editable in Administration;
deleting a card removes its incident links while preserving the other cards. The accepted,
implemented architecture is recorded in
`docs/adr/0018-use-postgres-for-memory-and-qdrant-for-retrieval.md`.
For Evidence administration, Q12 rejects a separate editorial publication workflow.
The final R0 direction uses external editors and a manual consolidation command that
validates structure, reports required corrections, updates derived metadata, and
activates the local Evidence index. The operator then checks the diff and runs Git
commit/push manually. No watcher, automated Git, or web editor is required.
The owner's later simplification
instruction removes the requirement to support administrative edits while core work
is in progress. Deliberate corrections made by the workflow itself remain supported.
The context specialist writes Evidence drafts independently of the installation and
without PostgreSQL access; the system refines them and stores them locally for use
and maintenance. The revised Q11 recommendation separates external drafts from a
durable local canonical file archive, removing automatic commit/push from CRUD.
The owner has now accepted local files and requires discoverable, editable Markdown
for nontechnical domain specialists, with no JSONL management surface. Editors on
Mac/PC or vim/nano on the server are the chosen R0 editing surface. Evidence
management keeps browsing/filtering/detail and clearly identifies the persistent
working tree, each Markdown file's actual host path, and the manual commands.
E1 implements editable Curated Evidence v4, deterministic legacy conversion, persistent
local files and a consolidation API with immutable candidates, manual provenance,
deletion records and recoverable activation. E2 connects its installed command, runtime source
selection and administration page. The operator completes Git steps manually.
All 35 PSD units were converted on an isolated copy with identical IDs and typed content.
Tests exercise visible edits through normalization, indexing and recall with real Qdrant;
see `docs/plans/2026-09-09-evidence-e1-validation.md` and
`docs/contracts/curated-evidence-v4.md`. E2 is now running on the local Docker preview:
all 35 PSD units were converted and indexed through the installed command. The editable
host archive is `/Users/mp/projects/ThothII/deploy/psd/evidence-registry/repo/psd-clinical/evidence`.
The original registry-volume checkout and original author repository were retained.
The installation descriptor now includes `workspace-bindings.yaml` and `evidence-host.yaml`
so native maintenance uses the same volumes and checkout as the preview. Descriptor backup:
`/private/tmp/thothii-installation-before-e2.yaml`; previous native binary: `/private/tmp/tht-before-e2`.
The launcher remains `bash /private/tmp/thothii-memory-preview.sh`; its worktree build override
is still required until integration. Runtime lease filenames now include rendered bytes so
upgrading the Evidence renderer does not collide with old immutable configs; Catalog
input fingerprints and readiness are unchanged. See `docs/plans/2026-09-09-evidence-e2-validation.md`.
E3 adds explicit source acquisition/refinement and durable comparisons in Evidence management.
Local drafts go in `evidence/incoming/`; original local documents and configured HTTP/S3
sources are reacquired only by **Import or refresh sources** or installed
`tht workspace evidence refresh --workspace <id>`. Keep/replace decisions, including the
native `workspace evidence decide` command, activate through the existing archive/index
path and have durable retry state. Acquired source versions and remote provenance stay
local; old documentary lineage can coexist with current manual declarations.
The installed preview's 35 PSD sources were verified unchanged with no new proposals.
The previous native CLI is backed up at `/private/tmp/tht-before-e3`.
See `docs/plans/2026-09-09-evidence-e3-validation.md` for tests and validation limits.
X1 adds `reviewer_archive_repair`: closed alternatives with complete before/after content,
explicit rejection/reformulation, admin-only application, durable session receipts and
retry of saved but inactive corrections. The coordinator uses the canonical Memory and
Evidence services; phase approval remains separate. Migration `004_archive_repairs.sql`
is required. See `docs/contracts/archive-repair.md` for authorization and recovery limits.
X1 validation is recorded in `docs/plans/2026-09-09-archive-repair-x1-validation.md`:
both archives were corrected and retrieved through the actual CLI with real PostgreSQL
and Qdrant; desktop/mobile widget behavior and permission failures were verified separately.
The local preview images include X1 and migration 004 is applied. Follow-up technical
acceptance passed with configured GLM 5.3 generating closed conflict alternatives and
the chosen Memory correction persisted and retrieved. An authenticated browser test
also verified both administration pages, navigation order, Evidence filtering, workspace
404s, desktop/mobile layout, and Evidence survival after Memory deletion. All approved
technical increments/checks are complete. A real PSD domain-conflict session remains
the reviewer's semantic acceptance check; automated cases did not modify PSD knowledge.
The joint browser inspection also fixed mobile archive navigation: below 768px,
Memory and Evidence keep the full content width and open navigation in the shared
accessible dialog. Desktop retains its sidebar; the local frontend image includes this fix.
The owner accepted the remaining simplifications and requested explicit clarification
of the core format change and the simple terminal-based Git check. E1 must adapt
the Evidence parser, renderer, authoring, validation, and normalization; convert
existing files and reindex; and verify that visible edits reach core consumption.
Preserve the internal typed model where possible. This precedes the administrative
page and is not merely a presentation change. Human inspection uses normal Git
status and optional line-level diff commands; no custom diff viewer or mandatory
double review is required.
The suggestion of making PostgreSQL the Evidence authority was withdrawn after this
clarification; it was never implemented or accepted as a replacement for Q11.
Q13 keeps manual corrections active when updated sources contradict them, until an
administrator resolves the comparison; deleted Evidence must not be regenerated
automatically. Q14 allows direct manual creation and records a manual declaration
as the current source, preserving any original document as distinct provenance.
Q15 refreshes external sources only on explicit administrator request; normal saves
and lookups do not reacquire them. These decisions, now implemented through E1–E3, are recorded in
`docs/adr/0019-author-evidence-in-app-with-automatic-activation.md`.
The decision-by-decision review is recorded in
`docs/plans/2026-09-08-memory-evidence-simplification-review.md`; it distinguishes the
new constraints from the revised technical recommendations. It also specifies a
sequential save with minimal durable retry state and direct Memory cleanup after
successful schema synchronization, without new queues or event infrastructure.
The delivery order remains Memory, Evidence, and persistent conflict repair between
both modules. Local curated Evidence must survive preprocessing Clear and be backed
up as primary data; the Qdrant projection remains rebuildable.
No runtime gate change or administrative page is implemented yet.
## Evidence restructuring — accepted
The evidence restructuring and PSD migration completed real acceptance on 2026-08-25.
- The curated PSD revision contains 35 approved Evidence units and 60 review items.
- The PSD authoring repository publishes all 35 units using Curated unit schema v3. Its table-free
presentation uses hidden canonical metadata, wrapping Markdown scope lists, list-based enum
values, and collapsed technical provenance. Long domain rules now have a deterministic
human-readable presentation while retaining their exact canonical text for vector ingestion.
`tht evidence migrate <workspace-root>` performs the deterministic v1/v2 upgrade and older-v3
presentation rewrite without model calls. The structured-rule PSD rewrite is currently local and
pending commit/publication.
- The accepted snapshot is
`psd-clinical-675990d90eae51da6f2bd51b1ae2609f245772ef-snapshot`.
- The active generation is `gen:f968b3bd7a553dbfef3cf47093698f2bc7f95f11`.
- Retrieval acceptance reached 20/20 Hit@10.
- A real session, `20301df7-cad7-403d-a4c1-9f35c9d07b66`, completed F1–F8 with five
receipts, three CTEs, and a final result of 78 patients.
- The durable acceptance record is
`docs/testing/evidence/evidence-restructuring-psd-acceptance-2026-08-25.md`.
The canonical authoring, validation, publication, materialization, and preprocessing flow is
documented in `docs/evidence.md`. The governing contracts are
`docs/contracts/workspace-evidence-v3.md` and
`docs/contracts/workspace-preprocessing-cli.md`.
The incremental server procedure for the `260906-preprocessing-complete` release is
`docs/operations/server-handoff-260906-preprocessing-complete.md`.
## Workspace preprocessing and configuration
The native host CLI `tht` is the operator surface. Workspace preprocessing runs through:
```sh
tht --installation /absolute/path/thothii-installation.yaml \
workspace preprocess run --workspace <workspace-id>
```
This complete one-shot command uses the profile-gated `workspace-maintenance` service. Partial DWH,
schema, Evidence, and vector mutation commands are retired.
The right Administration sidebar invokes that same operation for the selected workspace. It shows
only current readiness or the latest bounded failure diagnostic; there is no preprocessing history.
Known non-ready state disables **Session** when it would start a new question,
while backend admission remains authoritative.
The same control exposes an inline-confirmed **Clear** action to remove replaceable reference
vectors, LSH, corpus, and checkpoints while preserving the separate Memory collection. The host CLI
equivalent is `workspace preprocess clear`.
Each workspace now uses `<workspace>-reference` for Schema, relationships, and Evidence and
`<workspace>-memory` for `memory` and `solved_question`. Clear and preprocessing own only the former.
LSH ownership additionally binds the Catalog database ID and Metadata Content Revision, so derived
values cannot be reused across database identities or Catalog revisions.
Workspace descriptors use schema v4 and contain only workspace identity and optional Evidence.
PostgreSQL Metadata Catalog owns database identity, binding, schema, descriptions, sensitivity, and
relationships; model, provider, embedding, and vector-store configuration is installation-owned. For
PSD, workspace content and runtime roots point to the
separate uncommitted repository `/Users/mp/projects/tht-workspace-psd`. Secrets remain outside
Git and are supplied only through installation-local protected files.
## Installation Model Catalog
`thothii-installation.yaml` schema version 2 is the only operator-authored source for session,
metadata-generation, and embedding models. The host `tht` lifecycle validates `modelCatalog` and
regenerates the backend catalog, Pi `models.json`/`settings.json`, and Compose override under the
installation-local `generated/` directory. Those projections are replaceable runtime adapters:
they are not edited, backed up, or treated as configuration.
Core and Administration now share one canonical `modelCatalog.defaults.interaction` per installation,
independent of workspace. Runtime catalog schema v2 contains only `defaultInteraction`; apply the host,
backend, and regenerated projections together. Equal legacy defaults normalize on read; conflicting
ones require an explicit operator choice. With Admin AI configured, selectable models are the
intersection of the Pi and LiteLLM adapters; Core-only installations remain supported without Admin AI.
The existing Core and Database controls share the operational selection. Explicit model choices are
remembered per authenticated user/application mount in this browser, not in installation settings.
Resume uses that selection/default while preserving the historical workspace/revision and manifest.
Every model must be manually exercised in both Core and Admin as documented in
`docs/general/pi-configuration.md`; validation is not a live model certification.
These changes and shelf A are now deployed to local Docker; remote deployment remains pending.
PSD DeepSeek Pro/Flash now share canonical `deepseek/...` identities across native Pi and LiteLLM,
using the existing `DEEPSEEK_API_KEY` bundle entry. Its value was confirmed identical to the working
Pi key without exposing it; no secret files were changed. The duplicate `deepseek-metadata` descriptor
is removed locally and the tracked example uses the shared provider. `secret_env` overrides legacy
Pi auth only inside temporary runtime snapshots and fails closed if the bundle key is missing.
The original auth store/history and `zai/glm-5.3` default are preserved. Local Docker projections
and core/frontend were updated together on 2026-09-12. Regenerate projections with
the matching release for the separate server deployment.
Provider authentication declares
one explicit mode (`secret_env`, `pi_auth`, or `none`); `secret_env` names a protected bundle key.
The backend settings store now owns only the selected workspace and thinking level. Existing v1
installations use the explicit catalog migration command; schema-v3 workspace descriptors are
converted deterministically in their curator-owned repository before commit. Strict runtime loading
does not silently infer or merge legacy sources. ADR 0013 and
`docs/plans/2026-09-02-installation-model-catalog.md` record the decision and implementation.
## Database management
The database, table, and authoritative physical-schema catalog slices are implemented. Database
Management now opens the Fleet Ledger presentation by default inside `AppShell`, lists every YAML
workspace, creates at most one PostgreSQL database configuration per workspace, edits direct
PostgreSQL, REST API, or SSH-tunnel installation bindings, replaces write-only encrypted secrets,
and tests supported connector bindings. The surface keeps one responsive AG Grid visible at a time:
databases lead to tables, tables lead to columns, and relationships are a sibling database view.
Parent navigation remains explicit through the breadcrumb and emphasized back control.
Selection-scoped operations use one action selector plus an explicit **Run** control; ineligible
actions remain visible with their disabled reason, while row-scoped actions stay in the pinned final
column. The KPI strip reads installation-wide or selected-database aggregates from
`GET /catalog/metrics`. Database configuration, metadata editors, synchronization history,
description history, and sensitive-field review/history use the production APIs in right-side
drawers rather than prototype fixtures; closing a history drawer does not stop its background run.
Sensitive-field review is now driven by the versioned local `sensitivity-v4` policy, not by a
catalog model. The backend reads selected source tables through read-only, database-specific
adapters and makes every `sensitive | non_sensitive` draft decision in the TypeScript
`SensitivityClassifier`. A single validated match protects the column. Tables up to 1,000 rows are
fully scanned; larger tables use breadth-first 300, 1,000, and text-only 3,000-value targets, with a
five-second limit per source query and no global request deadline. Source failures fail the run
instead of yielding `unknown`; coverage remains visible separately from the proposal. Draft
assessments remain transient until an administrator explicitly saves them. Optional GLiNER2
evidence is CPU-only, offline, opt-in, and never replaces the deterministic decision point; see
`docs/operations/sensitivity-analysis.md`. The earlier v1 PSD shadow comparison kept NER disabled by
default; see `docs/reports/2026-09-02-psd-sensitivity-shadow.md`. The v2 comparison completed all
2,275 columns: CPU NER added 18 sensitive proposals and increased warm runtime from 50.1 to 61.3
seconds; see `docs/reports/2026-09-03-psd-progressive-sensitivity-shadow.md`.
Version 4 excludes declared `bigint` primary-key columns and conventionally named `pk bigint`
columns before source inspection, reporting both as non-informative structural identifiers while
distinguishing declared constraints from inferred roles.
Physical membership, source
comments, column types/default/nullability/PK positions, and constraint-level ordered FK pairs are
projections of the external schema. They cannot be created, renamed, or structurally edited by
hand, but administrators can explicitly clear catalog tables, columns, or relationships without
touching the source database, binding, configuration, or secrets. Table deletion cascades through
columns and relationships; table-scoped relationship cleanup includes incoming and outgoing
relationships. Curated and generated descriptions are editable; generated descriptions start null
and Database Management can generate or consolidate them for selected tables, selected columns,
all targets, or only targets whose Generated Description is missing.
Relationship Management is now reachable directly from each configured Fleet database. One
Relationship Map shows read-only Physical Relationships together with Generated and Manual Logical
Relationships, with Active, Excluded, and All filters. Administrators can add a single-column
relationship, run deterministic name/PK/type inference, exclude or restore a logical relationship,
or delete it permanently. Exclusion retains a tombstone that a rebuild cannot reactivate; permanent
deletion allows a later rebuild to infer the same endpoints again. Inference uses no LLM, embedding,
or source values. It supports normalized table-qualified names, unique non-generic PK names,
composite-PK source columns, and the `*time_key -> dim_time.<single PK>` warehouse convention while
ignoring bare generic names. Explicit table/column metadata cleanup remains a destructive boundary: it removes
the attached logical relationships and exclusions and requires a full schema synchronization before
inference or runtime publication can continue.
The previous Database Management renderer remains a temporary comparison fallback for development
and staging only: `?db-ui=legacy` is honored in Vite development or when
`VITE_DB_MANAGEMENT_LEGACY=true`; it is not a production presentation. The standalone Fleet Ledger
prototype on port `5173` also remains temporary until owner acceptance of the integrated surface,
after which both migration aids can be removed.
Schema refresh is one durable asynchronous engine with database-table, selected-table-column,
relationship, and full-database actions. Database-level menus expose only the table, relationship,
and full scopes; selecting tables exposes column synchronization plus manual column and relationship cleanup for that subset. Database selections
also expose manual table and relationship cleanup. Cleanup selections are atomic and share the
one-active-operation-per-database exclusion with synchronization. Runs have leases and
restart recovery, atomic apply, destructive-diff confirmation with re-scan, cancellation before
apply, retained history, and a live SSE log with polling fallback. Null metadata renders blank
rather than as a placeholder.
Direct PostgreSQL and strict known-host-verified OpenSSH use `pg_catalog`. REST bindings use the
typed full-snapshot `POST /rpc/schema_snapshot` contract when available. Servers such as the
current PSD endpoint that exposes only `POST /rpc/run_query` use one catalog-owned read-only query
to return the exact same strict v1 snapshot in a single round trip. Both paths remain fail-closed:
an absent capability, query error, partial result, or invalid snapshot applies no catalog changes.
SSH is not yet enabled for NL→SQL session runtime.
The catalog runs in the internal `catalog-db` PostgreSQL service. Kysely migrations are an explicit
one-shot `catalog-migrate` operation; `scripts/run-stack.sh` runs it before local startup. Runtime
sessions consume an immutable Catalog JSON snapshot tied to the runtime-config lease. It contains
the tables, columns, effective descriptions, sensitivity flags, and active relationships used by
the harness; PostgreSQL is the exclusive runtime authority for database metadata. Authored
workspace YAML remains limited to workspace identity and optional Evidence configuration. The
accepted design is recorded in ADR 0016 and the contracts under `docs/contracts/`.
Semantic aliases, value descriptions, synonyms, and concepts remain deferred to their dedicated
slices.
AI Description Generation uses the catalog's human-owned Sensitive Data Flag. The flag defaults to
`false`, including for newly synchronized columns. An administrator may request a local sensitivity
analysis for one selected database, selected tables, or selected columns. One deterministic
TypeScript classifier combines metadata, bounded source-content rules, and optional CPU-only NER;
no generative model decides the result. Its `sensitive` or `non_sensitive` assessments remain an
unsaved draft until the human reviews and saves any chosen flag changes, including a downgrade to
non-sensitive. Coverage is reported separately; interrupted history may count unprocessed columns.
Each started analysis records a separate Sensitivity Analysis Run with aggregate counters and safe
ordered events. The progress drawer opens before the synchronous request completes, polls the run,
and displays sanitized source-scan and local-NER phase/batch activity while classification is in
progress. This operational history never stores per-column assessments, source values,
matched spans, prompts, or free-form diagnostics. Saving a sensitive decision persists a sanitized
Sensitivity Reason as column Catalog Metadata alongside the human-owned flag; clearing the flag
clears that reason. Reloading still discards an unsaved review draft.
For unprotected columns, up to five source rows and five representative non-null values may be sent
transiently to the configured model provider. Protected columns are omitted from source reads and
replaced in the prompt by deterministic plausible values derived only from their metadata. Existing
descriptions are not regenerated when a flag changes.
The accepted AI-description design is recorded in
`docs/plans/2026-08-28-ai-catalog-description-generation.md`, with the formal specification in the
adjacent `-spec.md` document and Gitea issue #4. Gitea issues #5–#11 deliver the implementation.
The runtime deliberately keeps ThothAI's simple operating model: one installation-wide sequential
run owned by the backend, one short-lived Python/LiteLLM completion helper per request, and
persistence limited to the run, its safe ordered text events, and each Generated Description as
soon as it succeeds. The helper performs at most one provider retry and never falls back to another
model. Stop terminates the current helper and retains prior results; three consecutive exhausted
technical batches fail the run. Startup marks stale queued/running work interrupted, and Unlock is
available only when no local start, worker, or helper is live. Runs remain inspectable through a
live SSE log with ordered polling fallback; there is no automatic resume or user-facing generation
CLI. ADRs 0009–0010 record the runtime and source-sampling decisions.
The Installation Model Catalog accepts the protected `DEEPSEEK_API_KEY` and `ZAI_API_KEY`
references for metadata-generation providers.
It also accepts a model with no secret reference only when its OpenAI-compatible endpoint is
explicit; this covers the VPN-only AritmoLab Qwen 3.6 server without creating a fake operator
credential. The Python client supplies only its fixed non-secret compatibility placeholder.
The AritmoLab entry also sets `disableThinking: true`, mapped to the endpoint's chat-template flag,
because its default reasoning prose would violate the worker's exact JSON response contract.
Logical relationship integration with core schema-linking is complete: session creation and resume
materialize the active physical/generated/manual map, retrieval-pack generation and Pi receive the
same runtime config, and snapshot validation fails closed on a declared missing, invalid, or orphaned
endpoint. Broader publication of other Catalog metadata to schema-linking remains a separate future
slice.
**Deferred follow-up — Sensitive Data Policy in schema-linking.** The policy is first delivered
and tested in catalog description generation. Its enforcement for core schema-linking remains
out of scope until the current tickets are closed and the owner has completed the acceptance test.
At that gate, resume the design: `tht` must receive a read-only projection of the current Sensitive
Data Flags and exclude values from columns marked sensitive from every LSH result before it is
given to Pi. Do not start this integration before the owner gives final approval after that test.
## Active deployment work and manual gates
### PSD server deployment program
The approved design and executable entry point are:
- `docs/plans/2026-08-20-psd-server-deployment-program-design.md`
- `docs/plans/2026-08-20-psd-server-deployment-program.md`
- `docs/plans/2026-08-20-psd-server-survey.md`
- `docs/plans/2026-08-20-psd-server-project-a-standalone.md`
- `docs/plans/2026-08-20-psd-server-project-b-authentik.md`
Last recorded state:
- survey: `SURVEY_NO_GO`;
- Project A: `BLOCKED_BY_SURVEY_AND_MUTATION_GATE`;
- Project B: `BLOCKED_BY_PROJECT_A_AND_PRE_B_GATE`.
The deployment is a clean replacement: legacy sessions, indexes, and application configuration
are not migration inputs. The existing stack remains intact until its documented mutation and
rollback gates are explicitly approved. Shared Omics/LocalLLM networks, ETL Evidence, DWH,
`dwh-auth`, Supabase, Authentik, Superset, and Aritmolab are outside cleanup scope.
Human acceptance guides and sanitized report templates live under `docs/testing/` and
`docs/testing/evidence/`. The remediation checklist is
`docs/operations/psd-server-survey-remediation-checklist.md`.
### Authentication
The local/OIDC authentication remediation passed its automated review on 2026-08-18. Release and
PSD mutation gates remain governed by:
- `docs/architecture/authentication.md`;
- `docs/plans/2026-08-18-thothii-authentication-acceptance-and-psd-deployment.md`;
- `docs/operations/psd-dwh-auth-rollout.md`;
- `docs/testing/authentication-manual-acceptance.md`.
Do not infer authorization for server, Nginx, Authentik, database, credential, or cutover changes
from an automated PASS.
## Verification status
- The P1.1 workspace-directory registry and P2–P6 preprocessing workstreams are implemented and
have automated coverage.
- Evidence restructuring has a real PSD acceptance PASS as recorded above.
- AI Description Generation has automated coverage across installation setup, model selection,
generation/consolidation scopes, bounded sampling, cancellation/recovery, history, SSE/polling,
and the LiteLLM helper boundary.
- L2 tests requiring real providers or remote databases remain opt-in.
- Server deployment, release, and owner-operated acceptance steps remain pending wherever the
referenced runbooks require explicit approval.
Run the layer-specific checks documented in `AGENTS.md`. For release-sensitive changes, also run
the repository contract scripts in `scripts/` and build the MkDocs site.
## Operational invariants
- `tht`'s `-c`/`--config` option follows the subcommand; it is not a global option.
- `--json` commands write pristine JSON to stdout.
- Persisted phase documents and the decision ledger are the source of session truth; chat is not.
- UI chrome is English; workspace document content retains the workspace language.
- The backend refuses resume for finalized or archived sessions.
- A resume must send `/riprendi-sessione <id>`; a new session must send `/nuova-domanda`.
- DWH access is read-only.
+20 -59
View File
@@ -25,69 +25,30 @@ For the clone-based manual standalone installation test on macOS, Windows, and L
[Italian procedure](docs/install/standalone-manual-it.md) or the [Italian procedure](docs/install/standalone-manual-it.md) or the
[English procedure](docs/install/standalone-manual-en.md). [English procedure](docs/install/standalone-manual-en.md).
## Docker Compose: local startup The [public manual](https://git.tylconsulting.it/thothii-docs/) covers the product,
installation, use and administration. Developer architecture, contracts, ADRs, tests,
plans and release records remain in this repository but are excluded from MkDocs
pages and search. This is an editorial boundary, not an access restriction on the
public repository. See the [documentation cleanup review](docs/maintenance/2026-09-15-documentation-cleanup.md)
for the executed consolidation and the inventory of historical sources retained in Git.
Requirements: Docker Engine with Compose v2. The mandatory stack is `frontend`, `core`, ## Docker Compose and installation
`catalog-db`, `qdrant`, `embedding`, and the one-shot `embedding-model-init`. DWH and LLM remain external,
configurable endpoints—even when they are co-located with ThothII.
From a fresh clone, run these commands from the repository root: For a fresh installation, follow the complete manual procedure in
[Italian](docs/install/standalone-manual-it.md) or
[English](docs/install/standalone-manual-en.md). Configure protected files first;
then run the documented build, explicit migrations and startup commands with the
same installation descriptor and Compose project. There is no installer or launcher.
```sh The mandatory stack includes frontend, core, PostgreSQL catalog, Qdrant, Ollama
cp deploy/env/local.env.example deploy/env/local.env and the embedding initializer. DWH and LLM endpoints remain external dependencies.
# Copy docs/install/examples/thothii-installation.local.yaml to a protected operator path, Pi is included in the core image. Credentials and certificates belong in protected
# replace its placeholders, chmod it 600, and set that exact THT_INSTALLATION_CONFIG_SOURCE. installation-local files, never in the workspace repository.
# Edit deploy/env/local.env, including PI_AUTH_FILE, THT_SECRETS_FILE, and external endpoints.
./scripts/run-stack.sh
```
The low-level stack script builds the core, starts `catalog-db`, runs the explicit one-shot Kysely migrations, For developer topology, overlays and lifecycle details, see the internal
then runs the base+local stack in the foreground. Migrations never run implicitly in backend [Compose reference](docs/operations/compose-reference.md). Ordinary stop/down keeps
startup. The core image contains its Pi runtime; no host `pi` executable is used. For a server persistent data; removing volumes is destructive and is not an upgrade step.
installation, build the image, start the catalog, and run the same migration service before the Process health is distinct from external dependency checks performed by doctor.
application rollout:
```sh
cp deploy/env/server.env.example deploy/env/server.env
# Prepare a mode-600 thothii-installation.yaml from the server example and set its exact
# path as THT_INSTALLATION_CONFIG_SOURCE. Edit all remaining storage/secret/endpoint paths.
sudo scripts/prepare-server-pi-state.sh /srv/thothii/pi-state 10001 10001
docker compose --env-file deploy/env/server.env \
-f compose.yaml -f deploy/compose.server.yaml \
-f deploy/compose.session-server.yaml.example build core
docker compose --env-file deploy/env/server.env \
-f compose.yaml -f deploy/compose.server.yaml \
-f deploy/compose.session-server.yaml.example up -d catalog-db
docker compose --env-file deploy/env/server.env \
-f compose.yaml -f deploy/compose.server.yaml \
-f deploy/compose.session-server.yaml.example run --rm catalog-migrate
docker compose --env-file deploy/env/server.env \
-f compose.yaml -f deploy/compose.server.yaml \
-f deploy/compose.session-server.yaml.example up --build -d
```
The initializer is required for an empty or restored server Pi-state bind. It atomically creates
the three regular targets hidden below the writable parent bind; protected Pi auth and tracked
model/settings sources remain separate read-only mounts. See the server manual before substituting
a root other than `/srv/thothii/pi-state`.
Workspace descriptors come from the Git remote configured by `THT_WORKSPACE_GIT_REMOTE`; their
runtime endpoint and secret bindings remain installation-local. Open
<http://127.0.0.1:8080> (set `THOTH_HTTP_PORT` in `deploy/env/local.env` to choose another
loopback port).
Credentials and certificates are local protected files. Do not put them in environment examples,
workspace YAML, URLs, or Compose interpolation values.
Application state is split across the named `settings`, `pi-state`, `workspace-registry`,
`sessions`, `qdrant-data`, and `embedding-models` volumes. `docker compose down` keeps 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
application health endpoint intentionally checks process readiness only; external dependency
diagnostics are exposed by `tht doctor` and do not prevent the UI from starting.
## Git-backed workspace repository ## Git-backed workspace repository
+4 -1
View File
@@ -41,8 +41,11 @@ const runtimeModelSchema = z.object({
supportsReasoningEffort: z.boolean(), supportsReasoningEffort: z.boolean(),
supportsStore: z.boolean(), supportsStore: z.boolean(),
maxTokensField: z.string().optional(), maxTokensField: z.string().optional(),
thinkingFormat: z.enum(["qwen", "qwen-chat-template"]).optional(),
}).strict().optional(), }).strict().optional(),
}).strict().optional(), }).strict().refine((session) => !session.compatibility?.thinkingFormat || session.reasoning, {
message: "thinkingFormat requires reasoning: true",
}).optional(),
metadataGeneration: z.object({ disableThinking: z.boolean() }).strict().optional(), metadataGeneration: z.object({ disableThinking: z.boolean() }).strict().optional(),
}).strict(); }).strict();
+6 -3
View File
@@ -509,12 +509,15 @@ export function sessionRoutes(
// registry snapshot. The legacy fallback stays available for sessions created before // registry snapshot. The legacy fallback stays available for sessions created before
// the browser-local preference migration. // the browser-local preference migration.
let id: string; let id: string;
let sessionLanguage = language;
try { try {
({ id } = await runner.sessionNew({ const created = await runner.sessionNew({
question: b.question, name: b.name, workspaceConfigPath, question: b.question, name: b.name, workspaceConfigPath,
workspaceId, workspaceRevision, provider, model, thinking, workspaceId, workspaceRevision, provider, model, thinking,
interactionLanguage: language, interactionLanguage: language,
})); });
id = created.id;
sessionLanguage = created.interaction_language ?? language;
manifestPersisted = true; manifestPersisted = true;
if (revisionLease) { if (revisionLease) {
await revisionLease.markPersisted().catch((error: unknown) => { await revisionLease.markPersisted().catch((error: unknown) => {
@@ -527,7 +530,7 @@ export function sessionRoutes(
} catch { return storageFailure(reply); } } catch { return storageFailure(reply); }
const options = { const options = {
provider, model, thinking, provider, model, thinking,
interactionLanguage: language, interactionLanguage: sessionLanguage,
author: principal.displayName ?? principal.subject, author: principal.displayName ?? principal.subject,
principal, principal,
question: b.question, question: b.question,
+1 -1
View File
@@ -507,7 +507,7 @@ export class ThtRunner {
if (v) a.push(f, v); if (v) a.push(f, v);
} }
a.push("--json"); a.push("--json");
return this.json<{ id: string }>(a, o.workspaceConfigPath ?? o.workspace); return this.json<{ id: string; interaction_language?: string }>(a, o.workspaceConfigPath ?? o.workspace);
} }
/** Build and persist the deterministic F1 retrieval pack for a new session. */ /** Build and persist the deterministic F1 retrieval pack for a new session. */
@@ -95,6 +95,35 @@ test("returns empty catalogs when no runtime projection is configured", () => {
expect(loadMetadataGenerationModels({}).catalog()).toEqual({ models: [], default: null }); expect(loadMetadataGenerationModels({}).catalog()).toEqual({ models: [], default: null });
}); });
test.each([
["qwen-chat-template", true, true],
["qwen", true, true],
["qwen-typo", true, false],
["qwen-chat-template", false, false],
])("validates Qwen thinking format %s with reasoning=%s", (thinkingFormat, reasoning, valid) => {
const { catalogFile } = runtimeCatalog({
defaultInteraction: "local/qwen",
models: [{
id: "local/qwen", provider: "local", model: "qwen", label: "Qwen",
upstreamModel: "qwen", endpoint: { baseUrl: "http://localhost:8000/v1" },
authentication: { mode: "none" }, sessionAdapter: { mode: "openai_compatible" },
session: {
reasoning, contextWindow: 32768, maxTokens: 8192,
compatibility: {
supportsDeveloperRole: false, supportsReasoningEffort: false,
supportsStore: false, maxTokensField: "max_tokens", thinkingFormat,
},
},
}],
});
if (valid) {
expect(loadRuntimeModelCatalog(catalogFile).sessionModels()[0].session?.compatibility)
.toMatchObject({ thinkingFormat });
} else {
expect(() => loadRuntimeModelCatalog(catalogFile)).toThrow("runtime model catalog is invalid");
}
});
test("rejects a drifted default and an unprotected projection", () => { test("rejects a drifted default and an unprotected projection", () => {
const drifted = runtimeCatalog({ defaultInteraction: "zai/missing" }); const drifted = runtimeCatalog({ defaultInteraction: "zai/missing" });
expect(() => loadRuntimeModelCatalog(drifted.catalogFile)).toThrow("interaction default is invalid"); expect(() => loadRuntimeModelCatalog(drifted.catalogFile)).toThrow("interaction default is invalid");
+27
View File
@@ -140,6 +140,33 @@ test("resume rejects browser interaction language overrides", async () => {
} finally { await app.close(); } } finally { await app.close(); }
}); });
test("new session starts Pi with the question language persisted by the harness", async () => {
let runtime: any;
const app = buildApp(loadConfig({ THT_HARNESS_DIR: "../harness" }), {
thtRunner: {
sessionNew: async () => ({ id: "english-question", interaction_language: "en" }),
searchPack: async () => {},
},
readiness: { ensure: async () => ({ ok: true }) },
mgr: {
get: () => undefined,
createFor: (_id: string, options: any) => {
runtime = options;
return { bridge: { onClientEvent: () => {} } };
},
configure: async () => {}, start: () => {},
},
getSettings: () => ({ workspace: "default" }),
});
try {
const response = await app.inject({ method: "POST", url: "/sessions", payload: {
question: "How many patients were admitted last year?", interactionLanguage: "it",
} });
expect(response.statusCode).toBe(200);
expect(runtime.interactionLanguage).toBe("en");
} finally { await app.close(); }
});
test("resume pins legacy interaction language using the resolved harness workspace", async () => { test("resume pins legacy interaction language using the resolved harness workspace", async () => {
let pinnedWorkspace: string | undefined; let pinnedWorkspace: string | undefined;
let runtime: any; let runtime: any;
+8 -3
View File
@@ -96,10 +96,15 @@ deve invece contenere `mode` e `defaultLocale`, altrimenti viene rifiutato.
## Lingua, continuità e dati ## Lingua, continuità e dati
La lingua UI traduce il testo dell'applicazione, non i contenuti di dominio. La lingua UI traduce il testo dell'applicazione, non i contenuti di dominio.
Alla creazione, la lingua UI viene acquisita come `interactionLanguage`; il Alla creazione, la lingua UI viene acquisita come `interactionLanguage` di ripiego.
manifest salva `interaction_language`, che governa domande e scelte del modello. Il CLI riconosce la lingua della domanda originale e salva `interaction_language`
nel manifest; usa il ripiego solo per input troppo brevi, ambigui o composti da codice.
Questa lingua governa domande, spiegazioni, scelte e controlli HITL. Il gate la
include nei descrittori e il frontend la applica al sottoalbero dei widget,
senza cambiare la lingua della navigazione.
Alla ripresa vale la lingua salvata, non l'ultima scelta dell'header. Per i manifest Alla ripresa vale la lingua salvata, non l'ultima scelta dell'header. Per i manifest
precedenti senza campo viene fissata la lingua workspace disponibile alla prima ripresa. precedenti senza campo viene riconosciuta e fissata la lingua della domanda,
con la lingua workspace disponibile alla prima ripresa come ripiego.
Il cambio lingua Omics invia il form Django e ricarica la pagina. ThothII conserva Il cambio lingua Omics invia il form Django e ricarica la pagina. ThothII conserva
solo l'ID della selezione in `sessionStorage`, separato per pathname, issuer e solo l'ID della selezione in `sessionStorage`, separato per pathname, issuer e
+19 -12
View File
@@ -81,20 +81,20 @@ The model proposes; a human reviewer decides at gates through widgets:
The frontend renders these widget descriptors (registry in `src/widgets/`). It rebuilds the live transcript in memory from the SSE stream (`src/store/sessionStore.ts`); it is **not persisted**. The frontend renders these widget descriptors (registry in `src/widgets/`). It rebuilds the live transcript in memory from the SSE stream (`src/store/sessionStore.ts`); it is **not persisted**.
## Curated and immutable Evidence ## Evidence sources, local authority and search projections
The workspace repository is the publication boundary. The curator prepares `evidence/source/`, The workspace repository publishes revision-pinned workspace identities and Evidence sources.
reviews units in `evidence/curated/`, validates them, and merges them. With `evidence.schema_version: 2`, An initialized editable Evidence archive is maintained locally through external editors and
the runtime materializes the full `evidence/` tree from the exact Git commit, but the renderer passes explicit consolidation; normal preprocessing or source refresh must not overwrite its manual
only `curated/**/*.md` from the immutable revision root to preprocessing. Sources, manifests, and corrections. File save, active search generation and a later human Git commit/push are separate
evaluation data remain available for traceability. The runtime never modifies, stages, commits, or outcomes. See [the current Evidence contract](../contracts/curated-evidence-v4.md) and
publishes the authoring repository. [source/publication boundaries](../contracts/workspace-evidence-v3.md).
Before indexing, the curated corpus from the pinned revision is validated. Each workspace has two Each workspace has separate Qdrant collections. `reference` contains Schema, relationships and
physical Qdrant collections with different lifecycles. `reference` contains Schema, relationships, Evidence and may be replaced or cleared by preprocessing. Memory uses its own dense/BM25
and Evidence and may be replaced or cleared by preprocessing; `memory` contains `memory` and projection, rebuilt from authoritative PostgreSQL cards rather than old vector payloads or
`solved_question` records and is never preprocessing output. Only the `reference` collection has the session artifacts. Both collections can contain sparse vectors; their ownership and cleanup
sparse `bm25` vector with `idf`, and only the Evidence stage writes sparse values. lifecycles remain separate. See [Memory](../gestione-memory.md).
The Administration control can clear the replaceable reference collection, LSH, corpus, and derived The Administration control can clear the replaceable reference collection, LSH, corpus, and derived
checkpoints. The operation preserves the memory collection and makes preprocessing required before checkpoints. The operation preserves the memory collection and makes preprocessing required before
@@ -115,6 +115,13 @@ the core can admit a new session.
## Runtime composition ## Runtime composition
PostgreSQL is the current database-metadata authority for all core consumers, not a deferred
catalog-to-core integration. The historical research on a separate catalog service and
`annotations.yaml` publication is superseded by ADRs 0004 and 0016. Preserve read-only DWH access,
installation-local bindings/secrets, revision-pinned workspace identity, and fail-closed readiness
when changing those boundaries. The exact snapshot interface is the
[Catalog Schema Snapshot contract](../contracts/catalog-schema-snapshot.md).
The local stack is started by `./scripts/run-stack.sh` after the installation descriptor and The local stack is started by `./scripts/run-stack.sh` after the installation descriptor and
`deploy/env/local.env` exist. `core` includes Pi. `catalog-db`, Qdrant, and the Ollama embedding `deploy/env/local.env` exist. `core` includes Pi. `catalog-db`, Qdrant, and the Ollama embedding
service are internal Compose services; only the DWH and model-provider endpoint remain external. service are internal Compose services; only the DWH and model-provider endpoint remain external.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

@@ -1,32 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Archify automated browser evidence · thothii-core-sequence.html</title>
<style>
*{box-sizing:border-box}body{margin:0;padding:24px;background:#e9eef5;color:#172033;font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}header{max-width:1500px;margin:0 auto 18px}h1{margin:0 0 6px;font-size:20px}p{margin:0;color:#526176}.grid{max-width:1500px;margin:auto;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}figure{margin:0;padding:10px;background:white;border:1px solid #c9d4e3;border-radius:12px;box-shadow:0 10px 30px rgba(15,23,42,.08)}img{display:block;width:100%;height:auto;border:1px solid #e2e8f0}figcaption{padding:9px 4px 2px;color:#526176}@media(max-width:900px){.grid{grid-template-columns:1fr}}
</style>
</head>
<body>
<header><h1>Automated browser evidence</h1><p>thothii-core-sequence.html · visual-check containment pass · perceptual visual review pending</p></header>
<main class="grid">
<figure>
<img src="thothii-core-sequence.visual-check.1440x900.light.png" alt="light 1440 by 900">
<figcaption><strong>LIGHT</strong> · 1440×900 · containment pass</figcaption>
</figure>
<figure>
<img src="thothii-core-sequence.visual-check.1440x900.dark.png" alt="dark 1440 by 900">
<figcaption><strong>DARK</strong> · 1440×900 · containment pass</figcaption>
</figure>
<figure>
<img src="thothii-core-sequence.visual-check.2048x1320.light.png" alt="light 2048 by 1320">
<figcaption><strong>LIGHT</strong> · 2048×1320 · containment pass</figcaption>
</figure>
<figure>
<img src="thothii-core-sequence.visual-check.2048x1320.dark.png" alt="dark 2048 by 1320">
<figcaption><strong>DARK</strong> · 2048×1320 · containment pass</figcaption>
</figure>
</main>
</body>
</html>
@@ -1,548 +0,0 @@
{
"schemaVersion": 1,
"ok": true,
"command": "visual-check",
"evidenceKind": "automated-browser",
"status": "pass",
"visualReview": "pending",
"artifact": {
"path": "/Users/mp/projects/ThothII/docs/architecture/thothii-core-sequence.html",
"sha256": "b521eb942f7889cfc3a5e29010546ba59eeab1cf485c22c533d271ccef9a28d5",
"bytes": 716881
},
"state": {
"detail": "read",
"motion": "still"
},
"chrome": {
"status": "available",
"executable": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
},
"diagnostics": [],
"containment": {
"status": "pass",
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1194,
"diagramWidth": 1164,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1242,
"diagramWidth": 1212,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1402,
"diagramWidth": 1372,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1818,
"diagramWidth": 1768,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"readability": {
"status": "pass",
"minimumProjectedNodeTextPx": 6,
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1194,
"diagramWidth": 1164,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1242,
"diagramWidth": 1212,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1402,
"diagramWidth": 1372,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1818,
"diagramWidth": 1768,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"viewerChrome": {
"status": "pass",
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1194,
"diagramWidth": 1164,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1242,
"diagramWidth": 1212,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1402,
"diagramWidth": 1372,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1818,
"diagramWidth": 1768,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"captures": {
"status": "pass",
"screenshots": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1194,
"diagramWidth": 1164,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light",
"file": "thothii-core-sequence.visual-check.1440x900.light.png"
},
{
"width": 1440,
"height": 900,
"theme": "dark",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1194,
"diagramWidth": 1164,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "dark",
"file": "thothii-core-sequence.visual-check.1440x900.dark.png"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1818,
"diagramWidth": 1768,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light",
"file": "thothii-core-sequence.visual-check.2048x1320.light.png"
},
{
"width": 2048,
"height": 1320,
"theme": "dark",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1818,
"diagramWidth": 1768,
"viewBoxWidth": 1080,
"minimumProjectedNodeTextPx": 7,
"minimumProjectedNodeText": "browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "dark",
"file": "thothii-core-sequence.visual-check.2048x1320.dark.png"
}
],
"contactSheet": "thothii-core-sequence.visual-check.html"
},
"sidecars": {
"receipt": "thothii-core-sequence.visual-check.json",
"contactSheet": "thothii-core-sequence.visual-check.html"
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

@@ -1,32 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Archify automated browser evidence · thothii-runtime.html</title>
<style>
*{box-sizing:border-box}body{margin:0;padding:24px;background:#e9eef5;color:#172033;font:14px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}header{max-width:1500px;margin:0 auto 18px}h1{margin:0 0 6px;font-size:20px}p{margin:0;color:#526176}.grid{max-width:1500px;margin:auto;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px}figure{margin:0;padding:10px;background:white;border:1px solid #c9d4e3;border-radius:12px;box-shadow:0 10px 30px rgba(15,23,42,.08)}img{display:block;width:100%;height:auto;border:1px solid #e2e8f0}figcaption{padding:9px 4px 2px;color:#526176}@media(max-width:900px){.grid{grid-template-columns:1fr}}
</style>
</head>
<body>
<header><h1>Automated browser evidence</h1><p>thothii-runtime.html · visual-check containment pass · perceptual visual review pending</p></header>
<main class="grid">
<figure>
<img src="thothii-runtime.visual-check.1440x900.light.png" alt="light 1440 by 900">
<figcaption><strong>LIGHT</strong> · 1440×900 · containment pass</figcaption>
</figure>
<figure>
<img src="thothii-runtime.visual-check.1440x900.dark.png" alt="dark 1440 by 900">
<figcaption><strong>DARK</strong> · 1440×900 · containment pass</figcaption>
</figure>
<figure>
<img src="thothii-runtime.visual-check.2048x1320.light.png" alt="light 2048 by 1320">
<figcaption><strong>LIGHT</strong> · 2048×1320 · containment pass</figcaption>
</figure>
<figure>
<img src="thothii-runtime.visual-check.2048x1320.dark.png" alt="dark 2048 by 1320">
<figcaption><strong>DARK</strong> · 2048×1320 · containment pass</figcaption>
</figure>
</main>
</body>
</html>
@@ -1,548 +0,0 @@
{
"schemaVersion": 1,
"ok": true,
"command": "visual-check",
"evidenceKind": "automated-browser",
"status": "pass",
"visualReview": "pending",
"artifact": {
"path": "/Users/mp/projects/ThothII/docs/architecture/thothii-runtime.html",
"sha256": "ad19195852c7c643228663e5bf7f3cb273afb0456fedbe295d4df24bc345b6c7",
"bytes": 724277
},
"state": {
"detail": "read",
"motion": "still"
},
"chrome": {
"status": "available",
"executable": "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
},
"diagnostics": [],
"containment": {
"status": "pass",
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 968,
"diagramWidth": 938,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.20735294117647,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1009,
"diagramWidth": 979,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.478676470588235,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1172,
"diagramWidth": 1142,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 7.557352941176471,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1583,
"diagramWidth": 1533,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 9,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"readability": {
"status": "pass",
"minimumProjectedNodeTextPx": 6,
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 968,
"diagramWidth": 938,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.20735294117647,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1009,
"diagramWidth": 979,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.478676470588235,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1172,
"diagramWidth": 1142,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 7.557352941176471,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1583,
"diagramWidth": 1533,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 9,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"viewerChrome": {
"status": "pass",
"viewports": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 968,
"diagramWidth": 938,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.20735294117647,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1600,
"height": 1000,
"theme": "light",
"innerWidth": 1600,
"innerHeight": 1000,
"scrollWidth": 1600,
"scrollHeight": 1000,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1009,
"diagramWidth": 979,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.478676470588235,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 1920,
"height": 1080,
"theme": "light",
"innerWidth": 1920,
"innerHeight": 1080,
"scrollWidth": 1920,
"scrollHeight": 1080,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1172,
"diagramWidth": 1142,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 7.557352941176471,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1583,
"diagramWidth": 1533,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 9,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light"
}
]
},
"captures": {
"status": "pass",
"screenshots": [
{
"width": 1440,
"height": 900,
"theme": "light",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 968,
"diagramWidth": 938,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.20735294117647,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light",
"file": "thothii-runtime.visual-check.1440x900.light.png"
},
{
"width": 1440,
"height": 900,
"theme": "dark",
"innerWidth": 1440,
"innerHeight": 900,
"scrollWidth": 1440,
"scrollHeight": 900,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 968,
"diagramWidth": 938,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 6.20735294117647,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 51,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "dark",
"file": "thothii-runtime.visual-check.1440x900.dark.png"
},
{
"width": 2048,
"height": 1320,
"theme": "light",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1583,
"diagramWidth": 1533,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 9,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "light",
"file": "thothii-runtime.visual-check.2048x1320.light.png"
},
{
"width": 2048,
"height": 1320,
"theme": "dark",
"innerWidth": 2048,
"innerHeight": 1320,
"scrollWidth": 2048,
"scrollHeight": 1320,
"overflowX": false,
"overflowY": false,
"ok": true,
"readerWidth": 1583,
"diagramWidth": 1533,
"viewBoxWidth": 1360,
"minimumProjectedNodeTextPx": 9,
"minimumProjectedNodeText": "Browser",
"minimumProjectedNodeTextDetail": "context",
"minimumRequiredNodeTextPx": 6,
"readabilityOk": true,
"hasLegend": true,
"hasNavigationDock": true,
"legendDockIntersectionArea": 0,
"dockStageIntersectionArea": 0,
"dockStageGap": 10.21875,
"requiredDockStageGap": 10,
"viewerChromeStageOk": true,
"viewerChromeReserve": 41,
"viewerChromeActive": true,
"viewerChromeOk": true,
"resolvedTheme": "dark",
"file": "thothii-runtime.visual-check.2048x1320.dark.png"
}
],
"contactSheet": "thothii-runtime.visual-check.html"
},
"sidecars": {
"receipt": "thothii-runtime.visual-check.json",
"contactSheet": "thothii-runtime.visual-check.html"
}
}
+2 -2
View File
@@ -244,5 +244,5 @@ The first installed consolidation performs this conversion automatically when th
legacy manifest is present, then validates and activates the result. Preserve the legacy manifest is present, then validates and activates the result. Preserve the
existing checkout in backups before upgrading. The E1 validation used an isolated existing checkout in backups before upgrading. The E1 validation used an isolated
copy; E2 also converted and indexed all 35 units on the running local preview. copy; E2 also converted and indexed all 35 units on the running local preview.
See the [E1 validation report](../plans/2026-09-09-evidence-e1-validation.md) and See the [E1 validation report](../reports/knowledge-archives-release.md) and
[E2 validation report](../plans/2026-09-09-evidence-e2-validation.md). [E2 validation report](../reports/knowledge-archives-release.md).
+6 -6
View File
@@ -4,7 +4,7 @@ This page describes the complete workspace Evidence lifecycle: where original ma
## Editable local Evidence ## Editable local Evidence
E1 adds [Curated Evidence v4 and a persistent local archive](contracts/curated-evidence-v4.md). E1 adds [Curated Evidence v4 and a persistent local archive](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/curated-evidence-v4.md).
The visible title and payload fields are authoritative Markdown. New manual units need no The visible title and payload fields are authoritative Markdown. New manual units need no
external source; consolidation records their curator and distinguishes later corrections external source; consolidation records their curator and distinguishes later corrections
from original documentary provenance. The core archive API creates immutable candidates from original documentary provenance. The core archive API creates immutable candidates
@@ -14,12 +14,12 @@ E2 adds **Administration → Evidence management**, actual host file paths, comp
browsing and filtering, and the installed `tht workspace evidence consolidate browsing and filtering, and the installed `tht workspace evidence consolidate
--workspace <id>` command. Edit files externally, consolidate to activate them, then --workspace <id>` command. Edit files externally, consolidate to activate them, then
review and run Git manually. Runtime consumes only the active local snapshot. review and run Git manually. Runtime consumes only the active local snapshot.
The [v4 contract](contracts/curated-evidence-v4.md#administration-and-installed-command) The [v4 contract](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/curated-evidence-v4.md#administration-and-installed-command)
describes host mounting, first conversion, failure recovery and Clear behavior. describes host mounting, first conversion, failure recovery and Clear behavior.
The local PSD preview has 35 converted and indexed units. E3 adds explicit import/refresh The local PSD preview has 35 converted and indexed units. E3 adds explicit import/refresh
from local drafts and configured HTTP/S3 sources, durable current/proposed comparisons, from local drafts and configured HTTP/S3 sources, durable current/proposed comparisons,
and keep/replace decisions with activation and retry. See and keep/replace decisions with activation and retry. See
[Import drafts and refresh sources](contracts/curated-evidence-v4.md#import-drafts-and-refresh-sources). [Import drafts and refresh sources](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/curated-evidence-v4.md#import-drafts-and-refresh-sources).
Workflow gate corrections remain the subsequent shared increment, X1. Workflow gate corrections remain the subsequent shared increment, X1.
## Existing repository publication path ## Existing repository publication path
@@ -92,7 +92,7 @@ Join orders using the order number, financial year and company.
Use `tht evidence migrate <workspace-root>` for deterministic legacy conversion. The Use `tht evidence migrate <workspace-root>` for deterministic legacy conversion. The
conversion preserves typed content and initializes an archive baseline; it does not conversion preserves typed content and initializes an archive baseline; it does not
activate the local corpus. See the [v4 contract](contracts/curated-evidence-v4.md) for activate the local corpus. See the [v4 contract](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/curated-evidence-v4.md) for
all eight kinds, provenance, file layout and the E1/E2 boundary. all eight kinds, provenance, file layout and the E1/E2 boundary.
## Legacy v3 representation ## Legacy v3 representation
@@ -262,5 +262,5 @@ Formulas use a format distinct from document Evidence. A formula proposed during
## Contract references ## Contract references
- [Workspace Evidence v3 contract](contracts/workspace-evidence-v3.md) - [Workspace Evidence v3 contract](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/workspace-evidence-v3.md)
- [Preprocessing CLI contract](contracts/workspace-preprocessing-cli.md) - [Preprocessing CLI contract](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/workspace-preprocessing-cli.md)
+45
View File
@@ -128,6 +128,51 @@ endpoint expects a model name different from the catalog key.
Provider integrations remain declarative. Do not register providers from Provider integrations remain declarative. Do not register providers from
`harness/.pi/extensions/`; those extensions implement the workflow and human gates only. `harness/.pi/extensions/`; those extensions implement the workflow and human gates only.
### Qwen 3.6 sessions and thinking controls
For Qwen served through a vLLM-compatible chat template, declare the following inside the
model's `session` block, alongside its context and output limits:
```yaml
reasoning: true
compatibility:
supportsDeveloperRole: false
supportsReasoningEffort: false
supportsStore: false
maxTokensField: max_tokens
thinkingFormat: qwen-chat-template
```
This makes Pi send `chat_template_kwargs.enable_thinking` from the selected thinking level,
with `preserve_thinking: true`. Choose **off** to explicitly disable thinking. The alternative
`thinkingFormat: qwen` is for endpoints expecting top-level `enable_thinking`. Both formats
require `reasoning: true`; declaring `reasoning: false` does not tell the server to disable
thinking. Omit `thinkingFormat` to preserve Pi's default behavior for other providers.
Regenerate projections with the updated host CLI and recreate the local core container after
rebuilding it. Do not add these fields directly to generated Pi files. These controls do not
force tool calls or certify the workflow; perform the operator verification above.
```sh
tht --installation /absolute/path/thothii-installation.yaml installation generate
```
Use the model identifier exposed by your endpoint, such as `qwen3.6-35b-a3b`, and limits
supported by that deployment. The thinking format configures the Pi session adapter;
metadata generation continues to use its separate LiteLLM settings.
If a session displays text such as `{"type":"bash","command":"tht session show … --json"}`
and never opens a review widget, that text is not an executed tool call. A verified cause
was the Evidence JSON extension being loaded into interactive sessions and forcing
`response_format: {type: "json_object"}`. Upgrade to the core image containing the fix:
the extension belongs in `.pi/evidence-extensions/` and is loaded explicitly only by
Evidence authoring. It must not also remain in the automatically loaded `.pi/extensions/`
directory. Regenerating model configuration alone does not remove an extension from an old image.
After upgrading, reload the browser and resume the session. Verify that Pi executes
`tht session show` and opens a review widget. This fix does not require changing the Qwen
server, forcing every turn to call a tool, or teaching the model to print tool-call JSON.
## Authentication ## Authentication
Every provider chooses one explicit mode: Every provider chooses one explicit mode:
+1 -1
View File
@@ -226,5 +226,5 @@ error. Preparation is repeatable and checks migration checksums.
See [the M1 specification](plans/2026-09-08-memory-m1-spec.md) and See [the M1 specification](plans/2026-09-08-memory-m1-spec.md) and
[ADR 0018](adr/0018-use-postgres-for-memory-and-qdrant-for-retrieval.md) for the [ADR 0018](adr/0018-use-postgres-for-memory-and-qdrant-for-retrieval.md) for the
approved scope and acceptance boundaries. approved scope and acceptance boundaries.
See [M2 implementation and validation](plans/2026-09-09-memory-m2-validation.md) See [M2 implementation and validation](reports/knowledge-archives-release.md)
for the retrieval checks and real embedding test command. for the retrieval checks and real embedding test command.
+1 -1
View File
@@ -90,4 +90,4 @@ happen from the workflow’s point of view.
session does not become Evidence automatically: a curator must review and publish it in Git. session does not become Evidence automatically: a curator must review and publish it in Git.
- For login and access recovery, use [local authentication](install/authentication-local.md) or - For login and access recovery, use [local authentication](install/authentication-local.md) or
[OIDC authentication](install/authentication-oidc.md) for full, or contact the [OIDC authentication](install/authentication-oidc.md) for full, or contact the
portal administrator for [embedded/upstream access](install/authentication-upstream.md). portal administrator for [embedded/upstream access](install/shell-and-language.md).
+24 -26
View File
@@ -1,35 +1,33 @@
# ThothII documentation # ThothII documentation
ThothII is a human-reviewed datamart builder. It turns a question into validated SQL through an ThothII turns a natural-language question into reviewed SQL. The model proposes;
eight-phase workflow: the model proposes; a reviewer makes the decisions that are persisted. a human reviewer decides which interpretations, sources and results to accept.
Start with the path that matches the work you need to do: This is the public product manual. Start with the task you need to perform:
| I need to… | Start here | | I need to… | Start here |
| --- | --- | | --- | --- |
| Install or operate one instance | [Install and first start](install/first-start.md) | | Understand the product and its boundaries | [What ThothII does](product-overview.md) |
| Clone and manually install on macOS, Windows, or Linux | [Italian procedure](install/standalone-manual-it.md) · [English procedure](install/standalone-manual-en.md) | | Install on Mac, Windows through WSL2, or Linux | [Italian procedure](install/standalone-manual-it.md) · [English procedure](install/standalone-manual-en.md) |
| Upgrade the server and integrate Omics Portal | [Codex server handoff](operations/server-codex-handoff.md) | | Choose display mode and language | [Display mode and language](install/shell-and-language.md) |
| Choose full/embedded and configure the shell | [Shell and localization](operations/shell-and-localization.md) | | Configure login | [Local accounts](install/authentication-local.md) · [OIDC](install/authentication-oidc.md) |
| Reuse an authenticated server portal without a second login | [Upstream authentication](install/authentication-upstream.md) | | Configure model providers | [Model configuration](general/pi-configuration.md) |
| Understand how the two renderings share the same application | [Rendering architecture](architecture/application-shell.md) | | Prepare a domain workspace | [Workspaces](operations/workspaces.md) |
| Validate login, logout and portal integration before release | [Acceptance matrix](testing/authentication-manual-acceptance.md) | | Configure databases and reviewed descriptions | [Database management](operations/database-management.md) |
| Add, update, or prepare a workspace | [Workspace operations](operations/workspaces.md) | | Ask a question and review SQL | [User guide](guida-utente.md) · [Workflow](skills.md) |
| Ask a question and review the SQL workflow | [User guide](guida-utente.md) | | Maintain domain knowledge | [Evidence](evidence.md) · [Memory](usage/memory.md) |
| Configure and refresh an authoritative database catalog | [Database management](operations/database-management.md) |
| Author and publish domain Evidence | [Evidence](evidence.md) |
| Understand boundaries and persistence | [Architecture overview](architecture/overview.md) |
## How the documentation is organised ## Scope of this manual
- **Install and operate** documents host-side setup, authentication, lifecycle, workspaces, and The manual covers the product, installation, use and administration. Architecture,
Pi administration. code contracts, design decisions, implementation plans, test reports and site-specific
- **Use ThothII** documents the two application paths: reviewed NL→SQL sessions and administrative deployment handoffs are developer/project material maintained in the repository;
database management. they are not pages of this site and are not included in its search index.
- **Architecture and contracts** explain why the system behaves as it does and define the
machine-facing boundaries. Consult them when integrating or changing an implementation; they
are not a substitute for an operator runbook.
The host-side `tht` command is the operator interface. The Python `tht` inside `harness/` is a The installation procedures distinguish checked documentation from platform and
different, internal workflow CLI invoked by the core. In examples, use an absolute installation functional tests that still require execution. A clone does not transfer another
descriptor path whenever discovery is not unambiguous. installation's credentials, data or network access.
The host-side `tht` command operates an installation. The Python workflow CLI inside
the runtime is a separate internal interface; do not substitute its commands for
the host installation procedure.
+1 -1
View File
@@ -3,7 +3,7 @@
Use local mode for a standalone PC or Mac, with `shell.mode: full` and Use local mode for a standalone PC or Mac, with `shell.mode: full` and
`shell.defaultLocale: en` in the installation descriptor. Presentation and `shell.defaultLocale: en` in the installation descriptor. Presentation and
authentication are independent: selecting full does not create accounts. Omics authentication are independent: selecting full does not create accounts. Omics
embedded instead uses the [upstream guide](authentication-upstream.md), not local users. embedded instead uses the [upstream guide](shell-and-language.md), not local users.
Configure local authentication through `tht`; passwords are entered at an Configure local authentication through `tht`; passwords are entered at an
echo-free prompt or read from a protected `--password-file`, never from a command argument. echo-free prompt or read from a protected `--password-file`, never from a command argument.
+3 -3
View File
@@ -2,7 +2,7 @@
Use this guide for **ThothII's own login**, normally `shell.mode: full` on an Use this guide for **ThothII's own login**, normally `shell.mode: full` on an
autonomous server. It is not the integration procedure for an already logged-in autonomous server. It is not the integration procedure for an already logged-in
Omics user. That deployment uses [embedded/upstream](authentication-upstream.md), Omics user. That deployment uses [embedded/upstream](shell-and-language.md),
even when Omics's identity provider is Authentik. even when Omics's identity provider is Authentik.
OIDC mode supports a standards-based provider. The browser flow is generic: Authorization Code, OIDC mode supports a standards-based provider. The browser flow is generic: Authorization Code,
@@ -104,7 +104,7 @@ Any authentication failure prevents activation according to the static or live s
relevant diagnostic surface. relevant diagnostic surface.
The complete closed diagnostic-code union and exact role-to-permission expansion are in the The complete closed diagnostic-code union and exact role-to-permission expansion are in the
[authentication architecture](../architecture/authentication.md). [authentication architecture](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/architecture/authentication.md).
## Browser login and logout ## Browser login and logout
@@ -114,4 +114,4 @@ password prompt, but this remains a distinct ThothII login/session, unlike Omics
upstream. Full's name menu logs out of ThothII only. It does not revoke the upstream. Full's name menu logs out of ThothII only. It does not revoke the
provider session or log out other applications, so a subsequent login can return provider session or log out other applications, so a subsequent login can return
immediately through SSO. No provider token is placed in the UI adapter or browser immediately through SSO. No provider token is placed in the UI adapter or browser
storage. See the [manual acceptance matrix](../testing/authentication-manual-acceptance.md). storage. See the [manual acceptance matrix](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/testing/authentication-manual-acceptance.md).
+1 -1
View File
@@ -8,7 +8,7 @@ For **embedded in Omics**, retain Omics's existing Authentik authentication and
configure ThothII as upstream. Omics verifies `datamart_builder.access` and configure ThothII as upstream. Omics verifies `datamart_builder.access` and
administrator status and the proxy supplies the identity; no additional ThothII administrator status and the proxy supplies the identity; no additional ThothII
OIDC client, login or local user is required for that path. Follow the OIDC client, login or local user is required for that path. Follow the
[portal integration guide](authentication-upstream.md). [portal integration guide](shell-and-language.md).
```mermaid ```mermaid
sequenceDiagram sequenceDiagram
+47 -81
View File
@@ -1,98 +1,64 @@
# Install and first start # Install and first start
For the ordered clone-to-start procedure on Mac, Windows WSL2 and Linux, use the Use one complete procedure for a fresh installation:
[Italian manual guide](standalone-manual-it.md) or [English manual guide](standalone-manual-en.md),
including protected credentials and the explicit initial catalog migration.
This is the supported local installation path. It creates an installation-local configuration and - [Italian manual installation](standalone-manual-it.md)
starts the Compose stack; it does not create a workspace repository or a database catalog entry. - [English manual installation](standalone-manual-en.md)
## Prerequisites and boundaries Both cover macOS, Windows through Ubuntu WSL2, and Linux. They use a Gitea clone,
protected local configuration and manual terminal commands, without an application
installer or launcher. See their verification matrix for tests still pending.
Install Docker Engine with Compose v2, plus the host `tht` command. On macOS or Linux, install the ## What must be ready
host command from the repository with `./scripts/install-tht.sh`; Windows uses
`./scripts/install-tht.ps1`. The installer builds or verifies the native command and checks that
`tht` is resolvable on `PATH`.
The stack contains `frontend`, `core`, `catalog-db`, `qdrant`, `embedding`, and the one-shot You need Docker with Compose, the host operator command `tht`, access to the workspace
`embedding-model-init` and `catalog-migrate` services. DWH and model-provider endpoints are repository, and the credentials and network routes for the configured DWH and model
external installation settings. Pi runs inside `core`; do not install a host Pi executable for providers. Pi runs inside the application runtime; no host Pi installation is needed.
the application runtime.
Secrets, certificates, Pi authentication, and workspace endpoint bindings are protected The stack includes `frontend`, `core`, `catalog-db`, `qdrant`, `embedding`, plus the
installation-local files. Never put them in a workspace descriptor, an env file intended for one-shot `embedding-model-init` and `catalog-migrate` services. DWH and generative-model
version control, a URL, or a command line. endpoints remain separate installation settings.
## Create the local installation Secrets, certificates, Pi authentication and endpoint bindings are protected local files.
Do not commit them or copy the configuration of another machine unchanged.
From the repository root, start the interactive setup and select the local profile: ## Follow the ordered procedure
The bilingual guides provide the exact commands for:
1. Cloning the selected revision and checking prerequisites.
2. Bootstrapping the native host command.
3. Preparing catalog passwords and using
`tht setup --profile local --shell-mode full --shell-default-locale en --configure-only`.
4. Completing model, authentication and workspace credentials.
5. Generating configuration, building images and explicitly running `catalog-migrate`.
6. Starting the installation and checking health and readiness.
Do not run setup alone as a substitute for that sequence. Migrations are not an
implicit effect of backend startup or `tht start`. Do not mix this installation's
descriptor/project with a different low-level Compose environment.
For an already configured installation:
```sh ```sh
tht setup --profile local --shell-mode full --shell-default-locale en
```
It writes the selected non-secret descriptor below `deploy/<installation-id>/`, the associated
operator env file, and can create protected secret templates. Keep the descriptor path: pass it
to commands as `--installation /absolute/path/thothii-installation.yaml` when more than one
installation can be discovered.
The explicit shell options are important: compatibility defaults without them
are embedded/en/omics-portal, which expects an Omics document. The Mac's standalone
installation must use full, with English as its initial locale. Existing browser
language preferences take precedence over that initial value. Full does not
configure authentication; setup separately bootstraps local login.
For a server portal, use the [embedded/upstream procedure](authentication-upstream.md)
instead of creating a second ThothII login. For a standalone server, use full
with [direct OIDC](authentication-oidc.md). Shell mode does not follow `profile`
automatically. See [configuration and regeneration](../operations/shell-and-localization.md)
before modifying an existing installation.
If the descriptor is prepared manually instead, begin with
[`thothii-installation.local.yaml`](examples/thothii-installation.local.yaml), set mode `0600` or
`0400`, and ensure `THT_INSTALLATION_CONFIG_SOURCE` in the selected env file points to that exact
file. Create the secret bundle from `deploy/secrets/thothii.secrets.example`, protect it, and set
the file locations and external endpoints in the env file. The required settings include:
- `PI_AUTH_FILE`, `THT_SECRETS_FILE`, and the catalog password source files;
- `THT_INSTALLATION_CONFIG_SOURCE` and the workspace Git remote/branch;
- the DWH and model-provider endpoints; and
- `THT_AUTH_CONFIG_ROOT` for local authentication or the OIDC configuration selected during setup.
For the supported secret names and the metadata-generation model credential boundary, see the
`deploy/secrets/README.md` file in the installation checkout. It is intentionally not published
as a documentation page because it describes a protected local-file contract.
## Start and verify
For the normal local path, use the launcher:
```sh
./scripts/run-stack.sh
```
It builds `core`, starts `catalog-db`, runs `catalog-migrate`, then keeps the base plus local
Compose profile in the foreground. Database migrations are deliberately not a hidden backend
startup action. Open `http://127.0.0.1:8080` unless `THOTH_HTTP_PORT` was changed.
In another terminal, verify the installation without changing it:
```sh
tht --installation /absolute/path/thothii-installation.yaml doctor --json
tht --installation /absolute/path/thothii-installation.yaml status tht --installation /absolute/path/thothii-installation.yaml status
tht --installation /absolute/path/thothii-installation.yaml doctor --json
``` ```
`/health` verifies application-process readiness; `tht doctor` is the diagnostic surface for `/health` checks application-process readiness. Doctor also checks configuration,
Compose, configuration, workspace, workflow, and Pi prerequisites. workspace, workflow and Pi prerequisites; a healthy web page alone does not prove
that a real database question can complete.
## Routine lifecycle and next steps ## After startup
Use `tht start [--build]`, `tht stop`, `tht logs`, and `tht doctor` rather than composing ad-hoc Prepare [workspaces](../operations/workspaces.md), configure a database in
container commands. Named volumes retain settings, Pi state, workspace registry, sessions, [Database Management](../operations/database-management.md), and complete the functional
Qdrant data, and embedding models across `docker compose down`; removing them requires the checks in the installation guide before using real data.
explicit destructive `--volumes` form.
After the stack is healthy, configure authentication if setup did not do so, then continue with See [display mode and language](shell-and-language.md), [local authentication](authentication-local.md),
[Workspace operations](../operations/workspaces.md). For server profile, reverse proxy, backups, [OIDC](authentication-oidc.md) and [model configuration](../general/pi-configuration.md)
and recovery, use the deployment program and its manual gates; the server profile is not a for later changes. Embedded portal integration is separate from a fresh standalone setup.
drop-in replacement for the local command above.
Use the installation's normal `tht start`, `tht stop` and diagnostic commands.
Preserve its descriptor, credentials, database and persistent volumes; do not use
`down --volumes` as a routine stop or upgrade.
+70
View File
@@ -0,0 +1,70 @@
# Display mode and language
ThothII can run with its own application header (**full**) or inside an integrated
portal (**embedded**). Display mode and authentication are separate choices.
| Installation | Display | Authentication |
| --- | --- | --- |
| Standalone local instance | `full` | Local ThothII account |
| Standalone server | `full` | Local accounts or configured OIDC provider |
| Integrated portal | `embedded` | Identity verified by the portal's trusted server proxy |
## Standalone setup
Follow the complete [Italian](standalone-manual-it.md) or
[English](standalone-manual-en.md) installation procedure. It explicitly selects
`--shell-mode full --shell-default-locale en` and separates configuration, credentials,
initial migrations and startup. Do not skip those steps by running setup alone.
The authored installation descriptor contains:
```yaml
shell:
mode: full
defaultLocale: en
```
Use `it` for an Italian initial interface. Existing browser language preferences can
override that initial value. Full mode remembers language and theme in the browser.
For an existing installation, preserve the current descriptor and edit only the intended
settings; do not rerun setup to overwrite it. With a current host `tht` binary:
```sh
tht --installation /absolute/path/thothii-installation.yaml installation generate
tht --installation /absolute/path/thothii-installation.yaml start
tht --installation /absolute/path/thothii-installation.yaml status
tht --installation /absolute/path/thothii-installation.yaml doctor --json
```
Generation updates derived configuration; it does not start services. `start` applies
the installation's normal lifecycle and is not guaranteed to touch only the frontend.
Follow the deployment's maintenance procedure and retain its network, authentication and
model settings. Do not edit generated files or remove persistent volumes.
## Authentication and embedded deployments
Full mode does not configure login by itself. See [local authentication](authentication-local.md)
or [OIDC](authentication-oidc.md), with [Authentik](authentik.md) as a provider option.
Embedded mode requires a compatible portal integration, not just a descriptor toggle.
The portal owns login/logout and supplies a server-verified identity. A presentation
adapter does not authenticate users. The core must not be reachable by a route that
bypasses the trusted proxy. Do not add a second OIDC login to an already authenticated
upstream deployment.
Portal implementation details belong to the
[developer integration reference in the repository](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/install/authentication-upstream.md),
not to the standalone installation procedure.
## Three different languages
- **Interface language** controls labels, forms and application messages.
- **Session interaction language** is captured when a session is created. Resuming it
retains that language even if the interface language changes later.
- **Workspace language** concerns domain content and retrieval; switching the interface
does not translate Evidence, SQL, identifiers or database values.
In embedded mode the interface follows the portal's language and theme. A portal language
change may reload the page. Saved session artifacts remain available, but resuming work
is explicit; a reload does not by itself request a new model generation.
+1 -1
View File
@@ -319,6 +319,6 @@ The following remain future work:
## Related documents ## Related documents
- [Install and first start](first-start.md) - [Install and first start](first-start.md)
- [Shell and localization](../operations/shell-and-localization.md) - [Shell and localization](shell-and-language.md)
- [Workspace operations](../operations/workspaces.md) - [Workspace operations](../operations/workspaces.md)
- `deploy/secrets/README.md` (runtime secrets) - `deploy/secrets/README.md` (runtime secrets)
+1 -1
View File
@@ -324,6 +324,6 @@ Restano attività successive:
## Documenti collegati ## Documenti collegati
- [Install and first start](first-start.md) - [Install and first start](first-start.md)
- [Shell and localization](../operations/shell-and-localization.md) - [Shell and localization](shell-and-language.md)
- [Workspace operations](../operations/workspaces.md) - [Workspace operations](../operations/workspaces.md)
- `deploy/secrets/README.md` (runtime secrets) - `deploy/secrets/README.md` (runtime secrets)
-137
View File
@@ -1,137 +0,0 @@
# Docker installation in the current operating contexts
Rendering and authentication are independent of these Docker contexts. Explicitly
select full/en for the Mac, full/OIDC for an autonomous server, or
embedded/upstream for Omics. The same frontend image supports both renderings;
generated `config.js` and the host page decide the container, while the backend
and trusted proxy decide identity. See [shell configuration and deploy](operations/shell-and-localization.md)
and [server portal authentication](install/authentication-upstream.md). Do not
apply the standalone server authentication projection to the Omics upstream path.
ThothII uses one Compose topology:
- `frontend`
- `core`
- `catalog-db`
- `qdrant`
- `embedding`
- `embedding-model-init`
- `catalog-migrate` (one-shot)
Qdrant and Ollama embedding are required internal Compose services. Only DWH and the LLM remain
external. The fixed model is `qwen3-embedding:0.6b` with 1024 dimensions and cosine distance;
`embedding-model-init` prepares it before `core` starts.
```mermaid
flowchart TB
INSTALL["Installation descriptor"] --> CONTEXT{Context}
CONTEXT --> LOCAL["Local\nCompose local"]
CONTEXT --> SERVER["Server\nCompose server"]
CONTEXT --> SESSION["Session server\noperator services"]
CONTEXT --> AUTH["Auth runtime\nprojection services"]
LOCAL --> BUNDLE["Common secret bundle"]
SERVER --> BUNDLE
SESSION --> BUNDLE
AUTH --> BUNDLE
BUNDLE --> SERVICES["Frontend, core, catalog DB, vector, embedding"]
```
## Short ownership contract
| Componente | Ownership | Contratto operativo |
| --- | --- | --- |
| DWH | External | External endpoint configured by the installation. |
| LLM | External | Endpoint or policy outside the internal semantic infrastructure. |
| Qdrant | Internal | Required internal Compose service with persistent `qdrant-data` volume. |
| Ollama embedding | Internal | Required internal Compose service for `qwen3-embedding:0.6b`. |
## Local path: setup, migration, start
The normal local path is [Install and first start](install/first-start.md). It uses `tht setup`
to create the selected installation-local descriptor and runs the catalog migration explicitly
before application startup.
If an operator intentionally prepares the descriptor and protected files by hand, the equivalent
foreground launch is:
```sh
cp deploy/env/local.env.example deploy/env/local.env
cp deploy/secrets/thothii.secrets.example deploy/secrets/thothii.secrets
chmod 600 deploy/secrets/thothii.secrets
# Copy docs/install/examples/thothii-installation.local.yaml to a protected operator path,
# replace every placeholder, chmod it 600, then set that exact path as
# THT_INSTALLATION_CONFIG_SOURCE in deploy/env/local.env.
./scripts/run-stack.sh
```
Set these values in `deploy/env/local.env`:
- `PI_AUTH_FILE`
- `THT_SECRETS_FILE`
- `THT_INSTALLATION_CONFIG_SOURCE` (the exact protected host `thothii-installation.yaml`)
- `THT_WORKSPACE_GIT_REMOTE`
- DWH endpoint
- LLM endpoint
Do not put secrets in `.env`. Runtime secrets belong in the
`deploy/secrets/thothii.secrets` bundle.
## Secret bundle
The documented and supported bundle keys are:
```dotenv
THT_MODEL_API_KEY=...
THT_DWH_API_KEY=...
OPENAI_API_KEY=...
```
`THT_MODEL_API_KEY` is available only as an explicitly declared catalog bundle key. A
metadata-generation provider references one audited bundle name from `deploy/secrets/README.md`
through `modelCatalog.providers.<provider>.authentication.apiKeyEnv`.
`apiKeyEnv` may be omitted only for an explicit endpoint that accepts unauthenticated requests;
hosted/default endpoints remain keyed.
Provider/model/endpoint settings stay in the protected installation descriptor; raw keys do not.
Compose mounts exactly `THT_INSTALLATION_CONFIG_SOURCE` into `core` as a read-only config and sets
the backend-only runtime path `THT_INSTALLATION_CONFIG_FILE` to
`/run/thothii-installation/thothii-installation.yaml`. Do not set the runtime path in the host env.
Descriptor and bundle changes are loaded only after application restart.
A private PEM CA remains outside the bundle and must be mounted through a reviewed Compose override.
## Preprocessing
Preprocessing runs through the native host CLI and the installation descriptor:
```sh
tht --installation /percorso/assoluto/thothii-installation.yaml \
workspace preprocess run --workspace <workspace-id>
```
Per rimuovere soltanto gli indici e gli artifact ricostruibili, preservando Memory e domande risolte:
```sh
tht --installation /percorso/assoluto/thothii-installation.yaml \
workspace preprocess clear --workspace <workspace-id>
```
The CLI runs the profile-gated `workspace-maintenance` service. See the
[preprocessing contract](contracts/workspace-preprocessing-cli.md) and the
[Evidence guide](evidence.md) for details.
## Server
For server installations, use the server profile with the session overlay:
```sh
docker compose --env-file deploy/env/server.env \
-f compose.yaml -f deploy/compose.server.yaml \
-f deploy/compose.session-server.yaml.example up --build -d
```
Also see [Workspace operations](operations/workspaces.md). Server deployment, reverse-proxy,
backup, and recovery remain manual-gated operations; do not treat the local profile as a server
replacement.
@@ -0,0 +1,435 @@
# Revisione e bonifica della documentazione
Data: 15 settembre 2026. Baseline: `6a4634dcf1b1df4ad29510a4da371245abd5c666`.
Ambito: 121 Markdown e 23 altri file già presenti in `docs/`, navigazione, collegamenti
locali, build e verificatori documentali. Il documento conserva la proposta iniziale
e registra sotto l'esecuzione successivamente autorizzata dall'utente. È interno,
non una pagina del manuale pubblico.
## Bonifica eseguita dopo approvazione
Ritirati 35 file dal working tree: 23 Markdown e 12 artefatti visuali generati
(otto PNG, due HTML e due JSON). Recuperabili integralmente dalla revisione
`5f3a7f5975b96fae1e1cdd0da08b4d60d41064cc`, precedente a questo intervento,
mediante `git show REVISIONE:percorso`. Non eliminata né riscritta la storia Git.
- Dettagli Compose utili consolidati in [riferimento developer](../operations/compose-reference.md);
README rinvia alle sole due procedure manuali IT/EN per installazioni nuove.
- M1–M3, E1–E3, X1 e i piani Catalog/model già implementati consolidati nel
[record di rilascio](../reports/knowledge-archives-release.md). Test storici non
presentati come collaudi attuali; estensioni e accettazioni aperte conservate.
- Prompt security assorbito nel PRD, senza approvare il progetto di hardening.
- Snapshot di progetto riscritto; overview corretta su Catalog, Memory ed Evidence.
- Verificatori auth/DWH e fixture riallineati ai documenti attuali: controlli su
permessi, diagnostica, segreti, TLS, separazione dei servizi e link. Le simulazioni
del vecchio journal scanner, non più presente nei documenti, non sono conservate
come finto collaudo del runtime. Le suite applicative non vengono modificate.
- Rimandi Markdown verificati; contratti, ADR (anche superati), progetti con
decisioni ancora aperte e runbook/report con rollback o accettazione pendente
restano nel repository, esclusi dal sito. Non spostati gate in nuove issue né
dichiarati chiusi: lo snapshot corrente li rende espliciti. ADR 0019 conservato
senza dedurre dal solo nome che tutte le sue estensioni siano implementate.
- Pubblicazione effettiva distinta dal branch `pages`; procedura ripetibile nel
[runbook del manuale](../operations/public-docs-publication.md).
File ritirati (percorsi dalla radice):
- `docs/architecture/thothii-core-sequence.visual-check.1440x900.dark.png`
- `docs/architecture/thothii-core-sequence.visual-check.1440x900.light.png`
- `docs/architecture/thothii-core-sequence.visual-check.2048x1320.dark.png`
- `docs/architecture/thothii-core-sequence.visual-check.2048x1320.light.png`
- `docs/architecture/thothii-core-sequence.visual-check.html`
- `docs/architecture/thothii-core-sequence.visual-check.json`
- `docs/architecture/thothii-runtime.visual-check.1440x900.dark.png`
- `docs/architecture/thothii-runtime.visual-check.1440x900.light.png`
- `docs/architecture/thothii-runtime.visual-check.2048x1320.dark.png`
- `docs/architecture/thothii-runtime.visual-check.2048x1320.light.png`
- `docs/architecture/thothii-runtime.visual-check.html`
- `docs/architecture/thothii-runtime.visual-check.json`
- `docs/installazione-docker-4-contesti.md`
- `docs/plans/2026-08-26-metadata-catalog-from-thothai.md`
- `docs/plans/2026-08-28-ai-catalog-description-generation-spec.md`
- `docs/plans/2026-08-28-ai-catalog-description-generation.md`
- `docs/plans/2026-09-02-installation-model-catalog.md`
- `docs/plans/2026-09-08-memory-m1-validation.md`
- `docs/plans/2026-09-08-security-hardening-resume-prompt.md`
- `docs/plans/2026-09-09-archive-repair-x1-validation.md`
- `docs/plans/2026-09-09-evidence-e1-validation.md`
- `docs/plans/2026-09-09-evidence-e2-validation.md`
- `docs/plans/2026-09-09-evidence-e3-validation.md`
- `docs/plans/2026-09-09-memory-m2-validation.md`
- `docs/plans/2026-09-09-memory-m3-validation.md`
- `docs/research/2026-08-23-legacy-thothai-metadata-capabilities.md`
- `docs/research/2026-08-23-postgresql-catalog-deployment-constraints.md`
- `docs/research/2026-08-23-thothii-metadata-publication-qdrant-seams.md`
- `docs/research/2026-09-07-antigravity-gemini-flash-value.md`
- `docs/research/2026-09-07-deepseek-harness-terminal.md`
- `docs/research/2026-09-07-omp-codex-zcode.md`
- `docs/research/2026-09-07-pi-config-vs-oh-my-pi.md`
- `docs/research/2026-09-07-zai-coding-plan-cli-quota.md`
- `docs/research/2026-09-07-zed-acp-omp-pi.md`
- `docs/research/text-to-sql-products.md`
Le sezioni successive e l'inventario fotografano la revisione iniziale: la presente
sezione prevale per le azioni eseguite. Gli altri ritiri sono rinviati finché non
si chiudono i relativi gate; non si cancellano documenti solo perché datati.
## Esito e modifica applicata
Il sito confondeva quattro funzioni: manuale del prodotto, riferimento del codice,
progettazione e diario delle consegne. Il problema non era soltanto la lunghezza della nav:
le pagine non elencate potevano comunque essere generate e indicizzate.
La configurazione ora pubblica soltanto 20 pagine e cinque asset esplicitamente ammessi.
Il resto è escluso da HTML, file copiati e indice di ricerca. Le nuove pagine pubbliche
sono `product-overview.md`, `install/shell-and-language.md` e `usage/memory.md`;
i documenti tecnici da cui sono state ricavate restano intatti nel repository.
Home e `install/first-start.md` sono state riscritte per dare un ingresso univoco.
Non è stato cancellato alcun documento della baseline.
`exclude_docs` nega per default la pubblicazione: aggiungere un file a `docs/` non basta
più a metterlo online. La build e la pipeline verificano corrispondenza tra nav ed
eccezioni, pagine generate, asset e ricerca. I riferimenti tecnici ancora necessari
al lettore rinviano esplicitamente ai sorgenti su Gitea, non a pagine interne del sito.
**Fuori dal manuale non significa privato.** Il repository ThothII è pubblico: questi
file e la cronologia restano leggibili su Gitea. Per riservatezza reale occorrerebbe una
decisione separata su repository/accessi e sulla storia già pubblicata. Questa bonifica
non modifica autorizzazioni, altri repository o l'installazione applicativa.
## Tre destinazioni, con regole diverse
| Destinazione | Contenuto | Regola |
| --- | --- | --- |
| Manuale pubblico | Prodotto, uso, installazione, configurazione, amministrazione | Descrivere ciò che il lettore può fare oggi; distinguere prerequisiti e limiti verificati. |
| Riferimento developer | Architettura, contratti, ADR, test ripetibili, integrazione | Conservare nel repository, fuori da MkDocs; un'autorità per ciascun contratto. |
| Lavoro temporaneo o storico | Piani attuati, prompt di ripresa, survey, report di singola consegna | Estrarre decisioni, difetti aperti e rollback ancora necessari; poi ritirare dal working tree con Git come archivio. |
Un file datato non è automaticamente obsoleto. Un contratto con `v3` nel nome non è
automaticamente sostituito da un descriptor v4: sono versioni di oggetti diversi.
Un ADR superato conserva il motivo della decisione e il collegamento al successore.
## Rilievi prioritari, con motivazione
### 1. Percorsi di installazione concorrenti — corretto per il pubblico
`install/first-start.md` proponeva ancora setup senza `--configure-only` e avvio con
`run-stack.sh`, mentre le guide manuali separano segreti, migrazione e avvio per lo
stesso descriptor/progetto. Ora è un punto d'ingresso alle due procedure complete,
non una terza ricetta. `installazione-docker-4-contesti.md` rimane interno: consolidarne
i dettagli ancora esclusivi in una procedura developer di distribuzione, poi ritirarlo.
Anche il percorso rapido nel README va riallineato prima di eliminare quei dettagli.
### 2. Ricerca architetturale presentata come stato corrente — ritirare dopo estrazione
`research/2026-08-23-thothii-metadata-publication-qdrant-seams.md` dichiara ancora il
passaggio catalog-to-core «deferred» e descrive `annotations.yaml` come input corrente.
ADR 0016 e gli attuali contratti registrano invece PostgreSQL come autorità per i
consumatori del core. Non usarlo per implementare nuovi comportamenti.
`research/2026-08-23-postgresql-catalog-deployment-constraints.md` dichiara esplicitamente
di essere parzialmente superato da ADR 0004. L'inventario legacy ThothAI del 23 agosto
è esplicitamente storico. Estrarre soltanto vincoli non già presenti in ADR/contratti;
poi eliminare i tre file dal working tree, conservandoli nella storia Git.
### 3. Decisioni correnti distribuite tra troppi piani — consolidare prima di eliminare
Per Memory/Evidence convivono piani di famiglia, M1, amministrazione comune, revisione
di semplificazione e sette resoconti M1–M3/E1–E3/X1. Conservare gli invarianti nei
contratti e gli scenari ripetibili in `testing/`; trasferire i gate non chiusi in issue.
Solo dopo si possono ritirare piani e validazioni di incremento.
`adr/0019-author-evidence-in-app-with-automatic-activation.md` ha un nome che suggerisce
editor in-app e attivazione automatica, ma il titolo descrive editor esterni e
consolidamento manuale. Contiene anche «Implementation is pending»: non è un affidabile
indicatore dello stato di tutte le parti della release. Conservare numero/decisione,
correggere metadati e rimandi e distinguere eventuali estensioni ancora pendenti.
### 4. Report operativi e piani con stato non aggiornato — non cancellare in blocco
Il report `2026-09-12-ui-visual-review-delivery.md` apre con «non integrata in main»;
le integrazioni successive sono documentate altrove. I piani full-shell dicono ancora
«deploy server non eseguito», mentre esistono report di rilascio server del 14 settembre.
Ciò prova che lo stato è distribuito, non che tutti i collaudi siano conclusi.
Consolidare report UI del 12–13 settembre in un record di rilascio per versione.
Non ritirare i report del 14 settembre né i runbook di upgrade finché rollback,
accettazione interattiva e finestra di osservazione non risultano chiusi dall'operatore.
`server-handoff-260906-preprocessing-complete.md` va confrontato con il runbook corrente
`server-codex-handoff.md`: estrarre eventuali passaggi di preprocessing esclusivi prima
di ritirare la consegna datata.
### 5. Ricerca su strumenti personali estranea al manuale — candidata all'eliminazione
I sei confronti CLI/editor/provider del 7 settembre (Antigravity, DeepSeek/CyberArk,
OMP/Codex/ZCode, Pi/Oh My Pi, quota Z.ai e Zed/ACP) non spiegano un contratto di ThothII.
Prezzi, quote e confronti non sono stati riverificati in questa revisione. Proposta:
toglierli dal repository del prodotto, mantenendoli in Git o trasferendoli, su scelta
del proprietario, a una raccolta personale. Stessa destinazione proposta per
`research/text-to-sql-products.md`, che è una ricognizione di mercato.
Non applicare automaticamente questa regola alla ricerca ERD, relationship e
classificatore sensibile: prima verificare se documenta decisioni ancora aperte.
### 6. Controlli e snapshot già disallineati — debito da correggere
Due verificatori falliscono su file già assenti nella baseline, non per l'esclusione
da MkDocs introdotta qui:
- `scripts/auth-docs-smoke.sh`: manca `docs/install/local.md`.
- `scripts/verify-dwh-auth-docs.sh`: manca `docs/operations/psd-dwh-auth-rollout.md`.
Le relative suite di fixture conservano la vecchia struttura. Non ricreare documenti
obsoleti per far passare i test: aggiornare i controlli ai contratti attuali, mantenendo
le verifiche su segreti, modalità di autenticazione e TLS. Questo riallineamento è
proposto, non eseguito in questa bonifica editoriale.
`PROJECT_STATE.md` cita nove percorsi `docs/*.md` non più esistenti, fra cui il programma
server PSD del 20 agosto, l'accettazione Evidence del 25 agosto e il rollout dwh-auth.
Inoltre accumula consegne anziché restare uno snapshot breve. Riscriverlo come stato
attuale, gate aperti e rimandi esistenti. I normali link Markdown relativi dei documenti
esistenti non presentano target mancanti nella verifica eseguita: i riferimenti rotti
qui citati sono anche percorsi in backtick o imposti dagli script.
## Proposta operativa in ordine
0. **Collegare il sito servito al risultato della pipeline:** il controllo remoto descritto
sotto ha rilevato una copia statica vecchia. Prima di dichiarare la bonifica online,
aggiornare il percorso di pubblicazione sul server con accesso amministrativo verificato.
1. **Separazione pubblica:** applicata; niente cancellazioni di sorgenti.
2. **Riallineare le autorità:** snapshot, verificatori, ADR 0019, README e stato delle
consegne; spostare gate aperti in issue senza marcarli completati.
3. **Ritiro a basso rischio, previa approvazione:** confronti personali, ricerca già
esplicitamente superata, prompt di ripresa dopo assorbimento nel PRD, output visuali
rigenerabili. Controllare prima i riferimenti in tutto il repository.
4. **Consolidamento:** piani completati e report incrementali; non perdere criteri di
accettazione, problemi aperti, provenienza delle immagini e rollback ancora validi.
5. **Riorganizzazione eventuale:** `docs/` per il pubblico, `developer-docs/` per
architettura/contratti/ADR/testing, `project-notes/` per lavoro in corso. È un secondo
intervento: aggiornare insieme tutti i link in README, AGENTS, CONTEXT, script e codice.
Per ogni ritiro usare un commit dedicato con motivazione e documento sostitutivo.
Non creare una cartella `archive/` piena di copie obsolete: la cronologia Git è già
l'archivio, salvo una necessità operativa o di conservazione esplicita.
## Inventario e destinazione proposta
L'inventario seguente distingue l'azione proposta dalla sola esclusione già applicata
al sito. «Consolidare» e «ritirare» non indicano cancellazioni eseguite.
<!-- inventory:start -->
| File (relativo a `docs/`) | Destinazione | Azione proposta |
| --- | --- | --- |
| `adr/0001-postgres-metadata-catalog.md` | Developer | Conservare decisione architetturale. |
| `adr/0002-workspace-database-secret-references.md` | Developer | Conservare decisione architetturale. |
| `adr/0003-installation-local-database-bindings.md` | Developer | Conservare decisione architetturale. |
| `adr/0004-fastify-kysely-metadata-catalog.md` | Developer | Conservare decisione architetturale. |
| `adr/0005-hard-delete-catalog-tables-during-synchronization.md` | Developer | Conservare ADR superato e catena dei successori; non eliminarlo. |
| `adr/0006-separate-physical-and-logical-relationships.md` | Developer | Conservare decisione architetturale. |
| `adr/0007-durable-authoritative-schema-synchronization.md` | Developer | Conservare decisione architetturale. |
| `adr/0008-allow-manual-catalog-metadata-cleanup.md` | Developer | Conservare decisione architetturale. |
| `adr/0009-use-one-sequential-description-generation-run.md` | Developer | Conservare decisione architetturale. |
| `adr/0010-allow-bounded-real-source-samples-for-description-generation.md` | Developer | Conservare decisione architetturale. |
| `adr/0011-gate-source-samples-with-a-sensitive-data-flag.md` | Developer | Conservare decisione architetturale. |
| `adr/0012-use-the-catalog-as-the-logical-relationship-authority.md` | Developer | Conservare decisione architetturale. |
| `adr/0013-use-one-installation-model-catalog-with-runtime-projections.md` | Developer | Conservare decisione architetturale. |
| `adr/0014-assess-sensitive-columns-locally-from-source-content.md` | Developer | Conservare ADR superato e catena dei successori; non eliminarlo. |
| `adr/0015-use-progressive-sampling-for-sensitive-columns.md` | Developer | Conservare decisione architetturale. |
| `adr/0016-use-postgres-metadata-for-all-core-consumers.md` | Developer | Conservare decisione architetturale. |
| `adr/0017-separate-reference-vectors-from-runtime-memory.md` | Developer | Conservare decisione architetturale. |
| `adr/0018-use-postgres-for-memory-and-qdrant-for-retrieval.md` | Developer | Conservare decisione architetturale. |
| `adr/0019-author-evidence-in-app-with-automatic-activation.md` | Developer | Conservare; allineare nome/stato alla decisione manuale effettiva. |
| `adr/0020-unify-administration-pages-and-use-namespaced-routes.md` | Developer | Conservare decisione architetturale. |
| `adr/0021-separate-shell-modes-and-replaceable-portal-adapter.md` | Developer | Conservare decisione architetturale. |
| `adr/0022-separate-ui-locale-from-session-interaction-language.md` | Developer | Conservare decisione architetturale. |
| `agents/domain.md` | Developer | Conservare regole di contribuzione/agenti, fuori dal sito. |
| `agents/issue-tracker.md` | Developer | Conservare regole di contribuzione/agenti, fuori dal sito. |
| `agents/triage-labels.md` | Developer | Conservare regole di contribuzione/agenti, fuori dal sito. |
| `architecture/application-shell.md` | Developer | Conservare riferimento corrente; distinguere il prodotto dalla struttura del codice. |
| `architecture/authentication.md` | Developer | Conservare riferimento corrente; distinguere il prodotto dalla struttura del codice. |
| `architecture/components.md` | Developer | Conservare riferimento corrente; distinguere il prodotto dalla struttura del codice. |
| `architecture/overview.md` | Developer | Conservare riferimento corrente; distinguere il prodotto dalla struttura del codice. |
| `architecture/thothii-core-sequence.html` | Developer/build | Conservare sorgente/diagramma tecnico; non pubblicare nel manuale. |
| `architecture/thothii-core-sequence.visual-check.1440x900.dark.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-core-sequence.visual-check.1440x900.light.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-core-sequence.visual-check.2048x1320.dark.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-core-sequence.visual-check.2048x1320.light.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-core-sequence.visual-check.html` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-core-sequence.visual-check.json` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-core.sequence.json` | Developer/build | Conservare sorgente/diagramma tecnico; non pubblicare nel manuale. |
| `architecture/thothii-runtime.architecture.json` | Developer/build | Conservare sorgente/diagramma tecnico; non pubblicare nel manuale. |
| `architecture/thothii-runtime.html` | Developer/build | Conservare sorgente/diagramma tecnico; non pubblicare nel manuale. |
| `architecture/thothii-runtime.visual-check.1440x900.dark.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-runtime.visual-check.1440x900.light.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-runtime.visual-check.2048x1320.dark.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-runtime.visual-check.2048x1320.light.png` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-runtime.visual-check.html` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `architecture/thothii-runtime.visual-check.json` | Transitorio | Ritirare output QA rigenerabili dopo conferma che non siano baseline uniche. |
| `contracts/archive-repair.md` | Developer | Conservare contratto e versioni semantiche; aggiornare insieme a codice/test. |
| `contracts/catalog-schema-snapshot.md` | Developer | Conservare contratto e versioni semantiche; aggiornare insieme a codice/test. |
| `contracts/curated-evidence-v4.md` | Developer | Conservare contratto e versioni semantiche; aggiornare insieme a codice/test. |
| `contracts/portal-shell-adapter-v1.md` | Developer | Conservare contratto e versioni semantiche; aggiornare insieme a codice/test. |
| `contracts/tht-dwh.md` | Developer | Conservare contratto e versioni semantiche; aggiornare insieme a codice/test. |
| `contracts/workspace-evidence-v3.md` | Developer | Conservare contratto e versioni semantiche; aggiornare insieme a codice/test. |
| `contracts/workspace-preprocessing-cli.md` | Developer | Conservare contratto e versioni semantiche; aggiornare insieme a codice/test. |
| `disambiguazione-iniziale.md` | Developer | Conservare invarianti di gate/ledger; il percorso utente è in skills.md. |
| `evidence.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `general/pi-configuration.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `gestione-memory.md` | Developer | Conservare contratto di Memory; istruzioni pubbliche in usage/memory.md. |
| `guida-utente.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `index.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/authentication-local.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/authentication-oidc.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/authentication-upstream.md` | Developer/integratore | Conservare integrazione e confine di fiducia; guida pubblica separata. |
| `install/authentik.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/dwh-auth-client-enrollment.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/dwh-auth-server.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/dwh-auth-tls.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/examples/thothii-installation.local.yaml` | Asset pubblico | Conservare asset o esempio operativo. |
| `install/examples/thothii-installation.server.yaml` | Asset pubblico | Conservare asset o esempio operativo. |
| `install/examples/workspace-bindings.env.example` | Asset pubblico | Conservare asset o esempio operativo. |
| `install/first-start.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/standalone-manual-en.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `install/standalone-manual-it.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `installazione-docker-4-contesti.md` | Misto/transitorio | Estrarre dettagli server unici, poi ritirare la ricetta duplicata. |
| `javascripts/layout-init.js` | Asset pubblico | Conservare asset o esempio operativo. |
| `operations/database-management.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `operations/docker-refresh.md` | Operazioni interne | Conservare finché descrive il contesto locale attivo; poi consolidare il lifecycle. |
| `operations/sensitivity-analysis.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `operations/server-codex-handoff.md` | Operazioni interne | Conservare runbook finché migrazione e rollback sono operativamente necessari. |
| `operations/server-handoff-260906-preprocessing-complete.md` | Operazioni/transitorio | Confrontare con server-codex-handoff, assorbire passaggi unici, poi ritirare. |
| `operations/server-upgrade-gitea-workspace-v2.md` | Operazioni interne | Conservare runbook finché migrazione e rollback sono operativamente necessari. |
| `operations/shell-and-localization.md` | Developer/integratore | Conservare integrazione e confine di fiducia; guida pubblica separata. |
| `operations/workspaces.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `plans/2026-08-26-metadata-catalog-from-thothai.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-08-28-ai-catalog-description-generation-spec.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-08-28-ai-catalog-description-generation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-02-installation-model-catalog.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-08-evidence-management.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-08-memory-evidence-administration.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-08-memory-evidence-simplification-review.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-08-memory-m1-spec.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-08-memory-m1-validation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-08-memory-management.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-08-security-hardening-prd.md` | Progetto attivo | Conservare PRD aperto; risolvere decisioni/issue prima del ritiro. |
| `plans/2026-09-08-security-hardening-resume-prompt.md` | Transitorio | Ritirare dopo trasferimento di istruzioni e questioni aperte nel PRD/issue. |
| `plans/2026-09-09-archive-repair-x1-validation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-09-evidence-e1-validation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-09-evidence-e2-validation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-09-evidence-e3-validation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-09-memory-m2-validation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-09-memory-m3-validation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-10-administration-pages-spec.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-13-full-shell-and-portal-integration.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-13-full-shell-spec.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/2026-09-14-manual-standalone-installation.md` | Progetto attivo | Conservare fino alla verifica su tre piattaforme; poi assorbire gate e ritirare. |
| `plans/administration-pages/01-navigation.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/administration-pages/02-workspace-readiness.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/administration-pages/03-workbench-family.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `plans/administration-pages/04-embedded-acceptance.md` | Progetto/transitorio | Consolidare invarianti in contratti e gate in issue/test; poi ritirare piano o resoconto. |
| `reports/2026-09-02-psd-sensitivity-shadow.md` | Developer/benchmark | Conservare risultati aggregati come evidenza versionata, non garanzia corrente. |
| `reports/2026-09-02-sensitivity-ner-license-inventory.md` | Developer/release | Conservare provenienza licenze della release; rigenerare quando cambiano le dipendenze. |
| `reports/2026-09-03-psd-progressive-sensitivity-shadow.md` | Developer/benchmark | Conservare risultati aggregati come evidenza versionata, non garanzia corrente. |
| `reports/2026-09-12-admin-issues-28-31.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-12-context-shelf-a-implementation.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-12-ui-survey-and-graphic-revision-plan.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-12-ui-visual-review-delivery.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-12-unified-interaction-model.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-13-full-shell-implementation.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-13-header-layout-refinements.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-13-knowledge-reading.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-13-knowledge-typography.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-13-shell-simplification-review.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-13-visual-shell-integration.md` | Progetto/transitorio | Consolidare in record di release; ritirare solo dopo trasferimento gate/rollback utili. |
| `reports/2026-09-14-session-dialogs-release.md` | Operazioni/release | Conservare finché accettazione, osservazione e rollback non sono chiusi. |
| `reports/2026-09-14-session-layout-memory-fix.md` | Operazioni/release | Conservare finché accettazione, osservazione e rollback non sono chiusi. |
| `requirements.lock` | Developer/build | Conservare dipendenze e lock; esclusi dal sito. |
| `requirements.txt` | Developer/build | Conservare dipendenze e lock; esclusi dal sito. |
| `research/2026-08-23-legacy-thothai-metadata-capabilities.md` | Storico/superato | Estrarre vincoli non assorbiti da ADR/contratti, poi ritirare. |
| `research/2026-08-23-postgresql-catalog-deployment-constraints.md` | Storico/superato | Estrarre vincoli non assorbiti da ADR/contratti, poi ritirare. |
| `research/2026-08-23-thothii-metadata-publication-qdrant-seams.md` | Storico/superato | Estrarre vincoli non assorbiti da ADR/contratti, poi ritirare. |
| `research/2026-08-31-browser-erd-library-options.md` | Developer/ricerca | Verificare decisioni ancora aperte; assorbire conclusioni in issue/ADR, poi ritirare. |
| `research/2026-08-31-relationship-management-thothai-to-thothii.md` | Developer/ricerca | Verificare decisioni ancora aperte; assorbire conclusioni in issue/ADR, poi ritirare. |
| `research/2026-09-02-local-sensitive-column-classifier-libraries.md` | Developer/ricerca | Verificare decisioni ancora aperte; assorbire conclusioni in issue/ADR, poi ritirare. |
| `research/2026-09-07-antigravity-gemini-flash-value.md` | Fuori prodotto | Proposta di ritiro dal working tree; Git o raccolta personale su scelta del proprietario. |
| `research/2026-09-07-deepseek-harness-terminal.md` | Fuori prodotto | Proposta di ritiro dal working tree; Git o raccolta personale su scelta del proprietario. |
| `research/2026-09-07-omp-codex-zcode.md` | Fuori prodotto | Proposta di ritiro dal working tree; Git o raccolta personale su scelta del proprietario. |
| `research/2026-09-07-pi-config-vs-oh-my-pi.md` | Fuori prodotto | Proposta di ritiro dal working tree; Git o raccolta personale su scelta del proprietario. |
| `research/2026-09-07-zai-coding-plan-cli-quota.md` | Fuori prodotto | Proposta di ritiro dal working tree; Git o raccolta personale su scelta del proprietario. |
| `research/2026-09-07-zed-acp-omp-pi.md` | Fuori prodotto | Proposta di ritiro dal working tree; Git o raccolta personale su scelta del proprietario. |
| `research/text-to-sql-products.md` | Fuori prodotto | Proposta di ritiro dal working tree; Git o raccolta personale su scelta del proprietario. |
| `skills.md` | Pubblico | Conservare; istruzioni di prodotto/operatore. |
| `stylesheets/extra.css` | Asset pubblico | Conservare asset o esempio operativo. |
| `testing/2026-08-29-ai-catalog-description-generation-acceptance.md` | Developer/QA | Conservare criteri ripetibili e gate; separare risultati di singola release. |
| `testing/2026-08-31-metadata-privacy-description-test-plan.md` | Developer/QA | Conservare scenari; ridurre duplicazioni e distinguere test proposti da eseguiti. |
| `testing/authentication-manual-acceptance.md` | Developer/QA | Conservare criteri ripetibili e gate; separare risultati di singola release. |
| `testing/evidence-lifecycle-test-plan.md` | Developer/QA | Conservare criteri ripetibili e gate; separare risultati di singola release. |
<!-- inventory:end -->
## Verifica iniziale, prima dell'esecuzione dei ritiri
- Build MkDocs con `--strict` e dipendenze bloccate: superata.
- Verifica confine pubblico: 20 pagine, nessun file interno generato o indicizzato.
- Otto fixture positive/negative del confine pubblico: superate.
- Suite corrente installazione/workspace: superata.
- Due verificatori legacy: fallimenti preesistenti descritti sopra; non dichiarati verdi.
- Nessuna certificazione di nuova installazione o nuovo collaudo applicativo.
La pubblicazione remota va verificata separatamente dalla build locale: il ramo sorgente
`main`, il ramo generato `pages` e il sito servito non sono la stessa prova.
### Primo esito remoto del 15 settembre 2026 — diagnosi storica
Il commit `043ffdfa` è stato pubblicato su `main`. La
[pipeline 122](https://git.tylconsulting.it/mptyl/ThothII/actions/runs/122) è terminata
con successo e il ramo `pages` contiene 20 pagine pubbliche, senza pagine interne
nell'indice di ricerca verificato anonimamente.
L'URL `https://git.tylconsulting.it/thothii-docs/` serve però ancora la home precedente,
con `Last-Modified: Wed, 26 Aug 2026 09:00:07 GMT`; il suo indice di ricerca include
ancora architettura e contratti. La richiesta senza cache restituisce lo stesso risultato.
Nel repository la pipeline aggiorna soltanto il ramo `pages`: non è documentato il
collegamento alla directory effettivamente servita dal web server.
Il tentativo SSH in sola lettura con la configurazione locale non è proseguito:
manca una host key ED25519 conosciuta per `git.tylconsulting.it`. Non è stata disabilitata
la verifica dell'identità del server. Servono host/alias amministrativo verificato e
percorso o meccanismo di pubblicazione prima di intervenire sul sito effettivo.
Al momento di quella verifica la copia servita restava da aggiornare. L'accesso è
stato poi risolto usando l'alias SSH già configurato e fidato `contabo`; nessuna
host key è stata aggirata.
## Verifica della bonifica eseguita
- Build MkDocs strict e confine pubblico: superati, 20 pagine.
- Otto fixture del confine pubblico: superate.
- Contratti auth/DWH correnti e relative fixture di mutazione: superati.
- Suite installazione/workspace, Compose canonico e contratto comandi deployment: superate.
- Link Markdown locali di README, PROJECT_STATE e tutti i documenti: nessun target mancante.
- `git diff --check`: superato.
- `test-no-deployment-coupling.sh`: resta rosso su rilievi preesistenti relativi
a contenuti locali PSD e route Omics. Eseguita anche la versione dello script
precedente alla bonifica: stesso esito e identico insieme di rilievi, nessuno nuovo.
Non è un collaudo del sito e non è stato indebolito per nascondere i risultati.
- Nessuna nuova certificazione applicativa, modifica del runtime o chiusura di
collaudi reali. I gate elencati nello snapshot restano espliciti.
## Pubblicazione effettiva completata — 15 settembre 2026
- Sorgente: `4ff91e8d6e771545bc3d8e7a0ac0b027ef66ba14`, pubblicata su `main`.
- [Pipeline 124](https://git.tylconsulting.it/mptyl/ThothII/actions/runs/124):
completata con successo, inclusi i nuovi controlli auth/DWH e pubblicazione `pages`.
- Release servita: `/srv/thothii-docs/releases/20260915T123738Z-4ff91e8d`.
Il symlink `current` punta a questa release; ricreato soltanto `thothii-docs`.
- Configurazione nginx copiata identica; container healthy. Confronto degli ID
dei container prima/dopo: nessun altro container ricreato o fermato.
- Verifica anonima HTTP: home, ricerca e guide manuali IT/EN rispondono 200.
SHA-256 di home e indice remoto identici alla build locale; ricerca: 20 pagine,
nessuna interna.
- Campioni interni `architecture/overview/`, `plans/2026-09-08-memory-management/`
e `operations/compose-reference/`: HTTP 404.
- Release precedente `releases/20260826T090022Z-e910c7d` conservata per rollback;
nessuna vecchia release cancellata. Pubblicazioni future richiedono il passaggio
esplicito del runbook, non il solo push di `main`.
Il precedente blocco sulla copia statica obsoleta è quindi risolto.
+48
View File
@@ -0,0 +1,48 @@
# Compose reference for maintainers
This is an internal topology reference, not a second fresh-installation recipe. Operators
start with the [Italian](../install/standalone-manual-it.md) or
[English](../install/standalone-manual-en.md) manual. It replaces the duplicated four-context
Docker guide without changing the runtime.
The base topology contains frontend, core, catalog-db, qdrant, embedding, embedding-model-init,
catalog-migrate and profile-gated workspace-maintenance. Pi runs in core; DWH and generative
model endpoints remain installation settings. Reference preprocessing and Memory have distinct
lifecycles and collections. See [preprocessing](../contracts/workspace-preprocessing-cli.md).
## Configuration and migration boundaries
- Use one physical absolute installation descriptor path, its generated operator environment,
Compose project name, base/profile overlays, transport overlays and generated model overlay.
Mixing a generic `deploy/env/local.env` invocation with a native `tht` installation creates
a different stack; it is not an equivalent lifecycle command.
- `THT_INSTALLATION_CONFIG_SOURCE` identifies the protected host descriptor. The read-only
backend runtime mount is `/run/thothii-installation/thothii-installation.yaml`, exposed through
`THT_INSTALLATION_CONFIG_FILE`; the runtime path is not a host source path.
- Catalog runtime/migrator passwords and provider credentials are protected files. A provider's
`authentication.apiKeyEnv` names an allowed bundle entry; it is not a raw key. Private CAs
are separately mounted PEM files, not bundle values. See the repository's
`deploy/secrets/README.md` and the [model catalog guide](../general/pi-configuration.md).
- Generate projections after descriptor edits. Do not edit generated model/auth/frontend files.
Apply the installation's normal restart process when authored configuration changes.
- Explicitly start catalog-db and run catalog-migrate before application rollout on a fresh
database or after an approved schema update. That service runs Catalog and Memory migrations;
neither normal backend startup nor `tht start` implicitly performs them.
- Server deployments retain their reviewed session/auth/network/storage overlays. A writable
server Pi-state parent must be initialized with the regular targets expected by the read-only
nested mounts; use `scripts/prepare-server-pi-state.sh` with the installation's verified UID/GID.
## Deployment-specific authority
For the prepared generic server environment, the base/profile/session override
combination is `-f compose.yaml -f deploy/compose.server.yaml
-f deploy/compose.session-server.yaml.example`, with `--env-file` supplied before
the overrides. Add the reviewed transport/generated overlays for that installation;
this fragment alone is not a complete startup command.
The current [server handoff](server-codex-handoff.md) and
[legacy upgrade runbook](server-upgrade-gitea-workspace-v2.md) retain maintenance, backup and
rollback gates. Embedded/upstream identity is not standalone OIDC. Local/standalone setup
does not authorize replacing a running server stack, resetting volumes, or copying another
machine's descriptor. `scripts/run-stack.sh` remains a low-level path for an explicitly
prepared generic environment, not the public manual's default startup command.
+3 -3
View File
@@ -67,7 +67,7 @@ SSH uses a private key, optional key passphrase, mandatory `known_hosts`, and op
TLS CA/server name. REST prefers `POST /rpc/schema_snapshot`; when it is absent, the catalog may TLS CA/server name. REST prefers `POST /rpc/schema_snapshot`; when it is absent, the catalog may
use the same strict v1 snapshot through one read-only `POST /rpc/run_query`. An unavailable use the same strict v1 snapshot through one read-only `POST /rpc/run_query`. An unavailable
capability, malformed snapshot, or connector error applies no catalog changes. See the capability, malformed snapshot, or connector error applies no catalog changes. See the
[schema snapshot contract](../contracts/catalog-schema-snapshot.md). [schema snapshot contract](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/catalog-schema-snapshot.md).
## Synchronize authoritative schema metadata ## Synchronize authoritative schema metadata
@@ -176,6 +176,6 @@ atomic operation. It skips empty generated descriptions, reports aggregate copie
counts, and retains the generated text. Because this can replace reviewed descriptions, the counts, and retains the generated text. Because this can replace reviewed descriptions, the
interface requires explicit confirmation before applying it. interface requires explicit confirmation before applying it.
The decisions behind this surface are [ADRs 0001–0011](../adr/0001-postgres-metadata-catalog.md) The decisions behind this surface are [ADRs 0001–0011](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/adr/0001-postgres-metadata-catalog.md)
and the detailed acceptance record is and the detailed acceptance record is
[AI catalog description generation acceptance](../testing/2026-08-29-ai-catalog-description-generation-acceptance.md). [AI catalog description generation acceptance](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/testing/2026-08-29-ai-catalog-description-generation-acceptance.md).
@@ -0,0 +1,98 @@
# Publishing the public manual
This is an internal operator runbook. Publishing documentation must not recreate
the ThothII application, Gitea, proxy, DWH or other documentation containers.
## Three separate states
1. `main` contains reviewed source and the public allowlist in `mkdocs.yml`.
2. Gitea Actions builds and validates the generated `pages` branch. A successful
Actions run alone does **not** update the live site.
3. The live [manual](https://git.tylconsulting.it/thothii-docs/) is served by the
dedicated `thothii-docs` nginx container on the host reached through the existing
trusted SSH alias `contabo`. Its Compose project/service are `thothii-docs`/`docs`.
This procedure is manual. No timer, webhook, credential or automatic deployment
has been added. Use the operator's existing SSH authorization and known host key;
never disable host verification to make a publication work.
## Build and stage
From a clean ThothII `main` checkout already pushed to Gitea:
```sh
git status --short
git rev-parse HEAD
./scripts/build-docs.sh
bash scripts/auth-docs-smoke.sh
bash scripts/verify-dwh-auth-docs.sh
bash scripts/test-auth-docs-smoke.sh
bash scripts/test-verify-dwh-auth-docs.sh
```
The strict build also verifies the public boundary: exactly 20 navigation pages,
five approved source assets, no internal pages/assets, and matching search entries.
The locked Windmill theme requires a separate exact allowlist of 25 static assets;
MkDocs applies `exclude_docs` to those files too. Never replace the list with broad
directory exceptions. Sources under `docs/` may not shadow theme asset paths.
The verifier follows stylesheet/script/image references and CSS font references
from generated pages and fails when a local dependency is absent.
Choose a unique release identifier consisting of UTC timestamp and source SHA.
Record the current symlink and container identity before proceeding:
```sh
ssh -oBatchMode=yes -oStrictHostKeyChecking=yes contabo \
'readlink /srv/thothii-docs/current; docker inspect --format "{{.Id}} {{.State.Health.Status}}" thothii-docs'
```
On the server, create `/srv/thothii-docs/releases/RELEASE/site` only if RELEASE
does not exist. Copy the current `nginx.conf` unchanged into the new release.
Transfer the local built `site/` into that new empty directory with rsync over
the same verified SSH connection. Do not use `--delete` against `current`, and do
not edit routing, TLS, network or authentication configuration.
## Activate only this site
Replace RELEASE below with the validated identifier, not a user-supplied path.
Keep the previously recorded release for rollback.
```sh
cd /srv/thothii-docs
test -f releases/RELEASE/site/index.html
test -f releases/RELEASE/nginx.conf
docker exec thothii-docs nginx -t
ln -s releases/RELEASE current.next
mv -Tf current.next current
docker compose --project-name thothii-docs -f docker-compose.yml \
up -d --no-deps --force-recreate docs
```
Check that `current.next` does not already exist before creating it; stop if it
does, because another publication or recovery may be in progress. Changing the
symlink alone is insufficient: an existing Docker bind mount still resolves to
the old release. The service recreation above remounts the new directory. It may
briefly interrupt this manual only.
## Verify, record, or roll back
- Wait for `docker inspect` to report this container healthy; verify mounted
paths and nginx configuration. Stop after a bounded timeout (for example 60 s).
- Without login/cookies, require HTTP 200 for home, search and both
`install/standalone-manual-it/` and `install/standalone-manual-en/`.
- Compare live home and `search/search_index.json` checksums to the build. Search
must contain only the 20 approved pages, never plans, reports or architecture.
- Check the actual stylesheet and JavaScript URLs in both installation pages:
HTTP 200, CSS served as `text/css`, JavaScript with a valid script content type,
and fonts available. In a browser confirm the stylesheets load and the layout is
styled. HTML 200 alone is not enough to accept a publication.
- Require HTTP 404 for retired/internal paths, including `architecture/overview/`,
`plans/2026-09-08-memory-management/`, and `operations/compose-reference/`.
- Record source SHA, release ID, previous release and checks in the cleanup/release
record. Do not call the publication complete solely because `pages` was pushed.
If health or public verification fails, point `current` back to the recorded
previous release using a fresh temporary symlink and the same atomic rename, then
recreate **only** service `docs` with the same Compose command. Verify health and
old site availability. Do not delete either release during recovery. Historical
releases are retained; pruning them requires a separate retention decision.
+6 -11
View File
@@ -118,12 +118,12 @@ other CPU workloads.
Run the first evaluation in shadow mode: read the source with its existing read-only role, do not Run the first evaluation in shadow mode: read the source with its existing read-only role, do not
save proposed flags, and report only aggregate counts, rule IDs, coverage, and timings. Never copy save proposed flags, and report only aggregate counts, rule IDs, coverage, and timings. Never copy
matched values into test output. Use a separately approved, labeled Italian corpus to calculate matched values into test output. Use a separately approved, labeled Italian corpus to calculate
precision and recall; raw PSD values must remain inside the authorized environment. precision and recall; source values must remain inside the authorized environment.
Inside the configured core runtime, the non-mutating command is: Inside the configured core runtime, the non-mutating command is:
```bash ```bash
npm run sensitivity:shadow -- psd-clinical npm run sensitivity:shadow -- <workspace-id>
``` ```
It reads catalog metadata and source values but emits one aggregate JSON object with no database, It reads catalog metadata and source values but emits one aggregate JSON object with no database,
@@ -139,12 +139,7 @@ Enabling NER by default requires all of these gates:
If a gate fails, leave NER disabled. The deterministic policy remains available and produces the If a gate fails, leave NER disabled. The deterministic policy remains available and produces the
binary draft from its scan coverage; no content is sent to an internal or external LLM. binary draft from its scan coverage; no content is sent to an internal or external LLM.
The first aggregate PSD shadow comparison is recorded in Benchmarks from a particular installation are not a guarantee for another database or machine.
[`2026-09-02-psd-sensitivity-shadow.md`](../reports/2026-09-02-psd-sensitivity-shadow.md). On the NER remains opt-in until an approved evaluation establishes that additional findings justify
local CPU runner, NER found additional entities but reduced total coverage under the superseded their false-positive rate and operational cost. Keep benchmark and release records with the
global deadline. The v2 benchmark removed that confounder: CPU NER added 18 sensitive proposals and installation's technical evidence, separate from this operator procedure.
increased the warm analysis time from 50.1 to 61.3 seconds. It remains opt-in until a labeled Italian
evaluation establishes that the additional findings justify their false-positive rate and cost.
The deterministic progressive PSD run is recorded in
[`2026-09-03-psd-progressive-sensitivity-shadow.md`](../reports/2026-09-03-psd-progressive-sensitivity-shadow.md):
both deterministic and CPU-NER profiles assessed all 2,275 columns with zero `unknown` decisions.
+16 -9
View File
@@ -1,6 +1,6 @@
# Consegna a Codex sul server: ThothII e Omics Portal # Consegna a Codex sul server: ThothII e Omics Portal
Revisione: **14 settembre 2026**. Destinazione: Datamart Builder nel portale Revisione: **26 settembre 2026**. Destinazione: Datamart Builder nel portale
Omics esistente, non un nuovo sito standalone. Questo documento è la procedura Omics esistente, non un nuovo sito standalone. Questo documento è la procedura
di riferimento per questa consegna e sostituisce le precedenti istruzioni di di riferimento per questa consegna e sostituisce le precedenti istruzioni di
trasporto/pubblicazione del codice Omics. La distribuzione parte dai sorgenti trasporto/pubblicazione del codice Omics. La distribuzione parte dai sorgenti
@@ -57,20 +57,27 @@ conservati in un percorso operativo stabile sul server.
### ThothII ### ThothII
Il checkout aggiornato deve essere su `main` e includere almeno Il checkout aggiornato deve essere sulla `main` di Gitea (`origin`) e includere almeno
`bdcd8fcd28f3011471d77224db9c3f5baf227995` e questo documento. Registra anche lo `0d2e573e` (correzione della vista sessione del 26 settembre) e questo documento.
SHA effettivo di `main`, che include il commit di consegna e il merge successivi: Il branch `codex/guided-standalone-install` contiene un processo di nuova installazione
ancora in lavorazione: non usarlo per questo aggiornamento e non eseguire
`tht setup --complete` sull'installazione server esistente. Registra lo SHA effettivo e
confrontalo con `origin/main` senza modificare il checkout operativo:
```bash ```bash
git fetch origin main
git status --short --branch git status --short --branch
git rev-parse HEAD git rev-parse HEAD
git merge-base --is-ancestor bdcd8fcd28f3011471d77224db9c3f5baf227995 HEAD git rev-parse origin/main
git rev-list --left-right --count HEAD...origin/main
git merge-base --is-ancestor 0d2e573e HEAD
``` ```
Se il controllo fallisce, completa l'acquisizione della revisione approvata La divergenza ideale è `0 0` e la working tree è pulita. Se il controllo dell'antenato
prima di toccare l'installazione. Non ricostruire a mano le singole modifiche UI: fallisce, o se il server ha commit o modifiche locali, prepara e verifica la revisione
questa revisione contiene shell, autenticazione, i18n, workflow bilingue, approvata prima di toccare l'installazione; non usare reset o force push. Non ricostruire
amministrazione, typography e navigazione aggiornate. a mano le singole modifiche UI: la revisione di `main` contiene shell, autenticazione,
i18n, workflow bilingue, amministrazione, typography e navigazione aggiornate.
### Omics Portal ### Omics Portal
@@ -722,6 +722,6 @@ la nuova installazione è ferma ma ispezionabile.
- [OIDC generico](../install/authentication-oidc.md) - [OIDC generico](../install/authentication-oidc.md)
- [Operazioni workspace](workspaces.md) - [Operazioni workspace](workspaces.md)
- [Configurazione dei modelli Pi](../general/pi-configuration.md) - [Configurazione dei modelli Pi](../general/pi-configuration.md)
- [Contesti Docker](../installazione-docker-4-contesti.md) - [Contesti Docker](compose-reference.md)
- [Database Management](database-management.md) - [Database Management](database-management.md)
- [Analisi locale della sensibilità](sensitivity-analysis.md) - [Analisi locale della sensibilità](sensitivity-analysis.md)
+10 -7
View File
@@ -184,19 +184,22 @@ Ci sono tre scelte distinte:
| Scelta | Dove viene conservata | Cosa influenza | | Scelta | Dove viene conservata | Cosa influenza |
| --- | --- | --- | | --- | --- | --- |
| Lingua UI | preferenza full o stato Omics | label, form, messaggi e controlli | | Lingua UI | preferenza full o stato Omics | navigazione, amministrazione e messaggi generali |
| Lingua di interazione | `interaction_language` nel manifest | nuove domande, spiegazioni e scelte del modello | | Lingua di interazione | `interaction_language` nel manifest | domande, spiegazioni, scelte e controlli HITL |
| Lingua workspace | configurazione del workspace | documenti, descrizioni e contenuti di dominio | | Lingua workspace | configurazione del workspace | documenti, descrizioni e contenuti di dominio |
La creazione web acquisisce la lingua UI prima delle operazioni asincrone e la La creazione web acquisisce la lingua UI prima delle operazioni asincrone e la
invia come `interactionLanguage`. Un cambio successivo non modifica quella invia come `interactionLanguage` di ripiego. Il CLI riconosce localmente la lingua
richiesta. La ripresa legge il manifest e non usa il locale del browser come della domanda originale e la fissa nel manifest; usa il ripiego per testo troppo
breve, ambiguo o composto soltanto da codice. Un cambio successivo non modifica
quella richiesta. Il gate trasmette la lingua nei widget: anche i controlli HITL
seguono la sessione, mentre la navigazione conserva la lingua UI.
La ripresa legge il manifest e non usa il locale del browser come
override. SQL, identificatori, valori e citazioni dei contenuti rimangono invariati. override. SQL, identificatori, valori e citazioni dei contenuti rimangono invariati.
Per sessioni precedenti senza `interaction_language`, la prima ripresa fissa la Per sessioni precedenti senza `interaction_language`, la prima ripresa fissa la
lingua del workspace in modo idempotente. Se il workspace era stato modificato lingua della domanda in modo idempotente, usando la lingua del workspace come
nel frattempo, non esiste una registrazione da cui ricostruire con certezza la ripiego. Le sessioni che hanno già una lingua fissata la conservano.
vecchia lingua: il criterio di compatibilità è quella disponibile alla ripresa.
Il cambio lingua di Omics ricarica la pagina. ThothII ricorda soltanto l'identificatore Il cambio lingua di Omics ricarica la pagina. ThothII ricorda soltanto l'identificatore
della sessione per utente e pagina, senza salvare una trascrizione nel browser. della sessione per utente e pagina, senza salvare una trascrizione nel browser.
+2 -2
View File
@@ -73,9 +73,9 @@ only that run's safe stage, error code, and finish time; use `docker compose log
corresponding service log. corresponding service log.
The contract gives exact validation, exit code, and JSON rules in The contract gives exact validation, exit code, and JSON rules in
[Workspace preprocessing CLI](../contracts/workspace-preprocessing-cli.md). For Evidence source [Workspace preprocessing CLI](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/workspace-preprocessing-cli.md). For Evidence source
forms and the schema-v4 descriptor contract, see forms and the schema-v4 descriptor contract, see
[Workspace Evidence v3](../contracts/workspace-evidence-v3.md). [Workspace Evidence v3](https://git.tylconsulting.it/mptyl/ThothII/src/branch/main/docs/contracts/workspace-evidence-v3.md).
## Transport and revision rules ## Transport and revision rules
@@ -1,695 +0,0 @@
# Metadata Catalog di ThothII: ricognizione ThothAI e percorso incrementale
Data: 2026-08-26; aggiornato 2026-08-27
Stato: ricognizione e progettazione completate; navigazione, CRUD Workspace Database, Catalog
Table, Catalog Column, Catalog Relationship e sincronizzazione durevole dello schema implementati
il 2026-08-27. Generazione AI e integrazione con il workflow core restano negli step successivi.
## Obiettivo
ThothII deve introdurre un contesto amministrativo separato, il **Metadata Catalog**, per gestire
il database associato a ciascun workspace, la sua struttura fisica introspezionata e i metadati
semantici oggi rappresentati da `schema/annotations.yaml`.
Il programma procede per step indipendenti. Il primo step ha aggiunto l'accesso dalla sidebar; il
secondo ha sostituito la superficie vuota con il CRUD di configurazione, il PostgreSQL interno e i
test di connessione; gli step successivi hanno aggiunto navigazione gerarchica, colonne, relazioni
fisiche e sincronizzazione durevole dell'intero schema. Non introduce ancora generazione AI o
integrazione con il workflow core.
Questa analisi usa come riferimento il working tree legacy osservato in
`Thoth/ThothAI`. Non è stato verificato che quel contenuto corrisponda a una release o a un tag
canonico; i percorsi e i comportamenti descrivono il sorgente disponibile il 2026-08-26.
## Decisioni già confermate
1. Ogni Workspace Database appartiene a un solo workspace tramite un `workspace_id` obbligatorio e
univoco; un workspace può avere al massimo un Workspace Database. Poiché i workspace non sono
righe del catalogo PostgreSQL, l'associazione è un riferimento logico validato contro
`thoth-workspaces.yaml`, non una foreign key SQL.
2. Il CRUD non crea né rinomina workspace. Identità e lista ordinata dei workspace restano
autorevoli in `thoth-workspaces.yaml`; il catalogo conserva il loro identificatore stabile.
3. La struttura fisica viene acquisita interrogando il database esterno tramite i dati di
connessione registrati per il Workspace Database.
4. I contenuti semantici equivalenti a `annotations.yaml` vengono generati con l'AI e conservati nel
PostgreSQL interno.
5. Per PSD è prevista l'importazione delle annotations esistenti. Gli altri database partiranno
dalla struttura introspezionata e genereranno i metadati semantici da zero.
6. `annotations.yaml` sarà sostituito anche come input del core in uno step futuro. Il repository è
in fase di test e non è richiesta la conservazione delle sessioni esistenti durante il cutover.
7. La gestione catalogo resta una superficie separata dal processo NL→SQL. La futura integrazione
deve essere esplicita e non deve modificare fasi, gate o semantica del workflow.
8. Il link iniziale è visibile agli utenti con `workspace.manage`, usa stato React locale e non
introduce un router.
9. La pagina iniziale è vuota, segue il tema, nasconde l'intera colonna core e non interrompe una
sessione live. Le azioni di apertura, resume o creazione sessione riportano al core.
10. La compatibilità con il modello ThothAI è semantica, non una copia letterale: configurazione e
contenuti semantici sono campi relazionali mutabili, mentre identità e appartenenza della
struttura fisica derivano dall'introspezione; i segreti restano nel secret store e lo stato dei
job non viene mescolato ai dati amministrativi.
11. Il CRUD amministra il Metadata Catalog e non esegue DDL sul database esterno, che resta
read-only.
12. La prima versione supporta PostgreSQL; il confine di introspezione dovrà permettere di
aggiungere altri dialetti senza cambiare il modello del catalogo.
13. I segreti dei Workspace Database riusano il secret store cifrato di ThothII. Il catalogo
conserva riferimenti ai segreti e nessuna API, esportazione o log ne restituisce i valori.
14. La UI usa AG Grid Community per la lista master e un pannello React separato per il dettaglio;
non dipende dalle funzionalità master-detail di AG Grid Enterprise.
15. Un Workspace Database il cui `workspace_id` scompare dal catalogo YAML non viene cancellato
automaticamente: diventa orphaned e può soltanto essere recuperato, riassegnato o eliminato
esplicitamente da un amministratore.
16. La prima vertical slice gestisce configurazione del Workspace Database, riferimenti ai segreti,
test di connessione e stato. La seconda gestisce le Catalog Table: la collezione e i nomi sono
controllati dall'introspezione, mentre la descrizione curata è modificabile. Le slice successive
hanno aggiunto Catalog Column, Catalog Relationship e sincronizzazione durevole dello schema.
17. Il modello non conserva il `name` libero di ThothAI: nome e ID visualizzati appartengono al
workspace YAML, mentre `database_name` identifica il database PostgreSQL esterno.
18. Database management supporta i tre trasporti già riconosciuti da ThothII: `postgres_direct`,
`rest_api` e `ssh_tunnel`. PSD rimane un solo Workspace Database: usa la connessione diretta sul
server e l'endpoint REST in locale tramite una Database Binding specifica dell'installazione.
Questo supporto non abilita automaticamente `ssh_tunnel` nel runtime NL→SQL.
19. Una configurazione può essere salvata prima di una connessione riuscita. Il test separato
produce uno stato `untested`, `reachable` o `failed`; attivazione e introspezione richiedono uno
stato raggiungibile.
20. Il CRUD e il test di connessione richiedono `database.manage`; inserimento e sostituzione dei
segreti continuano a richiedere `workspace.secrets.manage`.
21. Il Workspace Database e il modo di raggiungerlo sono entità distinte. Ogni catalogo di
installazione conserva una sola Database Binding attiva per workspace: PSD usa `rest_api` in
locale e `postgres_direct` sul server senza duplicare il Workspace Database.
22. Nel modello finale il Metadata Catalog è autorevole per engine, `database_name`, schema,
capacità e binding. Lo YAML resta autorevole per identità e contenuti del workspace; i campi
DWH correnti saranno importati, confrontati e rimossi soltanto durante un cutover esplicito.
23. La lista master è l'unione fra workspace YAML e record del catalogo: mostra workspace
`unconfigured`, database configurati e record `orphaned`.
24. Ogni introspezione registra le capability disponibili. Una capability `unavailable` non viene
rappresentata come una collezione osservata ma vuota; REST può completare con successo anche
quando indici o enum non sono supportati.
25. Il Metadata Catalog non introduce snapshot, draft o pubblicazioni. Configurazione e contenuti
semantici, inclusi quelli futuri generati dall'AI, sono normali campi modificabili; la struttura
osservata cambia soltanto con una sincronizzazione esplicita.
26. Il normale Delete elimina realmente il Workspace Database, la Database Binding e i relativi
record catalogo e segreti. Non modifica il DWH esterno né il repository YAML; il workspace torna
visibile nella lista master come `unconfigured`.
27. La prima versione gestisce un solo schema obbligatorio per Workspace Database, identificato
dalla coppia `database_name + schema`; per PSD la coppia è `postgres + datawarehouse`.
28. I record mantengono soltanto `created_at`, `updated_at` e un contatore `version` per optimistic
concurrency. Non esistono storico delle revisioni, rollback o audit applicativo delle modifiche.
29. `workspace_databases` conserva soltanto UUID, `workspace_id` unique, engine, `database_name`,
schema, timestamp e version. Il nome visualizzato appartiene al workspace YAML.
30. Ogni Workspace Database ha al massimo una riga `database_bindings`. Una singola tabella usa
check constraint dipendenti da `transport` per i campi direct, REST e SSH; non esiste un flag
`active`, perché ciascuna installazione conserva una sola binding.
31. `rest_api` configura il Thoth REST Connector tipizzato: base URL, autenticazione e TLS sono dati
della binding, mentre path RPC e shape delle risposte appartengono al contratto applicativo e non
sono liberamente configurabili.
32. Il test connessione usa soltanto una configurazione già salvata ed è associato alla sua
`version`. Ogni modifica della binding o dei segreti invalida il risultato precedente e riporta
lo stato a `untested`.
33. Password, API key e chiavi sono write-only: l'API espone soltanto `configured`, un campo vuoto
conserva il valore esistente e la sostituzione è un'azione esplicita. Delete rimuove anche i
segreti associati.
34. La pagina usa AG Grid come master e un form React come detail, con sezioni Database, Connection
e TLS/SSH condizionali. Non esiste un'azione globale `Add database`: ogni riga `unconfigured`
offre `Configure catalog`, apre il form già vincolato a quello specifico workspace YAML e crea il
record soltanto al Save; `workspace_id` non è selezionabile né modificabile.
35. La grid mostra separatamente revisione/Evidence del workspace, binding runtime NL→SQL e
configurazione del Metadata Catalog, oltre a database, schema, endpoint e ultimo aggiornamento.
Su schermi piccoli il dettaglio occupa il pannello completo. Il cambio riga con modifiche non
salvate e Delete richiedono conferma, senza conferma testuale tipizzata.
36. La Database Binding conserva `connection_status`, `tested_version`, `last_tested_at`, un codice
errore e un messaggio breve sanificato. Non conserva stack trace, DSN, credenziali o output grezzo
del driver.
37. Le API vivono sotto `/api/catalog`: list/create di `/databases`, get/patch/delete di
`/databases/:id`, sostituzione dei segreti sotto `/databases/:id/secrets`, test connessione sotto
`/databases/:id/test` e list/patch/sync delle tabelle sotto `/databases/:id/tables`.
38. `GET /api/catalog/databases` restituisce l'intera master list unificata; AG Grid Community applica
client-side ricerca, filtri e ordinamento. La prima versione non introduce paginazione server o
funzionalità AG Grid Enterprise.
39. Il Metadata Catalog vive nello stesso processo Fastify come modulo isolato con repository,
service, route, diagnostica e readiness proprie. L'indisponibilità del catalogo non modifica
sessioni, SSE o health del core e non giustifica ancora un microservizio separato.
40. Il backend mantiene `pg@8.22.0` e aggiunge `kysely@0.29.5` per query e transazioni tipizzate. Le
migrazioni Kysely sono timestampate, compilate con il backend ed eseguite da un comando
`catalog:migrate` separato; l'applicazione non migra automaticamente il database all'avvio.
41. Lo stack aggiunge un servizio interno `catalog-db` con volume persistente, ruolo runtime DML,
ruolo migrator DDL e job one-shot `catalog-migrate`. Un catalogo indisponibile produce 503 sulle
sole route catalogo.
42. La prima vertical slice è amministrativa: scrive il catalogo ma non cambia ancora il runtime di
sessioni e workflow, che continua a usare YAML e binding correnti fino al cutover esplicito.
43. `Configure` precompila senza salvare engine, database e schema dal descriptor e i dati non
sensibili dalla binding effettiva. L'amministratore verifica, inserisce i segreti e salva; non
esiste importazione silenziosa.
44. Unit e route test usano un repository fake; una suite PostgreSQL Testcontainers separata verifica
migrazioni, constraint, transazioni, optimistic concurrency e cascade. SQLite ed emulatori non
sono sostituti ammessi per questi test.
45. La navigazione delle entità catalogo è gerarchica e senza scorciatoie globali: `Databases →
Database → Overview | Tables → Table`. Non esistono una voce globale Tables, un filtro globale
Database o una preselezione implicita; Columns continuerà sotto Table e Relationships sotto
Database.
46. Una Catalog Table conserva nome fisico, `source_comment`, descrizione curata nullable,
`generated_description` nullable per lo step AI futuro, version e timestamp. La UI mostra come
tre campi indipendenti senza fallback visivo: source comment read-only, generated description
modificabile e description modificabile. I valori null restano celle e controlli vuoti.
47. Le Catalog Table non possono essere aggiunte o rinominate manualmente. Un amministratore può
però ripulire esplicitamente le proiezioni nel Metadata Catalog senza modificare il database
esterno; `Sync tables` legge le tabelle PostgreSQL ordinarie e partizionate dello schema scelto,
mentre viste e materialized view sono escluse.
48. La sincronizzazione è esplicita. La scansione avviene fuori dalla transazione del catalogo; il
diff viene applicato atomicamente soltanto se la version del Workspace Database è ancora quella
sottoposta a scansione. Una scansione fallita non modifica il catalogo.
49. Tabelle nuove vengono create, i commenti sorgente vengono aggiornati e quelle non più osservate
vengono eliminate definitivamente. La rimozione di tabelle, colonne o relazioni richiede la
conferma dell'esatto piano distruttivo; se il secondo scan produce una fotografia differente,
l'applicazione richiede una nuova conferma.
50. Un rename fisico è intenzionalmente delete più create e perde i metadati curati. Le colonne e
relazioni dipendenti vengono eliminate in cascade insieme alla Catalog Table.
51. L'introspezione vive nel modulo catalogo Fastify dietro un adapter. PostgreSQL diretto e tunnel
SSH usano il catalogo `pg_catalog`; REST preferisce il contratto tipizzato
`POST /rpc/schema_snapshot` e, quando quell'RPC non è esposto, usa come fallback compatibile una
singola query read-only tramite `POST /rpc/run_query`. Entrambi i percorsi devono produrre la
stessa fotografia v1 stretta descritta in `docs/contracts/catalog-schema-snapshot.md`.
52. Test connessione e sincronizzazione sono serializzati per Workspace Database, hanno timeout e
richiedono che la binding nella version corrente abbia un test `reachable` prima di qualsiasi
Catalog Sync Run. La scansione asincrona ha un timeout separato, di default dieci minuti.
53. Il tunnel SSH usa OpenSSH in modalità stdio `-W`, chiave privata e passphrase opzionale dal
secret store, `known_hosts` obbligatorio, `StrictHostKeyChecking=yes`, agent e configurazione
globale disabilitati. Non è ammesso TOFU. TLS PostgreSQL con CA e server name resta verificato
anche attraverso il tunnel.
54. In questo slice `ssh_tunnel` è una binding supportata da Database management per Test connection
e Schema Sync. Il renderer e il runtime delle sessioni NL→SQL restano fuori scope e continuano a
rifiutarla finché non verrà deciso il relativo cutover.
55. I menu di azione a livello Workspace Database espongono separatamente `Synchronize tables`,
`Synchronize relationships` e `Synchronize all`. Su una selezione di
database lo scope scelto viene avviato per ogni database idoneo; non viene sostituito
implicitamente con una sincronizzazione completa.
56. Lo scope Columns è disponibile dalla grid Tables e limita la riconciliazione alle tabelle
selezionate; la pagina Columns non espone azioni di sincronizzazione. La grid Tables espone
`Synchronize columns` sulle tabelle selezionate.
## Correzione del modello mentale corrente
`schema/annotations.yaml` non contiene l'intero schema del database.
- `physical.yaml` è un artefatto derivato dall'introspezione. Contiene database, schema, timestamp,
tabelle, colonne, tipi, nullability, default, primary key, commenti sorgente, esempi, foreign key
fisiche e indici.
- `annotations.yaml` contiene metadati curati: descrizioni e concetti delle tabelle; descrizioni,
sinonimi, concetti, evidence, note e override `eligible` delle colonne; foreign key logiche.
- Il rendering M-Schema fonde questi due input. Le annotations prevalgono sui commenti sorgente e
le relazioni logiche vengono unite alle foreign key fisiche.
La sostituzione del solo file annotations non elimina automaticamente l'introspezione fisica. Il
nuovo catalogo dovrà conservare una distinzione esplicita fra fatti osservati nel database e
contenuto semantico modificabile.
## Architettura ThothII rilevante
### Autorità e revisionamento attuali
Il repository dei workspace contiene:
```text
thoth-workspaces.yaml
<workspace-id>/workspace.yaml
<workspace-id>/schema/annotations.yaml
<workspace-id>/evidence/**
```
Il backend legge descriptor e annotations allo stesso commit Git. Durante l'attivazione valida il
blob, lo copia atomicamente nello snapshot immutabile della revisione e registra commit, blob ID e
digest. Le nuove sessioni vengono legate a quella revisione; resume e SQL salvato riaprono lo stesso
snapshot.
Punti principali:
- `backend/src/workspaces/schema.ts`: descriptor v3 e singolo `dwh.database`/`dwh.schema`;
- `backend/src/workspaces/git-repository.ts`: lettura sicura del blob annotations al commit;
- `backend/src/workspaces/registry.ts`: validazione e attivazione atomica;
- `backend/src/workspaces/annotations-sync.ts`: materializzazione revision-qualified;
- `backend/src/workspaces/runtime-config-lease.ts`: binding dello snapshot al runtime;
- `harness/tht/mschema/models.py`: contratti `PhysicalSchema` e `Annotations`;
- `harness/tht/mschema/render.py`: fusione fisico/semantico;
- `harness/tht/cli/vector_cmd.py`: indicizzazione schema in Qdrant.
### Consumatori da preservare al cutover futuro
Le annotations incidono oggi su:
- override `eligible` prima del campionamento LSH;
- suggerimento, controllo e accettazione delle foreign key logiche;
- descrizioni, concetti e sinonimi dei record schema in Qdrant;
- retrieval delle tabelle e colonne candidate;
- rendering M-Schema usato dal gate F4 e dalla generazione SQL;
- digest della revisione accettata durante il preprocessing.
Il futuro cutover non potrà limitarsi a rimuovere il file: dovrà fornire al core lo stesso contenuto
effettivo, con un'identità coerente e test di equivalenza. Poiché non occorre preservare le sessioni
di test esistenti, non serve progettare compatibilità con i vecchi manifest, ma resta necessario
evitare letture parziali o semanticamente incoerenti.
## Inventario ThothAI
### Modelli legacy
I modelli sono definiti in `Thoth/ThothAI/backend/thoth_core/models.py`.
#### `SqlDb`
Campi di connessione osservati:
- `name`;
- `db_host`, `db_port`;
- `db_type`;
- `db_name`, `schema`;
- `user_name`, `password`;
- `db_mode`;
- configurazione SSH e Informix opzionale.
Il modello contiene anche scope, JSON dello scope, ERD, direttive, campi GDPR, collegamento a
`VectorDb` e numerosi campi di stato/task/log per lavori AI asincroni.
I tipi legacy dichiarati sono Informix, MariaDB, MySQL, Oracle, PostgreSQL, SQL Server e SQLite.
Questo elenco non costituisce automaticamente un requisito per ThothII: il core corrente supporta
PostgreSQL e l'estensione ad altri dialetti dovrà essere decisa separatamente.
#### `SqlTable`
- `name`;
- `description`;
- `generated_comment`;
- foreign key obbligatoria a `SqlDb`, con cancellazione cascade.
#### `SqlColumn`
- `original_column_name` e alias `column_name`;
- `data_format` normalizzato;
- `column_description`;
- `generated_comment`;
- `value_description`;
- stringhe denormalizzate `pk_field` e `fk_field`;
- foreign key obbligatoria a `SqlTable`, con cancellazione cascade.
#### `Relationship`
Contiene quattro foreign key obbligatorie:
- `source_table` e `source_column`;
- `target_table` e `target_column`.
Il form admin verifica che le tabelle appartengano allo stesso database e che ogni colonna
appartenga alla tabella selezionata. Il database non impone però gli stessi check.
#### `Workspace`
ThothAI usa `Workspace.sql_db` come foreign key nullable verso `SqlDb`: un workspace seleziona un
solo DB, mentre lo stesso DB può essere riusato da più workspace. ThothII adotterà invece una
relazione uno-a-uno: `workspace_id` deve essere unico nel catalogo.
### Lacune dei constraint legacy
Non risultano constraint database-level per:
- unicità del nome database nel workspace;
- unicità `(database, table name)`;
- unicità `(table, column name)`;
- unicità degli estremi di una relationship;
- appartenenza degli estremi della relationship allo stesso database;
- corrispondenza fra colonna e tabella dichiarata.
ThothII deve applicare queste invarianti sia nel database interno sia nel servizio applicativo. La
sola validazione del form non è sufficiente perché API, import e job la possono aggirare.
### Django Admin e UX da replicare concettualmente
ThothAI espone il CRUD tramite il Django Admin standard, registrato da
`backend/thoth_core/admin.py` e pubblicato su `/admin/`.
Capacità utili:
- lista database con ricerca per nome, host, tipo, database e schema;
- fieldset separati per identità, connessione, autenticazione, SSH e stato;
- lista tabelle filtrabile per database;
- lista colonne filtrabile in cascata per database e tabella;
- lista relazioni con estremi leggibili e filtri per database e tabelle;
- form relazione con dropdown dipendenti database → tabella → colonna;
- validazione degli estremi prima del salvataggio;
- azioni separate per test connessione, introspezione, import/export e generazione AI;
- azioni bulk sulle righe selezionate.
ThothII deve replicare i contratti di interazione e validazione, non il rendering server-side o i
template Django.
### Introspezione legacy
`Thoth/ThothAI/backend/thoth_core/dbmanagement.py` usa `thoth-dbmanager` per:
1. costruire l'adapter del dialetto;
2. acquisire tabelle;
3. acquisire e normalizzare colonne e tipi;
4. acquisire relazioni;
5. creare le eventuali colonne mancanti necessarie alle relazioni;
6. aggiornare i campi PK/FK denormalizzati.
Il comportamento è principalmente additivo: usa `get_or_create` o controlli `exists`, aggiorna
alcuni commenti, ma non riconcilia in modo completo rename, rimozioni o drift. Non va copiato così
com'è. Il processo ThothII implementato distingue scansione, differenze osservate e applicazione
della nuova snapshot.
### Generazione AI legacy
ThothAI dispone di azioni e workflow per:
- commenti delle tabelle;
- commenti delle colonne;
- scope del database;
- ERD Mermaid;
- documentazione del database;
- analisi GDPR.
Per il requisito attuale sono direttamente rilevanti descrizioni di tabelle e colonne, scope e
metadati semantici. ERD, documentazione aggregata e GDPR sono estensioni future, non prerequisiti
del CRUD iniziale.
La separazione `description`/`generated_comment` del legacy non offre versioning o approvazione
robusti. Nei passi successivi andrà deciso se l'output AI è una proposta revisionabile o diventa
immediatamente il valore editabile corrente.
### Import ed export legacy
ThothAI offre:
- CSV di database, tabelle, colonne e relazioni;
- export di struttura per workspace;
- import mediante `import_db_structure`;
- script SQL dei commenti per più dialetti;
- aggiornamento delle descrizioni colonna da CSV.
Il futuro import PSD dovrà leggere il contratto YAML corrente e convertirlo su chiavi naturali,
non riutilizzare gli ID numerici Django. Deve essere idempotente e produrre un report di elementi
creati, aggiornati, ignorati o non risolti.
## Comandi osservati in ThothAI
### Backend locale
Eseguiti da `Thoth/ThothAI/backend`:
```sh
uv sync
uv run python manage.py migrate
uv run python manage.py createsuperuser
uv run python manage.py runserver 8200
uv run pytest
```
Import catalogo legacy:
```sh
uv run python manage.py import_db_structure --source local
uv run python manage.py load_defaults --only-level 4 --source local
```
Test mirati rilevanti:
```sh
uv run pytest tests/test_relational_database_operations.py -v
uv run pytest tests/test_ssh_tunnel_configuration.py -v
```
### Stack Docker legacy
ThothAI dichiara `postgres:16-alpine` nel profilo `internal-db`, con volume persistente e
healthcheck `pg_isready`.
```sh
docker compose --profile internal-db up --build
```
Il wrapper legacy abilita lo stesso profilo quando `POSTGRES_INTERNAL=true`:
```sh
POSTGRES_INTERNAL=true ./docker-up.sh
```
Questi comandi documentano il riferimento osservato; non sono comandi di installazione per
ThothII.
## Cosa copiare in ThothII
### Parità necessaria
- gerarchia Workspace Database → Table → Column;
- relazione strutturale fra colonne sorgente e destinazione;
- navigazione e filtri dipendenti workspace/database/tabella;
- test di connessione separato dal salvataggio;
- introspezione esplicita e ripetibile;
- descrizioni generate dall'AI ma modificabili dall'utente;
- validazione cross-entity delle relazioni;
- azioni di import/export senza segreti;
- stato leggibile dei job lunghi;
- PostgreSQL interno persistente con migrazioni esplicite;
- test di CRUD, cardinalità, cascade/restrict, isolamento per workspace e idempotenza.
### Parità semantica con `annotations.yaml`
Il modello futuro deve poter rappresentare almeno:
- descrizione, concetti e note per tabella;
- descrizione, sinonimi, concetti, evidence, note ed `eligible` per colonna;
- foreign key logiche;
- distinzione fra commento fisico osservato e descrizione curata;
- provenienza del contenuto importato o generato.
L'eventuale esclusione di uno di questi campi deve essere una decisione esplicita perché cambia
rendering, retrieval, LSH o SQL generation.
### Vincoli minimi da progettare
- `workspace_id` obbligatorio e unico sul Workspace Database, con esistenza validata contro il
catalogo YAML dal servizio applicativo;
- nome tabella unico nel database e schema appropriato;
- nome colonna unico nella tabella;
- relationship unica secondo il modello, anche per chiavi composite;
- estremi della relationship nello stesso Workspace Database;
- appartenenza certa della colonna alla tabella;
- mutazioni aggregate transazionali;
- gestione esplicita di concorrenza fra CRUD e introspezione.
## Cosa non copiare
- Django, Django Admin, Django ORM, DRF, template admin e frontend Next;
- modello Workspace legacy e condivisione dello stesso DB fra più workspace;
- password o passphrase come normali campi testuali;
- password incluse in CSV o export completi;
- token SSO inseriti nella query string;
- migrazioni generate automaticamente all'avvio;
- validazioni presenti soltanto nel form;
- `pk_field` e `fk_field` testuali come fonte di verità;
- duplicazione di tabella e colonna negli estremi senza constraint coerenti;
- introspezione additiva che non segnala rename, delete o drift;
- azioni admin che possono mostrare successo dopo output AI non valido;
- dipendenza del workflow core dalla disponibilità della UI o del PostgreSQL amministrativo.
## Aspetti di sicurezza da non ereditare
L'export legacy della struttura include username e password in chiaro. Il modello conserva inoltre
password, passphrase SSH e altri segreti in `CharField`; non è stata trovata cifratura applicativa,
nonostante un testo admin affermi il contrario.
ThothII distingue i metadati di connessione dai riferimenti al secret store cifrato. In ogni caso:
- nessun endpoint o export deve restituire segreti;
- log ed errori devono sanificare DSN e credenziali;
- le credenziali di migrazione non devono essere disponibili al runtime CRUD;
- il catalogo non deve riusare credenziali del DWH, delle sessioni o di Qdrant;
- test connessione e introspezione devono usare timeout e privilegi read-only.
La binding REST corrente richiede una verifica prima del cutover: il renderer emette
`ssl_ca_file`, mentre il modello Python espone `ssl_ca`; il percorso della CA privata potrebbe quindi
non essere consumato. PSD richiede TLS con CA privata in locale, perciò questo disallineamento deve
essere corretto e coperto da un test end-to-end prima di affidare il profilo REST al catalogo.
## Percorso incrementale
### Step 1: accesso alla superficie vuota
Implementato in questo worktree:
- pulsante `Database management` nella sidebar destra;
- visibilità legata a `workspace.manage`;
- superficie centrale React separata e vuota;
- nessun router, endpoint, fetch o stato catalogo;
- sessione e SSE conservati in background;
- ritorno al core tramite creazione, apertura o resume di una sessione;
- test frontend dedicati.
Comandi di verifica:
```sh
cd frontend
npx vitest run src/shell/AppShell.database-management.test.tsx
npx vitest run src/shell/AppShell.new-session.test.tsx \
src/shell/AppShell.session-target.test.tsx \
src/shell/AppShell.session-mgmt.test.tsx
npx tsc -b
```
### Step 2: contratto di dominio e schema relazionale
Progettazione della vertical slice completata: Workspace Database, Database Binding, singolo schema,
riferimenti al secret store, optimistic concurrency e capability per trasporto hanno contratti
espliciti. Configurazione e contenuti semantici restano mutabili; la struttura fisica osservata è
sincronizzata e non modificabile manualmente.
### Step 3: PostgreSQL interno e migrazioni
PostgreSQL interno con volume e ruoli runtime/migrator separati. Il modulo catalogo usa Kysely sopra
il driver `pg`; le migrazioni compilate vengono applicate soltanto dal comando `catalog:migrate` e
mai allo startup Fastify. Health, readiness e diagnostica restano dedicate; l'indisponibilità del
catalogo non cambia `core /health` e non interrompe una sessione.
### Step 4: API CRUD
Contratti HTTP, autorizzazione, paginazione, filtri, errori, optimistic concurrency e transazioni.
Gli endpoint dovranno vivere sotto un namespace catalogo e non riutilizzare le route sessione.
### Step 5: UI CRUD
Workspace Database, Catalog Table, Catalog Column e Catalog Relationship sono implementati con
React/Vite e il design system ThothII.
La navigazione è gerarchica e locale al database (`Overview | Tables`), senza menu o filtri globali
per tipo di entità. La grid delle tabelle non offre Add o cancellazione della singola configurazione;
le selezioni espongono invece la pulizia esplicita dei metadati. Il dettaglio full-width mantiene
immutabili i fatti fisici e consente di modificare separatamente Description e Generated
Description. Colonne e relazioni seguono la stessa gerarchia: Columns appartiene al dettaglio
della tabella, Relationships al database. I valori descrittivi null sono mostrati come celle e
campi vuoti, senza fallback visivi o placeholder `Not set` che nascondano quale sorgente è
effettivamente valorizzata.
Le griglie che dispongono di azioni massive usano checkbox e una toolbar contestuale con conteggio,
menu `Actions` e cancellazione della selezione. La selezione identifica ID espliciti, può essere
accumulata attraverso i filtri e viene azzerata dopo successo, nuova sincronizzazione o uscita
dalla pagina; un'azione è all-or-nothing se un elemento non è idoneo. I menu a livello database
espongono gli scope fisici come azioni distinte: `Synchronize tables`, `Synchronize relationships`
e `Synchronize all`. La grid Tables espone invece `Synchronize columns` per le tabelle selezionate;
la pagina Columns non espone sincronizzazione. Le selezioni database aggiungono `Delete all tables` e
`Delete all relationships`; le selezioni tabelle aggiungono `Delete all columns` e `Delete all
relationships`. Queste operazioni sono atomiche, richiedono conferma e non modificano database
esterno, binding, configurazione o segreti. Test connection resta un'azione distinta; griglie senza
azioni non mostrano controlli di selezione inerti.
### Step 6: introspezione
Catalog Table, Catalog Column e Catalog Relationship sono implementate per PostgreSQL diretto,
Thoth REST Connector e tunnel SSH. La scansione read-only è separata dalla transazione; una
riconciliazione atomica crea, aggiorna i commenti sorgente ed elimina, dopo conferma, i fatti fisici
assenti senza rendere modificabile manualmente la struttura osservata. Gli scope autorevoli sono
Tables per database e Physical Relationships per database. Per Columns, `tableIds` vuoto include
tutte le Catalog Table correnti, mentre una lista di ID limita lo scope al sottoinsieme esplicito;
`Synchronize all` osserva tutti e tre gli scope in un unico snapshot e li riconcilia insieme. Tutti
gli scope sono eseguiti come Catalog Sync Run durevoli in background, non attraverso implementazioni
sincrone e asincrone separate. Un run che prevede cancellazioni conserva il diff, attende una
conferma esplicita e verifica nuovamente lo snapshot prima dell'applicazione; se la sorgente è
cambiata, invalida la conferma. Ogni applicazione è atomica e fail-closed: errori, timeout o
capability non disponibili non producono aggiornamenti parziali.
PK e FK devono essere visibili sulle Catalog Column senza duplicare le stringhe denormalizzate di
ThothAI. La posizione nella primary key è un fatto osservato della colonna; membership e conteggio
FK sono proiezioni derivate dalle Catalog Relationship e dalle loro coppie ordinate, aggiornate
nella stessa transazione di riconciliazione.
Ogni scope registra la versione della Database Binding osservata e l'istante dell'ultima
sincronizzazione. Una modifica della binding conserva il catalogo precedente ma lo marca stale;
solo un `Synchronize all` riuscito rende nuovamente corrente l'intero schema.
### Step 7: generazione AI dei metadati
Generated Description è una proposta distinta e modificabile: un revisore può correggerla prima
di consolidarla esplicitamente come Description. Lo slice AI dovrà decidere e implementare anche
alias semantici, descrizioni dei valori, sinonimi e concetti per tabelle e colonne, oltre alla
gestione esplicita di errori e output non validi. La generazione AI e l'azione di consolidamento non
appartengono allo slice di introspezione dello schema.
### Step 8: migrazione PSD
Import idempotente delle annotations PSD, riconciliazione contro la struttura introspezionata,
report degli orfani e confronto semantico con il rendering corrente. Gli altri workspace non
ricevono import legacy.
### Step 9: sostituzione dell'input core
Rimuovere la dipendenza da `annotations.yaml` soltanto dopo avere un contratto equivalente,
test di rendering/search/Qdrant e una policy di disponibilità. Le sessioni di test esistenti
possono essere eliminate, ma le nuove sessioni non devono osservare aggiornamenti parziali.
Questo cutover è esplicitamente rinviato fino al completamento del database dei metadati. Il primo
gate successivo obbligatorio sarà valutare l'integrazione del Catalog Schema Snapshot con il
workflow core e lo schema-linking corrente; il rinvio non autorizza a dimenticare o assorbire
implicitamente il lavoro in altri slice.
### Step 10: operazioni e accettazione
Backup/restore reale, diagnostica, metriche, permessi definitivi, hardening degli export e
test di failure isolation fra catalogo e workflow. I Catalog Sync Run hanno un solo job attivo per
Workspace Database, sono concorrenti fra database diversi e usano un lock persistente. Un pannello
operativo non modale rimane visibile durante la navigazione del database, mostra fasi, contatori,
tempo trascorso e log sanitizzato via SSE con polling di fallback, e offre Confirm, Cancel e Retry
quando consentiti. Un restart marca `interrupted` i run rimasti attivi; il retry crea un nuovo run.
Le modifiche ai metadati restano consentite durante la scansione e sono preservate dall'applicazione.
Il worker gira inizialmente nello stesso servizio Fastify ma dietro un'interfaccia estraibile, con
coda, lease e heartbeat persistiti nel catalog-db. I riepiloghi dei run non scadono; gli eventi
dettagliati sono conservati per 30 giorni, mentre snapshot e diff completi vengono eliminati dopo
la conclusione lasciando conteggi, decisioni e una sintesi sanitizzata dell'esito.
## Verifiche del core da conservare per il cutover
Comandi attuali rilevanti:
```sh
tht --installation <absolute>/thothii-installation.yaml workspace preprocess dwh \
--workspace <id> --json
tht --installation <absolute>/thothii-installation.yaml workspace schema suggest-fks \
--workspace <id> --json
tht --installation <absolute>/thothii-installation.yaml workspace schema check \
--workspace <id> --json
tht --installation <absolute>/thothii-installation.yaml workspace schema accept \
--workspace <id> --run <run-id> --yes --json
tht --installation <absolute>/thothii-installation.yaml workspace index-schema \
--workspace <id> --json
```
Suite che documentano il comportamento da preservare:
```sh
cd backend
npx vitest run test/workspaces-git-annotations.test.ts \
test/registry-annotations.test.ts \
test/annotations-sync.test.ts \
test/workspace-runtime-config-lease.test.ts \
test/workspace-preprocessing-service.test.ts
npx tsc --noEmit -p .
cd ../harness
.venv/bin/pytest -q \
tests/test_annotations_root.py \
tests/test_schema_fk_annotations.py \
tests/test_mschema_render.py \
tests/test_qdrant_cli_commands.py
```
Questi test non implicano che la futura implementazione debba continuare a usare file YAML.
Definiscono gli effetti semantici e le guardie da mantenere o sostituire consapevolmente.
## Decisioni rinviate
Le seguenti scelte non appartengono allo step 1:
- lifecycle dei riferimenti ai segreti durante sostituzione e cancellazione;
- criteri per aggiungere dialetti successivi a PostgreSQL;
- criteri per un'eventuale estensione futura a più schemi per database;
- lifecycle e gestione amministrativa delle future Logical Relationship;
- alias semantici, descrizioni dei valori, sinonimi e concetti prodotti o assistiti dall'AI;
- formato e momento del cutover dal file al database interno;
- permission definitiva separata da `workspace.manage`.
Ognuna sarà affrontata nel relativo step, senza anticipare scelte tecnologiche nel presente
documento.
@@ -1,251 +0,0 @@
# AI-generated descriptions for Catalog Tables and Catalog Columns
## Problem Statement
ThothII already stores a Generated Description separately from the curated Description for Catalog
Tables and Catalog Columns, but administrators cannot populate it with AI. ThothAI provides the
useful core workflow—generate table and column comments from schema context and small real-data
samples—but its execution, configuration, and interaction model cannot be copied directly into
ThothII.
Administrators need an asynchronous workflow integrated into Database Management. They must be
able to choose an installation-approved model, generate descriptions for selected or missing
targets, observe understandable progress, stop or recover a stuck operation, review generated
text, and explicitly consolidate it. The solution must retain ThothAI's practical simplicity and
must not introduce a general job platform, model gateway, distributed scheduler, or competing
user-facing CLI.
## Solution
Add Description Generation to Database Management as one installation-wide, sequential background
run owned by the Fastify backend. The browser starts a run and remains responsive while the backend
processes bounded requests one at a time. Each completion is delegated to a short-lived internal
Python helper using LiteLLM. Models, their default, and any API-key secret references are declared in
application setup YAML and are independent of both workspaces and Pi configuration.
Each valid result is written immediately to the target's Generated Description. A minimal run row
and ordered text events provide status, counters, history, and a live log. A stopped or crashed run
is not resumed automatically; completed results remain in place and Generate Missing supplies the
simple recovery path. An Unlock action marks a stale recorded run interrupted only when no helper
or backend generation loop is alive.
Prompts use catalog context and, when available, no more than five real rows and five representative
non-null examples. Samples are transient and never logged or persisted. A valid inability to infer
a description produces a standard application-localized value such as `Non generabile`; provider,
timeout, and response-validation failures remain technical errors.
Generated text remains separate from Description until an administrator uses the existing
checkbox selection and Actions control to consolidate it. Consolidation retains Generated
Description and never writes comments to the external Workspace Database.
## User Stories
1. As an installation operator, I want to declare the models allowed for metadata generation in setup YAML, so that model availability is controlled centrally.
2. As an installation operator, I want to declare one default metadata-generation model, so that administrators begin with a safe operational choice.
3. As an installation operator, I want each model to reference its own API-key secret, so that credentials are not stored in workspaces or browser-visible settings.
4. As an installation operator, I want metadata-generation models to remain independent of Pi models, so that changing this workflow cannot disrupt the core NL-to-SQL experience.
5. As an installation operator, I want invalid model setup to fail validation clearly, so that the application does not start with ambiguous provider behavior.
6. As a Catalog Administrator, I want generation controls to explain when no model is configured, so that I know why the action is unavailable.
7. As a Catalog Administrator, I want to select an approved model from a selector initialized to the setup default, so that I control which model performs the work.
8. As a Catalog Administrator, I want to generate descriptions for selected Catalog Tables, so that I can work on a focused part of the catalog.
9. As a Catalog Administrator, I want to generate descriptions for selected Catalog Columns, so that I can work on individual fields without regenerating a whole table.
10. As a Catalog Administrator, I want to generate all eligible descriptions for a Workspace Database, so that I can initialize a catalog in one operation.
11. As a Catalog Administrator, I want to generate only missing descriptions, so that I can continue interrupted work without replacing completed proposals.
12. As a Catalog Administrator, I want a full run to process Catalog Columns before their Catalog Tables, so that table descriptions can benefit from column descriptions.
13. As a Catalog Administrator, I want generation to run asynchronously after I start it, so that the browser remains usable and progress is not tied to one HTTP request.
14. As a Catalog Administrator, I want only one Description Generation Run active in the installation, so that provider traffic and operational behavior remain predictable.
15. As a Catalog Administrator, I want a second start attempt to return a clear conflict, so that I cannot accidentally overlap generation runs.
16. As a Catalog Administrator, I want synchronization, cleanup, consolidation, and edits for the target Workspace Database blocked during generation, so that the simple sequential run sees stable catalog state.
17. As a Catalog Administrator, I want to see the run's model, scope, status, counters, and timestamps, so that I understand what is happening.
18. As a Catalog Administrator, I want a chronological text log, so that I can follow completed targets and diagnose errors.
19. As a Catalog Administrator, I want live log updates with a polling fallback, so that temporary SSE problems do not hide run progress.
20. As a Catalog Administrator, I want completed and interrupted runs to remain inspectable, so that I can understand prior activity.
21. As a Catalog Administrator, I want to stop an active run, so that I can halt an incorrect or unexpectedly costly operation.
22. As a Catalog Administrator, I want stopping a run to terminate its current model helper and prevent later targets from starting, so that stop has prompt operational effect.
23. As a Catalog Administrator, I want valid results completed before a stop or failure to remain saved, so that useful work is not discarded.
24. As a Catalog Administrator, I want a run left active by a backend restart to become interrupted, so that the UI does not claim nonexistent work is still running.
25. As a Catalog Administrator, I want to unlock a stale active run when no generation process is alive, so that an erroneous recorded lock cannot block future work.
26. As a Catalog Administrator, I want Unlock rejected while a live generation process exists, so that recovery cannot create an overlapping run.
27. As a Catalog Administrator, I want Generate Missing to continue after interruption, so that recovery does not require a special resume mechanism.
28. As a Catalog Administrator, I want one retry for a transient model failure, so that a brief provider fault does not immediately lose a batch.
29. As a Catalog Administrator, I want the run to fail after three consecutive technical failures, so that a broken provider does not generate an unbounded stream of attempts.
30. As a Catalog Administrator, I want a successful request to reset the consecutive-failure count, so that isolated errors do not prematurely stop a useful run.
31. As a Catalog Administrator, I want a completed-with-errors result when isolated batches fail but the run reaches its end, so that partial problems remain visible.
32. As a Catalog Administrator, I want no automatic fallback to a different model, so that the selected model remains truthful and predictable.
33. As a Catalog Administrator, I want malformed or ambiguous model output rejected without writing it, so that descriptions cannot be assigned to the wrong target.
34. As a Catalog Administrator, I want an inability to infer a description represented by standard localized text, so that every valid outcome is understandable in the workspace language.
35. As a Catalog Administrator, I want technical failures kept distinct from non-generatable outcomes, so that provider problems are not mistaken for catalog knowledge.
36. As a Catalog Administrator, I want generated prose written in the workspace language, so that it matches the catalog's intended audience.
37. As a Catalog Administrator, I want generated text stored separately from curated Description, so that AI output remains a reviewable proposal.
38. As a Catalog Administrator, I want to edit a Generated Description manually, so that I can improve a proposal before consolidation.
39. As a Catalog Administrator, I want to select one or more tables or columns and run “Move generated description to Description” from the existing Actions control, so that review remains integrated into the current grids.
40. As a Catalog Administrator, I want consolidation to retain the Generated Description, so that I can still see the proposal from which the curated text was copied.
41. As a Catalog Administrator, I want selected records without a Generated Description skipped and reported, so that the bulk action does not erase curated text.
42. As a Catalog Administrator, I want consolidation and generation to modify only the Metadata Catalog, so that no external database comment is changed.
43. As a Catalog Administrator, I want prompts to use schema facts and existing catalog text, so that generated descriptions are grounded in available metadata.
44. As a Catalog Administrator, I want prompts to use at most five real source rows and five representative values when available, so that the model has useful examples without unbounded disclosure.
45. As a Catalog Administrator, I want to be warned that real source samples are sent to the selected provider, so that I can make an informed disclosure decision.
46. As a Catalog Administrator, I want sampled rows and values excluded from persistence and logs, so that operational history does not become a secondary data store.
47. As a security operator, I want API keys, prompts, samples, and complete provider payloads redacted from logs, so that diagnostics do not leak secrets or source data.
48. As a support operator, I want concise per-target and per-batch event messages, so that failures can be diagnosed without provider-specific internals.
49. As an authorized administrator, I want all generation, cancellation, unlock, and consolidation actions protected by database-management permission, so that ordinary users cannot mutate catalog metadata.
50. As an unauthorized user, I want generation controls hidden or disabled and API calls rejected, so that frontend visibility is not treated as authorization.
51. As an operator, I want setup changes to take effect after an application restart, so that configuration lifecycle remains simple and explicit.
52. As a product owner, I want the first release to avoid queues, parallel calls, distributed locks, and automatic resume, so that effort remains focused on generating and reviewing useful descriptions.
## Implementation Decisions
- The Fastify backend owns one installation-wide Description Generation Run and its sequential
processing loop. It does not delegate lifecycle ownership to Pi or Python.
- A Description Generation Run has one of `queued`, `running`, `completed`,
`completed_with_errors`, `cancelled`, `failed`, or `interrupted`. It stores the Workspace
Database, requested scope, selected model identifier, workspace language, progress counters,
timestamps, and an optional final error summary.
- Ordered Description Generation Events store timestamp, severity, and safe human-readable text.
When an event identifies a target, it uses the object type and qualified physical name, such as
`Column "patients.birth_date"` or `Table "patients"`; catalog UUIDs remain internal identifiers.
No durable per-target jobs, model invocation rows, prompt snapshots, sample snapshots, leases,
heartbeats, registry revisions, or provenance chains are introduced.
- Starting a run schedules an in-process background loop and returns the run immediately. The API
exposes start, run/history lookup, event listing and streaming, cancellation, stale-run unlock,
and the safe list of configured model choices. There are no retry-item or resume endpoints.
- One in-memory generation manager enforces the installation-wide active-run rule. The existing
Catalog Operation Coordinator reserves the target Workspace Database for the duration of the
run, without being generalized into a new operation framework.
- On backend startup, persisted `queued` or `running` Description Generation Runs become
`interrupted`. The application performs no automatic replay or resume.
- Unlock succeeds only when no live generation loop or helper child exists. It marks the stale run
interrupted and releases the local reservation; it is not a distributed lock recovery protocol.
- Each model completion uses a short-lived Python helper backed by LiteLLM. Structured input is
supplied over stdin, structured output alone is emitted on stdout, diagnostics use stderr, and
the helper can be terminated by cancellation.
- A completion request contains no more than ten targets. Requests run one at a time. The helper
performs at most one retry for a transient technical failure.
- Three consecutive model-request failures fail the run. A successful request resets that count.
Isolated exhausted failures may be logged and skipped, producing `completed_with_errors` if the
run later reaches its end.
- Every valid generated or non-generatable result is applied immediately to Generated Description.
Earlier writes are retained after cancellation, interruption, or later failure.
- A response must identify requested targets unambiguously and classify each returned result as
generated or non-generatable. Duplicate, unknown, missing, or malformed mappings cause a
technical request failure and no result from that ambiguous response is applied.
- The parser also tolerates one JSON object enclosed by one complete `json` code fence, because
some supported models add that formatting despite the prompt. Any prose outside the fence,
multiple payloads, or malformed/ambiguous mappings remain invalid.
- The application supplies localized standard non-generatable text. Provider wording is not used
as the standard value, and technical errors never write that value.
- A full-database run generates eligible Catalog Columns before Catalog Tables. Generate Missing
excludes targets whose Generated Description is already non-empty; all-generation may replace
existing generated proposals only after the initiating action makes that scope explicit.
- Model choices are declared under a metadata-generation section in installation setup YAML. Each
choice has a stable identifier, display label, LiteLLM provider/model settings, optional endpoint
settings, and an optional environment-secret reference for its API key. The reference may be
omitted only when an explicit endpoint is configured for unauthenticated access. One identifier
is the default.
- An explicit endpoint may set `disableThinking: true`; the helper translates it only to the
Qwen-compatible chat-template switch needed to keep the response within the strict JSON contract.
- Metadata-generation setup is separate from application settings for Pi and from workspace
`llm_policy`. Raw keys never enter setup YAML, the catalog database, API responses, process
arguments, or event text. Configuration reload is restart-only.
- If setup defines no usable model, the safe model-list response is empty and the UI disables
generation with an explanation. The backend still rejects direct generation attempts.
- Prompt construction treats schema names, comments, descriptions, and values as untrusted data.
It requests output in the workspace language and separates instructions from catalog content.
- A request may contain up to five real source rows and up to five representative distinct,
non-null values for relevant columns. Inputs are bounded before prompt construction and are not
persisted or logged.
- The UI discloses that real data can be sent to the selected provider. A future Sensitive Data
Policy will classify values and exclude or anonymize protected data; that policy is not silently
approximated in this slice.
- The generation UI reuses Database Management's table and column selections, model selector,
Actions control, run drawer conventions, SSE delivery, and polling fallback where practical.
Visual parity with Catalog Sync Run logs is not required.
- The consolidation action copies each selected, non-empty Generated Description into Description
in a catalog transaction, retains Generated Description, skips empty proposals, and reports
copied and skipped counts. It never writes to the external Workspace Database.
- Generation, cancellation, unlock, and consolidation require the existing database-management
permission and are validated by the backend independently of UI state.
- No user-facing generation CLI is added. The Python process is an internal completion adapter,
not an operator surface or a long-lived service.
## Testing Decisions
- Tests assert externally observable behavior rather than private loop structure, process timing,
or LiteLLM implementation details.
- The primary and highest test seam is the Fastify catalog API with a test PostgreSQL catalog and
an injected fake Model Completer. It verifies complete paths through authorization, run
persistence, sequential processing, event delivery, Generated Description updates, and final
status without contacting a real provider.
- API tests cover each generation scope, column-before-table order, the ten-target request bound,
model validation, one-active-run conflict, target-database exclusion, cancellation, startup
interruption, Unlock safeguards, Generate Missing, partial success, consecutive failure
handling, non-generatable localization, malformed responses, redacted events, and permissions.
- Catalog repository integration tests verify the migration, run and event ordering, active-run
constraint, immediate description writes, history queries, startup interruption, and bulk
consolidation behavior against PostgreSQL.
- The Python helper has a small black-box contract suite using a simulated LiteLLM adapter. It
verifies stdin/stdout framing, pristine stdout, stderr diagnostics, normalized success and
failure output, one transient retry, secret redaction, and termination behavior.
- Setup-validation tests cover duplicate model identifiers, missing or unknown defaults, malformed
provider settings, missing secret references, safe public model projection, and strict separation
from Pi and workspace model settings.
- Database Management tests use the existing browser-level component seam with MSW. They verify
model selection and default, selected/all/missing actions, disabled state without models, running
progress and logs, polling recovery, cancellation, Unlock visibility, terminal summaries,
generated-text refresh, and selected consolidation with copied/skipped counts.
- Existing Catalog Sync Run route, repository, SSE, and drawer tests are prior art for asynchronous
status and event behavior. Existing catalog table/column editing and Database Management tests
are prior art for optimistic catalog updates, permissions, selection, and action controls.
- One required manual acceptance gate, outside deterministic CI, uses the installation's configured
default model and a disposable PostgreSQL database containing only invented data. Its application
credentials are read-only. It generates Italian text for one Catalog Column and one Catalog
Table, verifies their Generated Description, inspects the safe activity log, confirms that no key
or sample value is exposed, and consolidates one selected result. If the configured secret is not
available, acceptance stops without exposing or requesting the key in conversation.
- Delivery includes a strict MkDocs build executed through repository-managed, reproducible
documentation dependencies rather than globally installed Python packages. A readable direct
dependency file is retained, a complete transitive lock is generated with `uv`, and one canonical
repository command performs the strict build from that lock.
- Successful real-provider acceptance is recorded in a short sanitized report under
`docs/testing/`. It identifies the model and checks performed but contains no credentials,
prompts, source samples, complete provider payloads, or generated database values.
- No tests are added for worker queues, parallel generation, distributed locking, multi-replica
recovery, automatic resume, cost accounting, or model fallback because those behaviors are out
of scope.
## Out of Scope
- Reusing Pi to execute Description Generation or changing Pi's model configuration.
- A shared Installation Model Registry, model gateway, long-lived Python sidecar, or provider
management platform.
- A user-facing generation CLI.
- Parallel model calls, worker queues, adaptive rate limiting, distributed locks, leases,
heartbeats, automatic resume, or multi-replica execution.
- Durable target jobs, invocation history, prompts, samples, token usage, cost accounting,
provenance chains, target snapshots, or advanced retention controls.
- Automatic retry or resume of individual targets beyond one technical helper retry and a new
Generate Missing run.
- Automatic fallback to a different model.
- Writing generated text into comments of the external Workspace Database.
- Generating logical relationships or other catalog metadata beyond Catalog Table and Catalog
Column descriptions.
- Implementing the Sensitive Data Policy. Its definition and exclusion/anonymization behavior are
a required follow-up improvement.
- Generalizing the log viewer across unrelated metadata operations. That broader concern remains
related to Gitea issue #2.
## Further Notes
- The design deliberately follows ThothAI's proven simple workflow while adapting it to ThothII's
asynchronous browser interaction, setup ownership, and existing Generated Description model.
- The source-sampling disclosure is a release requirement, not merely documentation for operators.
- `completed_with_errors` is reserved for a run that reaches the end after isolated technical
failures. Three consecutive failures end the run as `failed`.
- Successful values are their own recovery record: after interruption, Generate Missing naturally
skips them without needing replay state.
- The implementation is available without a feature flag once the catalog migration and valid
setup are present. With no configured model, the feature remains visibly unavailable rather than
partially initialized.
- The final manual gate is intentionally narrow: one real-provider run covers one Catalog Column
and one Catalog Table, generated Italian text, safe events, and one consolidation. Automated
tests remain the evidence for All, Missing, Stop, restart interruption, Unlock, and failure paths.
@@ -1,173 +0,0 @@
# AI catalog description generation
Status: simplified design, API, persistence, test seams, and delivery tickets accepted.
## Objective
Bring ThothAI's useful AI comment-generation workflow into the ThothII Metadata Catalog without
turning it into a general job platform. Administrators can generate editable descriptions for
catalog tables and columns, inspect progress, stop a run, recover a stale run, and explicitly copy
approved generated text into the curated Description field.
The implementation is UI/API only. There is no user-facing generation command.
## ThothAI behavior retained
- Generate descriptions for selected tables, selected columns, missing descriptions, or all
eligible targets.
- Generate columns before their containing table when running the full workflow, so table prompts
can benefit from the resulting column descriptions.
- Process bounded batches of at most ten targets, one model request at a time.
- Include schema context, existing catalog text, up to five real source rows, and up to five
representative non-null values when available.
- Keep generated text separate from the curated Description until an administrator consolidates
it.
- Use the existing table and column checkboxes plus the Actions selector to copy Generated
Description into Description for one or more selected records. The generated value is retained.
- Store a localized standard value such as `Non generabile` when a valid model response says that
a description cannot be inferred.
Unlike ThothAI, every generation action is asynchronous from the browser's perspective and exposes
a persistent, readable activity log.
## Minimal architecture
The Fastify backend owns the run lifecycle and sequential loop. It starts one short-lived Python
helper for each model completion. The helper uses LiteLLM, accepts structured input on stdin,
returns structured output on stdout, and writes diagnostics only to stderr.
This is preferred over reusing Pi. Pi remains the interactive NL-to-SQL orchestration surface,
whereas description generation is a bounded batch transformation with no conversational state or
human gate. A LiteLLM helper avoids inventing a Pi session protocol for a task that needs one
request and one structured response.
There is no Python daemon, model gateway, queue service, worker pool, or generation CLI. Python is
already a core implementation language in ThothII's harness and core image; this helper does not
introduce a new runtime family.
## Run lifecycle and exclusion
- At most one Description Generation Run may be queued or running in the installation.
- Start returns immediately after creating the run and scheduling the in-process backend loop.
- Requests are sequential; there is no parallel provider traffic.
- The target Workspace Database is reserved through the existing in-memory catalog-operation
coordinator. Synchronization, cleanup, consolidation, and direct catalog edits for that database
are rejected while generation is active.
- A second generation start is rejected with a conflict response.
- Stop terminates the current helper process, stops further targets, and marks the run cancelled.
- Backend startup marks any queued or running generation row interrupted. It does not resume work.
- Generate Missing is the normal manual continuation mechanism because successful values were
already saved.
- Unlock is available only when the backend has no live generation process; it marks a stale
recorded run interrupted and clears the local reservation.
This is intentionally a single-process policy. Multi-replica coordination is out of scope.
## Persistence
Persist only:
- a Description Generation Run with database, scope, selected model, language, status, counters,
timestamps, and an optional final error summary;
- ordered Description Generation Events containing timestamp, level, and human-readable text;
- each successful or non-generatable result directly in the target's Generated Description.
Do not add per-target job rows, invocation history, prompt or sample snapshots, provider cost
accounting, leases, heartbeats, registry revisions, or generated-description provenance. The event
log is operational evidence, not a replay mechanism.
## Model setup
Selectable models and their default belong to application setup YAML, not to a workspace. Each
entry supplies a stable display identifier, LiteLLM provider/model information, optional endpoint
settings, and—unless that explicit endpoint is unauthenticated—a reference to an installation
secret containing the API key. Keyless entries without an explicit endpoint are invalid. Raw keys must not be
stored in the YAML, database, frontend, events, or process arguments.
An explicit endpoint may opt into `disableThinking: true` when its Qwen-compatible chat template
would otherwise place reasoning text around the required JSON result.
This metadata-generation configuration is independent of the existing Pi provider/model settings
and workspace `llm_policy`. A setup change takes effect after application restart. If no model is
configured, generation controls are disabled with an explanatory message.
The browser receives only the selectable identifiers and labels. The selected value defaults to
the setup default and is validated again by the backend when a run starts.
## Prompt inputs and outputs
Targets are grouped in model requests of at most ten. Prompts distinguish instructions from
untrusted schema names, comments, descriptions, and sampled values. A response must map every
returned result to a requested target and classify it as generated or non-generatable. Missing,
duplicate, unknown, or malformed target results make that request a technical failure rather than
silently writing ambiguous text.
One complete `json` code fence around the object is tolerated for model compatibility; prose
outside it, multiple payloads, and ambiguous mappings are still rejected.
For a complete database run, eligible columns are processed before tables. A table request can use
the current Generated Description or Description of its columns. The output language is the
workspace language; the standard non-generatable text is localized by the application rather than
trusted to arbitrary model wording.
Up to five source rows and five representative examples may be sent to the provider and are never
persisted. Delivery must call out this disclosure. A follow-up Sensitive Data Policy will define
which values are excluded or anonymized.
## Errors, retry, and logs
The helper performs at most one retry for a transient technical provider failure. A final failed
request produces an error event and increments the consecutive-error count. The run stops as
failed after three consecutive technical failures; any successful request resets the count. There
is no automatic fallback to another model.
Valid non-generatable outcomes are results, not technical errors. Successful results from earlier
requests remain stored when a later request fails or the run is stopped.
The UI shows status, counters, selected model, start/end times, and a chronological text log. Live
delivery may reuse the existing SSE infrastructure with polling as fallback; exact visual parity
with synchronization logs is not required. Logs must not contain API keys, prompts, source sample
values, or full provider payloads.
## Explicitly deferred complexity
- shared model registry or cutover of Pi configuration;
- long-lived Python sidecar or internal HTTP model gateway;
- generic catalog-operation kernel;
- durable target items, invocation records, target snapshots, or provenance chains;
- distributed locks, leases, heartbeats, worker queues, automatic resume, or multi-replica support;
- parallel calls, adaptive rate limiting, cost estimation, advanced metrics, or model fallback;
- user-facing generation CLI;
- automatic writeback to comments in the external database;
- Sensitive Data Policy implementation, which remains a required improvement after this slice.
## Delivery tracking
The accepted specification is Gitea issue #4 and the implementation is split into issues #5–#11.
Each ticket is a bounded vertical slice with explicit Gitea dependencies. Implementation proceeds
from the unblocked frontier, using a fresh subagent context for each ticket; integration and final
verification remain centralized so later slices cannot silently reopen the deferred platform
features above.
Issue #4 remains open until delivery completes four final gates: the stale Compose service-set
contract is corrected in its own commit; documentation dependencies are repository-managed and a
strict MkDocs build passes; one narrow real-provider acceptance run succeeds against non-sensitive
test data; and a separate, non-blocking Sensitive Data Policy design ticket is linked as required
follow-up work.
The documentation toolchain retains a readable direct-dependency input, adds a complete lock
generated with `uv`, and exposes one canonical strict-build command. Real-provider acceptance uses
the installation's configured default model and a disposable PostgreSQL database seeded only with
invented values and accessed read-only by the application. A missing protected model secret stops
the gate without disclosing it. The successful gate is captured in a sanitized report under
`docs/testing/` without prompts, samples, full generated values, payloads, or credentials.
Delivery is organized as four reviewable commits: the stale Compose contract correction, the
reproducible documentation toolchain, the AI-description feature, and—only after acceptance—the
sanitized acceptance report. A failed real-provider gate does not invalidate already verified
commits, but issue #4 remains open and no acceptance report claims success. Application defects are
fixed and reverified; missing configuration or provider unavailability is recorded and retried.
After every gate passes, the existing `codex/db-management` branch is pushed to its configured
origin without introducing a new pull-request workflow, then issue #4 is closed with links to the
delivery evidence. The separate Sensitive Data Policy issue is created as non-blocking follow-up,
linked to #4, and labeled `enhancement` plus `ready-for-human` because its design requires a future
`grill-with-docs` before agent implementation.
@@ -1,248 +0,0 @@
# Installation Model Catalog
Status: implemented on 2026-09-02.
## Outcome
`thothii-installation.yaml` is the only operator-authored source for models used by interactive
sessions, metadata generation, and embedding. Runtime-specific files are deterministic projections,
not additional configuration sources. Workspace descriptors contain database and Evidence concerns
and no model, provider, allowlist, default, embedding, or vector-store configuration.
This design does not merge execution lifecycles. Pi continues to run interactive sessions, the
short-lived LiteLLM helper continues to perform metadata generation, and the internal Ollama service
continues to provide embeddings. They share model declaration, not execution machinery.
## Canonical installation shape
The following example covers all currently required cases: a Pi built-in model, an authenticated
custom endpoint, a keyless internal endpoint, metadata generation, and the single embedding model.
```yaml
schemaVersion: 2
profile: server
projectDirectory: /srv/thothii
envFile: /srv/thothii/operator.env
workspaceRepository:
remote: git@git.example.com:organization/workspaces.git
branch: main
access: ssh
modelCatalog:
defaults:
session: zai/glm-5.3
metadataGeneration: local-qwen/qwen3.6-35b-a3b
embedding:
id: ollama/qwen3-embedding:0.6b
dimensions: 1024
providers:
deepseek:
authentication:
mode: pi_auth
session:
mode: pi_builtin
models:
deepseek-v4-pro:
session: {}
deepseek-v4-flash:
session: {}
zai:
endpoint:
baseUrl: https://api.z.ai/api/coding/paas/v4
authentication:
mode: secret_env
apiKeyEnv: ZAI_API_KEY
session:
mode: openai_compatible
metadataGeneration:
litellmProvider: openai
models:
glm-5.3:
label: GLM-5.3
session:
reasoning: true
contextWindow: 200000
maxTokens: 131072
metadataGeneration: {}
local-qwen:
endpoint:
baseUrl: https://ml-aritmolab.policlinicosandonato.it/v1
authentication:
mode: none
session:
mode: openai_compatible
metadataGeneration:
litellmProvider: openai
models:
qwen3.6-35b-a3b:
label: Qwen3.6 35B A3B
session:
reasoning: false
contextWindow: 131072
maxTokens: 16384
compatibility:
supportsDeveloperRole: false
supportsReasoningEffort: false
supportsStore: false
maxTokensField: max_tokens
metadataGeneration:
disableThinking: true
authentication:
configDirectory: /srv/thothii/auth-canonical
runtimeProjection:
directory: /srv/thothii/auth-runtime
uid: 10001
gid: 10001
```
The catalog uses maps instead of repeated IDs. The canonical identity of a model is always derived
as `<provider-key>/<model-key>`. `upstreamModel` may be added to a model only when the endpoint uses
a different identifier. `label` is optional and falls back to the canonical identity.
Model eligibility is not repeated in an `usages` array. A `session` block makes the model eligible
for sessions; a `metadataGeneration` block makes it eligible for metadata generation. The embedding
is a single required installation value rather than a list plus default.
## Provider and authentication rules
A provider owns one endpoint, one authentication mode, and zero or one adapter for each runtime.
Model entries cannot override provider endpoint or credentials. If the same upstream service needs
different endpoints or credentials, the installation declares two provider identities.
Supported session modes are intentionally closed:
- `pi_builtin`: Pi already owns the model's technical descriptor; the model's `session` block is
empty and ThothII does not copy context-window or compatibility facts.
- `openai_compatible`: ThothII generates a Pi custom-provider descriptor; each session model supplies
the technical values required by Pi.
Metadata generation uses the provider-level `litellmProvider`. A model-level
`metadataGeneration.disableThinking: true` is permitted only for an explicit compatible endpoint.
There is no generic adapter or plugin abstraction in schema version 2.
Exactly one provider authentication mode is allowed:
- `secret_env` requires an approved API-key environment reference present in the protected secret
bundle. Secret values never enter YAML, generated files, logs, arguments, or API responses.
- `pi_auth` is valid only for session-only `pi_builtin` providers and resolves through Pi's protected
authentication projection.
- `none` is valid only for an explicit endpoint. Runtime projections may supply a fixed non-secret
compatibility placeholder when a client library requires a non-empty key.
## Defaults and selections
`defaults.session` and `embedding` are required. `defaults.metadataGeneration` is required exactly
when at least one model has a `metadataGeneration` block; metadata generation may otherwise be
absent and its UI controls are disabled.
`modelCatalog.defaults.session` is the only configured session-model default. `PI_PROVIDER`,
`PI_MODEL`, and provider/model fields in installation-default settings are removed. A user choice is
a Model Selection containing only the canonical model identity and runtime controls such as thinking
level. A session manifest pins the selected canonical identity.
Removing the currently selected model causes new-session selection to fall back to the catalog
default with an explicit administrative warning. An existing session is never silently moved to a
different model; resume fails with `model_unavailable` when its pinned identity can no longer be
resolved.
## Generated runtime projections
Before Compose starts, `tht` strictly validates schema version 2 and generates installation-local
artifacts below `deploy/<installation-id>/generated/`:
- a normalized catalog JSON consumed defensively by the backend;
- Pi `models.json` for custom providers;
- Pi `settings.json`, combining fixed product settings with the session-eligible canonical IDs;
- a Compose override that mounts the projections and supplies embedding identity and dimensions to
core, preprocessing, and `embedding-model-init`.
Generation is deterministic and published only after every candidate artifact validates. A failed
generation aborts start before Compose is invoked. `tht doctor` recomputes expected bytes and reports
differences; no digest manifest or separate apply command exists. When projection bytes change,
`tht start` recreates the affected services so they cannot continue with an older bind mount.
Pi-only restart, update, and rollback operations reject projection drift and direct the operator to
`tht start`, because applying only the core-facing files could leave embedding services stale.
Generated projections are not backed up. Restore validates the canonical installation descriptor,
regenerates every projection, and only then starts services. Base Compose files and `operator.env`
must contain no model identities, defaults, endpoints, or dimensions.
## Workspace schema v4
Workspace schema v4 removes both top-level `llm_policy` and `semantic_index`. The entire latter
block is redundant today: its engine and distance are product constants, its collection duplicates
the workspace ID, and its model and dimensions are installation facts.
The runtime derives:
- Qdrant collection identity from the workspace ID;
- engine and distance from the supported product contract;
- embedding identity and dimensions from the Installation Model Catalog.
The published index generation records the canonical embedding identity and dimensions that created
it. A mismatch makes the index explicitly incompatible and requires operator-triggered
preprocessing. No existing index is deleted or rebuilt automatically.
The v3-to-v4 workspace migration is deterministic: set `workspace.schema_version` to `4`, remove
`llm_policy`, and remove `semantic_index`. It does not alter database, Evidence, diagnostics, or
binding data.
## Installation migration
Legacy installation migration must inspect all three former sources:
1. `metadataGeneration` in `thothii-installation.yaml`;
2. `deploy/pi/models.json`;
3. `deploy/pi/settings.json`.
The migrator emits a version-2 candidate only when it can reconcile identities, endpoints,
credentials, and runtime-specific facts without guessing. Ambiguous aliases such as `glm-53`,
`zai/glm-5.3`, and `openai/glm-5.3` are not silently equated. A conflict produces a field-level
report and leaves every input unchanged for operator resolution.
After migration, the strict loader rejects `metadataGeneration`, workspace `llm_policy`, workspace
`semantic_index`, legacy Pi source files, unknown fields, duplicate YAML keys, invalid defaults, and
incompatible authentication/adapter combinations with an actionable `migration_required` or
validation error.
## Final simplicity audit
The accepted design removes every configuration duplication that can be removed without inference:
- one authored installation file instead of an installation block plus two Pi files;
- one canonical `provider/model` identity instead of display IDs and runtime IDs;
- per-use blocks instead of a duplicated usages list;
- one embedding entry instead of a selectable embedding catalog;
- one catalog session default instead of environment and settings defaults;
- no model or vector-store fields in workspace descriptors;
- provider-level credentials instead of per-model credentials;
- no generic runtime-plugin abstraction;
- no persisted digest, apply command, or backup of generated projections.
The remaining generated files are necessary boundary adapters, not configuration concepts. Making
the backend parse the authoring YAML independently would remove one file but restore two semantic
validators. Hard-coding embedding values in Compose would remove one projection but restore a model
source outside the catalog. Inferring authentication from missing fields would save one YAML key but
turn a safe explicit choice into ambiguity. These apparent simplifications are therefore rejected.
No further reduction was found that preserves one authority, strict validation, explicit security,
session determinism, and model-free workspaces.
## Implementation surface
Implementation must update the host `tht` installation loader, setup and lifecycle projection,
doctor, backup/restore, Compose mounts and embedding inputs, backend catalog/settings/session model
resolution, workspace schema and migration, runtime rendering and diagnostics, frontend workspace
drafts and model filtering, examples, fixtures, and documentation. Existing session manifests remain
readable and keep their pinned provider/model identity; only resume resolution changes to the new
catalog.
Implementation completed after explicit approval. The installation schema, deterministic runtime
projections, migration path, model-free workspace schema v4, backend consumers, operator UI,
fixtures, and documentation now enforce this contract.
+3 -3
View File
@@ -5,9 +5,9 @@ manuale con commit/push dell'operatore accettati per la release 0;
restanti semplificazioni confermate, con chiarimenti su impatto core e controllo Git; restanti semplificazioni confermate, con chiarimenti su impatto core e controllo Git;
E1, E2, E3 e il collegamento ai gate X1 implementati il 2026-09-09. E1, E2, E3 e il collegamento ai gate X1 implementati il 2026-09-09.
Il contratto X1 è in [Session corrections](../contracts/archive-repair.md). Risultati e limiti in Il contratto X1 è in [Session corrections](../contracts/archive-repair.md). Risultati e limiti in
[E1 — validazione](2026-09-09-evidence-e1-validation.md) e [E1 — validazione](../reports/knowledge-archives-release.md) e
[E2 — validazione](2026-09-09-evidence-e2-validation.md) e [E2 — validazione](../reports/knowledge-archives-release.md) e
[E3 — validazione](2026-09-09-evidence-e3-validation.md). [E3 — validazione](../reports/knowledge-archives-release.md).
La [revisione di semplicità](2026-09-08-memory-evidence-simplification-review.md) La [revisione di semplicità](2026-09-08-memory-evidence-simplification-review.md)
registra il riesame di Q1–Q15. Il flusso principale è draft scritta dallo specialista registra il riesame di Q1–Q15. Il flusso principale è draft scritta dallo specialista
@@ -2,7 +2,7 @@
Data del piano: 2026-09-08. Aggiornamento 2026-09-09: M1–M3, E1–E3 e X1 Data del piano: 2026-09-08. Aggiornamento 2026-09-09: M1–M3, E1–E3 e X1
implementati. Risultati e limiti della verifica finale sono raccolti nel implementati. Risultati e limiti della verifica finale sono raccolti nel
[rapporto X1](2026-09-09-archive-repair-x1-validation.md). [rapporto X1](../reports/knowledge-archives-release.md).
La [revisione di semplicità](2026-09-08-memory-evidence-simplification-review.md) La [revisione di semplicità](2026-09-08-memory-evidence-simplification-review.md)
riesamina tutte le decisioni Q1–Q15 alla luce degli ultimi chiarimenti del riesamina tutte le decisioni Q1–Q15 alla luce degli ultimi chiarimenti del
+1 -1
View File
@@ -6,7 +6,7 @@ confini di test confermati dal proprietario il 2026-09-08.
Primo incremento del progetto Memory management. Attua le decisioni già approvate Primo incremento del progetto Memory management. Attua le decisioni già approvate
nel piano del 2026-09-08 e nell'ADR 0018. Le scelte tecniche di dettaglio qui nel piano del 2026-09-08 e nell'ADR 0018. Le scelte tecniche di dettaglio qui
proposte derivano dalla ricognizione del runtime. Gli esiti dell'implementazione proposte derivano dalla ricognizione del runtime. Gli esiti dell'implementazione
sono riportati nel [rapporto di verifica](2026-09-08-memory-m1-validation.md). sono riportati nel [rapporto di verifica](../reports/knowledge-archives-release.md).
## Problem Statement ## Problem Statement
@@ -1,103 +0,0 @@
# M1 — Implementazione e verifica
Data: 2026-09-08. Implementazione locale della
[specifica approvata](2026-09-08-memory-m1-spec.md), associata all'
[issue 27](https://git.tylconsulting.it/mptyl/ThothII/issues/27).
## Risultato
La pagina **Memory management** è disponibile nell'Administration dopo Database
management. Gestisce le quattro famiglie di card, elenco completo, ricerca e filtri,
ordinamento, dettaglio, creazione, modifica, cancellazione, collegamenti e dipendenze.
Richiede un amministratore autenticato e una selezione esplicita del workspace;
non richiede una sessione o un database DWH configurato.
Il harness possiede l'archivio PostgreSQL `thoth_memory`. Card, collegamenti,
dipendenze e lavoro di propagazione sono salvati nella stessa transazione.
La pagina distingue salvataggio fallito e contenuto salvato con indice incompleto,
offrendo retry anche per le cancellazioni. Recall Memory ed exemplar verificano
esistenza, workspace e proiezione corrente nell'archivio prima di restituire contenuto.
Promozione, salvataggio singolo e finalizzazione corrente passano dal servizio
autorevole. Le ricevute della sorgente impediscono duplicati e ricreazione di card
cancellate. Reindicizzazione e preprocessing non importano vecchi payload o sessioni.
L'errore Memory non annulla una sessione già finalizzata; il gate segnala anche
una promozione salvata con indicizzazione incompleta.
Le migrazioni sono versionate, controllate tramite checksum e incluse nel wheel
e nell'immagine core. Il servizio di preparazione `catalog-migrate` le esegue dopo
quelle del Catalog. Il runtime assume il ruolo limitato `thoth_memory_runtime`,
con isolamento del workspace tramite RLS e senza privilegi DDL.
## Verifiche eseguite
| Confine | Esito |
| --- | --- |
| Harness, test senza L0/L2 | 1.134 passati; i 9 test dei percorsi portabili sono stati eseguiti separatamente e sono passati. |
| Servizio Memory, PostgreSQL e Qdrant reali | 17 passati, inclusi CLI, migrazioni, ruolo runtime, isolamento, transazioni, outage, retry, cancellazioni, cambio famiglia e rebuild. |
| Gate Pi | 190 passati, inclusi identità UUID e avviso dopo salvataggio con indice incompleto. |
| Backend | 1.345 passati nella suite completa, 40 esclusi dalle condizioni previste dai test; un test di autenticazione ha superato il timeout sotto carico. Il relativo file è stato rieseguito isolato: tutti i 17 test passati. |
| Frontend | 632 passati, inclusi ingresso dall'AppShell, form, filtri, collegamenti, dipendenze e retry delle cancellazioni. |
| Browser integrato | Passato: autenticazione amministratore, creazione, modifica, riavvio del backend, rilettura, cancellazione e assenza nel recall. |
| Build e tipi | Build backend e frontend, typecheck TypeScript e build documentale strict superati. |
| Lint e diff | Ruff sui file Python modificati e `git diff --check` superati. Il lint globale segnala tre rilievi in file non modificati, elencati sotto. |
Il browser utilizza autenticamente frontend, login locale, Fastify, ThtRunner,
CLI Python, PostgreSQL e Qdrant. Gli embedding sono deterministici e le attività
Pi/sessione estranee al percorso Memory usano le fixture esistenti. Non sono state
intercettate le API Memory. Sono stati usati container temporanei PostgreSQL 16 e
Qdrant 1.18.2, senza accesso a un DWH remoto o a un modello generativo.
Il test browser ha consentito di correggere etichette accessibili instabili nei
campi compilati e la sovrapposizione del pannello di recupero ai comandi del dettaglio.
La selezione del workspace e l'uscita dalla pagina sono bloccate durante le operazioni.
Il lint globale preesistente riguarda soltanto:
- ordinamento import in `harness/tests/test_effective_relationships.py`;
- ordinamento import in `harness/tests/test_p3_dwh_binding.py`;
- uso di `datetime.UTC` in `harness/tht/mschema/catalog_snapshot.py`.
## Riproduzione
Usare Node 24 e le dipendenze installate dei tre layer. Per eseguire il harness
in un ambiente con home non scrivibile si può impostare `THT_HOME` su una directory
di prova. I test dei percorsi portabili devono essere eseguiti senza questo override,
perché verificano deliberatamente la risoluzione dell'home e di `THT_DATA_ROOT`.
```sh
cd harness
THT_HOME=/private/tmp/thothii-m1-test-home .venv/bin/pytest -m 'not l0 and not l2' --ignore=tests/test_portable_paths.py -q
.venv/bin/pytest tests/test_portable_paths.py -q
.venv/bin/pytest tests/memory/test_administration.py -q
npm test
```
```sh
cd backend
npx vitest run
npx tsc --noEmit -p .
npm run build
```
```sh
cd frontend
npx vitest run
npx tsc -b
npm run build
THT_MEMORY_BROWSER_E2E=1 npx playwright test e2e/memory-real.spec.ts
```
Il percorso browser richiede Docker, Python del harness, Go per il bridge di
autenticazione e Chromium di Playwright. Avvia risorse isolate e le rimuove alla
fine. Su macOS il browser deve poter avviare i processi Chromium fuori dalle
restrizioni della sandbox. La build documentale si esegue dalla radice con
`./scripts/build-docs.sh`.
## Stato della consegna
Le modifiche sono nel worktree locale. Nessuno stack già attivo è stato aggiornato
e nessun dato esistente è stato migrato o eliminato. Prima di usare M1 su
un'installazione occorrono il nuovo core e la preparazione `catalog-migrate`.
M2 (retrieval ibrido ed espansione dei collegamenti), M3 (integrazione estesa nel
workflow) ed Evidence management restano incrementi successivi.
+1 -1
View File
@@ -2,7 +2,7 @@
Data del piano: 2026-09-08. Aggiornamento 2026-09-09: M1–M3 implementati, Data del piano: 2026-09-08. Aggiornamento 2026-09-09: M1–M3 implementati,
con integrazione X1 per le correzioni persistenti dei conflitti. Risultati e limiti con integrazione X1 per le correzioni persistenti dei conflitti. Risultati e limiti
sono raccolti nel [rapporto X1](2026-09-09-archive-repair-x1-validation.md). sono raccolti nel [rapporto X1](../reports/knowledge-archives-release.md).
La [revisione di semplicità](2026-09-08-memory-evidence-simplification-review.md) La [revisione di semplicità](2026-09-08-memory-evidence-simplification-review.md)
mantiene il perimetro Memory e precisa un salvataggio sequenziale e una pulizia mantiene il perimetro Memory e precisa un salvataggio sequenziale e una pulizia
@@ -1,9 +1,31 @@
# PRD — Security hardening per Docker personale e server multiutente # PRD — Security hardening per Docker personale e server multiutente
## Ripresa del lavoro e limiti di autorizzazione
Questo PRD resta una bozza, non una specifica di implementazione approvata. Il precedente
prompt di ripresa è stato consolidato qui il 15 settembre 2026. Riconfermare i rilievi
SEC-01–SEC-12 contro codice e dipendenze correnti, separando fatti, ipotesi, rischi del
profilo locale/server e problemi già risolti. Le vecchie survey non provano lo stato del server.
Usare inizialmente controlli in sola lettura e dati sintetici; accesso a server, IdP, DWH
o provider e relative mutazioni richiedono target e operazioni concordati.
Prima di implementare, il proprietario deve confermare profili di fiducia, isolamento
del runtime Pi, trattamento dei valori sensibili, revoca, retention, limiti di risorse,
priorità e criteri misurabili. I modelli visibili sul server sono un problema separato.
Registrare le decisioni in questo PRD; pubblicare spec e ticket Gitea soltanto dopo la
conferma del perimetro e della granularità. La bonifica documentale non autorizza il
codice di sicurezza, né il rollout di tutti i punti SEC.
L'implementazione futura richiede un worktree dedicato, test positivi/negativi ai confini
concordati, typecheck e revisione contro standard e specifica. Conservare gate manuali,
evidenze e rollback; un ticket completato non chiude automaticamente il PRD. Non copiare
segreti o dati operativi nei worktree. Usare le skill effettivamente disponibili per
chiarimento, diagnosi e revisione, senza assumere che i vecchi nomi dei comandi esistano.
**Stato:** bozza da validare con `grill-with-docs`; implementazione rinviata. **Stato:** bozza da validare con `grill-with-docs`; implementazione rinviata.
**Data:** 8 settembre 2026. **Data:** 8 settembre 2026.
**Owner delle decisioni:** il maintainer di ThothII. **Owner delle decisioni:** il maintainer di ThothII.
**Ripresa:** [prompt per la prossima sessione](2026-09-08-security-hardening-resume-prompt.md). **Ripresa:** seguire la sezione «Ripresa del lavoro e limiti di autorizzazione» sopra.
Questo documento conserva la survey di sicurezza discussa con il maintainer e propone requisiti, Questo documento conserva la survey di sicurezza discussa con il maintainer e propone requisiti,
priorità e criteri di accettazione. Non è una spec approvata, un penetration test, una certificazione priorità e criteri di accettazione. Non è una spec approvata, un penetration test, una certificazione
@@ -1,100 +0,0 @@
# Riprendere il PRD di sicurezza con le skill di Pocock
**Stato:** prompt conservato per uso futuro; nessuna esecuzione programmata.
**PRD:** [Security hardening per Docker personale e server multiutente](2026-09-08-security-hardening-prd.md).
Apri una sessione nella codebase ThothII e incolla il blocco seguente. Il nome corretto della
skill è `grill-with-docs`, che combina `grilling` e `domain-modeling`. Il prompt apre la fase di
chiarimento; il passaggio a issue, worktree e implementazione resta soggetto alle conferme indicate.
```text
Riprendiamo il lavoro di sicurezza rinviato l'8 settembre 2026.
Leggi docs/plans/2026-09-08-security-hardening-prd.md. È una bozza di PRD ricavata da una
survey storica, non una spec approvata né una prova della configurazione del server remoto.
Voglio preparare interventi proporzionati per Docker su Mac/PC personale e per un server
multiutente con autenticazione built-in oppure OIDC. Il problema separato dei modelli
visibili sul server è fuori perimetro.
Usa realmente le skill di Matt Pocock: leggi le istruzioni installate, dichiarando quali
applichi. Parti da ask-matt per verificare il percorso e da grill-with-docs per il lavoro
di design; quest'ultima richiede grilling e domain-modeling. Se una skill non è disponibile,
segnalalo e concorda il fallback, senza installarla o fingere di averla eseguita.
FASE 1 — Riconferma delle evidenze, senza modificare il runtime
1. Leggi AGENTS.md, PROJECT_STATE.md, CONTEXT.md, le istruzioni docs/agents/ su dominio,
issue tracker e label, gli ADR pertinenti e il PRD. Controlla HEAD, stato del worktree
e differenze dalla baseline della survey. Preserva tutte le modifiche preesistenti.
2. Riconferma i rilievi SEC-01…SEC-12 nel codice corrente. Separa fatti verificati,
ipotesi, rischi condizionati al profilo e problemi già risolti. Non trattare i vecchi
conteggi delle dipendenze come una scansione aggiornata.
3. Usa controlli locali read-only e dati sintetici. Per un difetto da riprodurre, usa
diagnosing-bugs con un segnale ripetibile sul comportamento effettivo; una diagnosi
non autorizza ancora il fix. Confronta fatti di librerie e advisory con fonti primarie
correnti quando necessario, senza inviare codice privato o segreti ai servizi di ricerca.
4. Non accedere o intervenire su server, IdP, DWH o provider reali senza aver concordato
target e operazioni. Non mostrare API key, cookie, password o campioni di dati reali.
Esito della fase: una matrice aggiornata che conserva gli ID dei rilievi, con evidenza,
profilo interessato e stato. Un fatto ancora non verificabile resta esplicitamente aperto.
FASE 2 — grill-with-docs, con me presente
5. Costruisci l'albero delle decisioni. Parti da profili di deploy, fiducia fra utenti e
condivisione dei dati; poi affronta isolamento di Pi, policy dei valori sensibili,
revoca, retention e limiti seguendo le dipendenze effettive.
6. A ogni round presenta soltanto le domande attualmente sbloccate, numerate, con la tua
raccomandazione e i trade-off. Attendi le mie risposte prima di assumere le decisioni
successive. Cerca autonomamente i fatti ricavabili dal repository; usa agenti di
ricerca mirati quando previsto dalla skill, senza delegare a loro le mie decisioni.
7. Aggiorna il PRD distinguendo proposte e decisioni confermate. Aggiorna CONTEXT.md solo
per termini realmente risolti. Proponi ADR soltanto per scelte difficili da invertire,
sorprendenti senza contesto e fondate su alternative reali: basta il formato minimo.
8. Concorda requisiti, priorità, rischi accettati e criteri misurabili, inclusi i tempi
di revoca e i limiti di risorse. Conferma con me i confini pubblici dei test prima
di scriverli. Mantieni espliciti i gate manuali già presenti in PROJECT_STATE.md.
Esito della fase: nessuna decisione bloccante lasciata implicitamente all'agente;
riepilogo e mia conferma della comprensione condivisa. Fino a quella conferma rimani
su analisi e documentazione: nessun cambiamento applicativo o di deployment.
FASE 3 — Spec e ticket, soltanto dopo mia conferma
9. Usa to-spec per sintetizzare le decisioni già prese, senza riaprire arbitrariamente
l'intervista. Chiedimi conferma della pubblicazione prima di creare la spec nel
tracker canonico Gitea indicato in docs/agents/issue-tracker.md, non nel mirror GitHub.
Collega la spec canonica dal PRD e rendi chiaro quale documento è la fonte aggiornata.
10. Usa to-tickets per proporre fette verticali verificabili autonomamente, dimensionate
per un contesto fresco. Collega ogni ticket ai requisiti e ai rilievi pertinenti,
indica i veri blocker e includi criteri positivi e negativi. Fai approvare granularità
e dipendenze prima di pubblicare. Solo i ticket approvati e completi ricevono
ready-for-agent; non rimetterli in triage e non chiudere automaticamente la spec padre.
11. Se una decisione richiede una prova eseguibile, proponi un prototype limitato a quella
domanda prima di fissare la spec. Usa wayfinder solo se il lavoro risulta realmente
troppo ampio e incerto per essere chiarito con grill-with-docs.
Esito della fase: spec approvata e ticket autosufficienti con dipendenze risolte o esplicite.
Chiedimi se autorizzo il primo ticket: l'approvazione del design non avvia da sola il codice.
FASE 4 — Implementazione futura autorizzata
12. Prima di modificare codice, concorda e crea un worktree dedicato, verificando percorso,
branch e commit base. Non riusare una directory occupata, non alterare il worktree
originario e non copiare automaticamente segreti o dati operativi. Assicurati che
PRD e prompt siano disponibili nel worktree attraverso un passaggio esplicito.
13. Esegui implement su un ticket sbloccato per volta, in un contesto fresco. Segui tdd
ai confini concordati: un test rosso sul comportamento, implementazione minima,
test verde. Esegui typecheck e test mirati durante il lavoro e le suite pertinenti
al termine; usa fixture locali per IdP, DWH e provider.
14. Esegui code-review sui due assi Standards e Spec, usando i due agenti previsti dalla
skill e una base Git fissata. Assicurati che il diff esaminato includa tutto il lavoro
del ticket, anche se ancora non committato; un diff vuoto non è una review superata.
Risolvi i rilievi e verifica di nuovo. Commit soltanto del lavoro pertinente nel
worktree autorizzato; push, merge e deploy richiedono un'autorizzazione distinta.
15. Consegna evidenze dei test, istruzioni di adozione e rollback, gate manuali pendenti
e rischi residui. Non dichiarare chiuso il PRD intero se è concluso soltanto un ticket
o se resta un'accettazione dell'owner.
Inizia dalla Fase 1, poi proponimi il primo round di grill-with-docs.
```
@@ -1,112 +0,0 @@
# X1 — validation of session archive corrections
Date: 2026-09-09. The joint Memory/Evidence repair increment is implemented.
The authoritative contract is [Session archive corrections](../contracts/archive-repair.md).
## Delivered behavior
The session gate shows complete before/after content for specific alternatives targeting
Memory or Evidence. The reviewer chooses one correction or rejects all proposals as
inadequate and requests reformulation. The resulting receipt survives interruption;
saved content and index activation are reported separately. Pending activation offers
retry of the same chosen operation. A subsequent curator change blocks replay.
Application requires an administrator in the harness and the responding browser
principal's archive-management permission. Cross-principal runtime responses cannot
misattribute the correction. A non-administrator can decline or continue the current
question without modifying shared archives. The gate does not advance a workflow phase.
Memory and Evidence remain separate domains. The integration coordinator reuses their
canonical persistence and activation operations. A session Evidence correction requires
a consolidated archive, preserves source lineage, and cannot publish unrelated external
edits. Existing administration, source import, dependency cleanup and final Memory review
remain available. No automatic Git commit or push was added.
## Verification
- Harness regression: **1,264 passed**, one skipped, five deselected. All **nine**
portable-path checks passed separately without `THT_HOME`. Python lint passed on changed modules.
- Backend: **1,366 passed**, 40 skipped. Tests include actual session-response routes
for both target archives, unauthorized response, malformed choice and runtime ownership.
- Frontend: complete suite **645 passed**; the final display adjustment passed all four
focused widget tests. Backend and frontend TypeScript checks passed.
- Pi extension: **199 passed**, including closed human choices, rejection, failure/retry,
forged selections and the updated public tool schema. The modular skill projection is
byte-identical to its updated approved template.
- PostgreSQL/Qdrant integration traverses the actual Python CLI for preparation,
application and recovery inspection. Corrected Memory and Evidence are retrieved from
real indexes, and Evidence activation preserves the Memory card. Session loading is a
controlled fixture and embeddings are deterministic; this is not an LLM quality test.
- Failure tests cover both targets, index outage, replay, later edits, workspace/session
isolation, changed session context, rejection, non-admin writes, and interruption after
the Evidence file write but before its saved receipt.
- Playwright desktop/mobile: **one passed**. The real widget renders both alternatives,
accepts an Evidence choice, displays pending activation and allows retry to active.
No page errors or mobile horizontal overflow. Screenshots are
`/private/tmp/thothii-x1-repair-desktop.png` and `/private/tmp/thothii-x1-repair-mobile.png`.
This browser fixture controls operation outcomes; persistent behavior is tested above.
- Strict MkDocs build and `git diff --check` passed.
## Local installation and reviewer acceptance
Core and frontend images were rebuilt from this worktree using the existing local
preview launcher. Migration `004_archive_repairs.sql` was applied to the existing
installation catalog. The new gate is available to session workflows; it is not an
always-visible administration panel. Existing PSD archive content was not changed by
the synthetic validation cases.
All five local services are healthy at `http://127.0.0.1:8080/`.
The technical increments and their planned checks are complete. The end-user acceptance
check remains a real session containing a meaningful domain conflict, with the reviewer
evaluating the proposed correction. Automated browser validation uses temporary accounts
and data, not the user's authenticated PSD session. Source import retains its E3
validation boundaries; no broader model-quality benchmark was added.
## Follow-up acceptance: configured model
The opt-in `test_real_model_proposes_a_reviewable_persistent_archive_correction`
passed with the installation's **zai/glm-5.3** model. Synthetic Memory asserted an
order-ID-only join; synthetic Evidence required the financial year too. The model
returned two schema-valid, specific alternatives with complete content and the exact
target revisions. The test reviewer selected Memory, persisted the correction through
the real coordinator and PostgreSQL, and retrieved the updated rule. Evidence stayed
unchanged. This test uses deterministic vectors and the configured completion helper;
it does not claim a full autonomous Pi session or human acceptance of PSD semantics.
The run log is `/private/tmp/x1-acceptance-model.log`. Reproduce with
`THT_MEMORY_L2_INSTALLATION=<installation.yaml>` and `THT_MEMORY_L2_CORE=<core-container>`
using `pytest -q -s -m l2 tests/memory/test_administration.py -k real_model_proposes`.
Credentials are resolved inside core and are not returned to the test runner.
## Follow-up acceptance: both administration pages
The opt-in `frontend/e2e/memory-real.spec.ts` passed through real authentication,
Fastify, ThtRunner, Python, isolated PostgreSQL and Qdrant. It verifies:
- Database management, Memory management and Evidence management appear as peers in
that order, with no active core session or DWH binding required.
- Memory creation, editing, persistence across backend restart, deletion and absence
from subsequent recall.
- Canonical Evidence remains intact after the Memory deletion. Its full rule is read
through the real Evidence administration worker; content filtering finds it and an
unmatched filter produces the empty state.
- Requests for an unregistered workspace return 404 for both archives.
- Desktop and mobile Evidence views render without horizontal document overflow.
On phones, both archive pages have at least 380px of usable width at a 390px viewport.
Navigation opens in the shared accessible dialog, closes with Escape or archive selection,
and returns focus to the trigger after Escape.
The temporary PostgreSQL readiness probe now waits for TCP, avoiding the image's
socket-only initialization server. The browser waits for Memory refresh to finish
before leaving its page, matching the existing navigation guard. Visual inspection
also exposed a real mobile layout issue: the fixed sidebar left only 134px for the
Evidence page. `ArchiveNavigation` now moves that sidebar into the shared dialog below
768px on Memory/Evidence pages. Desktop behavior is unchanged. The frontend image
was rebuilt for the local preview.
Run log: `/private/tmp/x1-acceptance-browser7.log` (**one passed**).
Screenshots: `/private/tmp/thothii-acceptance-evidence-desktop.png` and
`/private/tmp/thothii-acceptance-evidence-mobile.png`. Reproduce with
`THT_MEMORY_BROWSER_E2E=1 npx playwright test e2e/memory-real.spec.ts` from `frontend/`.
The fixture removes its temporary containers, accounts and checkout on completion.
The TypeScript check, Python lint, strict documentation build and diff check also pass.
@@ -1,67 +0,0 @@
# Evidence E1 — validation
Date: 2026-09-09. Scope: editable Curated Evidence v4 and the persistent local archive.
## Implemented behavior
- Parser, renderer, authoring output and normalization share the existing typed payloads.
Visible Markdown edits determine content for all eight kinds. Legacy v1–v3 conversion
is explicit and lossless, with errors for content that cannot be represented exactly.
- Manual declarations record the curator. A correction preserves the original document
as lineage, separately from the current declaration. No source hash is needed to
create a manual file.
- The local archive records baselines, immutable candidates, active revisions and
deletion/source suppression metadata. Unresolved review items and invalid edits block
consolidation. Missing archive directories are availability failures, not deletions.
- Activation failures preserve the previous active revision. Interrupted normalization
replays only unchanged input bytes; later operator edits survive recovery.
- Revision-checked correction methods reject stale workflow updates. Legacy preparation
and resolution cannot overwrite an initialized local archive; explicit import/refresh
integration is deferred to E3.
## Verification
The final harness suite excluding opt-in L0/L2 and portable-layout cases passed with
**1,180 tests** (58 deselected). All **9 portable-layout tests** passed separately with
`THT_HOME` unset. The dedicated real-Qdrant integration test passed, including the
optional 35-unit PSD probe. Ruff passed on the changed Evidence implementation and
tests, and the strict documentation build succeeded. No frontend or backend TypeScript
changes are part of E1.
The integration test uses an isolated Qdrant 1.18.2 container, the actual corpus
pipeline, semantic chunking, vector adapter and active Evidence searcher. Deterministic
three-dimensional embeddings isolate file/content correctness from model behavior.
It verifies that raw edits do not change recall, consolidation updates recalled content
and curator identity, a blocked candidate preserves prior recall, and deletions remove
recall. Existing schema and Memory records survive each operation.
All **35 PSD units** were copied from the owner's workspace into
`/private/tmp/thothii-e1-psd.bsW4cp`. Deterministic conversion preserved every ID, payload,
scope, provenance and review item. There were no unresolved review items. The optional
integration probe then indexed all 35 converted units and compared their complete ID
set to the original. It uses PSD's actual `max_chunk_chars: 5000`; a preliminary probe
at 4000 correctly blocked an oversized atomic unit.
Reproduce the isolated real-corpus probe after creating a converted workspace copy:
```sh
cd harness
THT_E1_PSD_COPY=/absolute/path/to/converted-copy \
.venv/bin/pytest -q -s tests/test_evidence_editable_integration.py
```
The environment variable is optional. Ordinary CI uses only synthetic Evidence. No
source refresh, external document download, DWH call or model request is involved.
## Delivery boundary
E1 is a core/library increment. E2 must add the installed manual consolidation command,
connect runtime source selection to the active local snapshot, and build administrative
list/filter/detail with real persistent host paths and manual Git instructions. E3
adds source acquisition and explicit refresh/conflict handling. X1 later wires deliberate
joint Memory/Evidence corrections into review gates.
The actual PSD Evidence checkout was not converted. The live Docker preview at
`http://127.0.0.1:8080` remains the previously deployed M3 stack, with no new Evidence
administration page. The corpus conversion and reindexing described here used copies
and disposable test resources.
@@ -1,86 +0,0 @@
# Evidence E2 — validation
Date: 2026-09-09. E2 is implemented locally and installed on the existing Docker preview.
E3 source imports/refresh and X1 deliberate Memory/Evidence workflow corrections remain open.
## Delivered behavior
- Independent **Administration → Evidence management**, after Memory, protected by
`evidence.manage`: complete typed content, provenance and original excerpts, review items,
pagination, search, kind/purpose/status and scope/source filters, sort, and refresh.
- Working-file states distinguish active, modified, new, removed, invalid, legacy and review
required. Detail shows the actual configured host path with copy controls. Instructions
cover external editing, all eight Markdown templates, consolidation and manual Git.
- Installed `tht workspace evidence consolidate --workspace <id> [--json]` uses a closed
maintenance envelope. First use converts legacy units. Validation, immutable candidates,
activation and retry run through the existing corpus pipeline without a DWH scan or Git.
- Runtime and ordinary preprocessing consume the active local snapshot. Unconsolidated
edits remain excluded. Catalog/Schema readiness is not advanced by this operation.
Clear preserves curated files, archive metadata and Memory; full preprocessing must
recreate the missing Reference/Schema derivations afterward.
- Immutable runtime lease filenames now identify rendered bytes as well as logical input
identity. This fixes upgrades colliding with old runtime files without changing Catalog
fingerprints or removing the checks against tampered files.
## Automated checks
The complete backend suite passed: **1,355 tests**, 40 skipped. The complete frontend
suite passed: **639 tests**. Both TypeScript checks passed. Native Go workspace operation
and CLI tests passed, including rejection of arbitrary consolidation flags. Ruff passed
for changed Python implementation and test files.
The harness run passed **1,248 tests**, with one skipped and five deselected. Its three
portable-path tests failed because that run deliberately set `THT_HOME` to the test
runtime; rerunning the portable tests with `THT_HOME` unset passed. The final focused
administration/path suite passed all 18 tests, including actionable migration errors and invalid
consolidation combinations rejected before cleanup or indexing.
The real-Qdrant integration test exercised the actual harness consolidation CLI with
deterministic embeddings: all 35 PSD units converted and indexed with stable identities;
active-only source selection; separate Schema and Memory canaries; Clear and rebuild
from the retained snapshot. Unit tests cover validation, saved-but-unindexed failure,
retry, browsing/filtering, no automatic Git, and no Catalog mutation from consolidation.
A temporary Git repository and bare local remote exercise the documented manual sequence:
edit, add and remove files, consolidate, inspect, stage the complete Evidence tree,
commit, push and clone. The clone retains changed content, additions, deletions, managed
metadata and an accessible active snapshot. No remote user repository was pushed.
## Installed preview
The existing Compose project is `thothii-18998cca7b0a`, at `http://127.0.0.1:8080`.
The persistent editable checkout is:
```text
/Users/mp/projects/ThothII/deploy/psd/evidence-registry/repo/psd-clinical/evidence
```
The original registry checkout was copied from its retained Docker volume. The original
author repository was not changed. Core and maintenance share a nested host bind for
`repo`; registry state/snapshots and all other existing data volumes were retained.
The installation descriptor includes the existing workspace bindings and the new
`evidence-host.yaml` override. The previous descriptor and native binary are backed up
at `/private/tmp/thothii-installation-before-e2.yaml` and `/private/tmp/tht-before-e2`.
The real installed command succeeded with **35 documents, 35 chunks, 35 changed, zero
removed**, using the configured embedding service and Qdrant. A second run succeeded
with **35 unchanged, zero changed**. Reading the actual archive from core returned
35 active units and no file errors. All five long-running services are healthy.
Only the Evidence stage ran. The strict documentation build and `git diff --check`
also passed. The stack launcher is `bash /private/tmp/thothii-memory-preview.sh`; keep its
worktree image-build override until this branch is integrated into the main checkout.
Browser verification reached the local login page. The saved administrator password
does not match the current account hash, so the authenticated visual check remains
manual. No account or password was modified. React interaction tests cover navigation,
detail, host paths, templates, filtering, pending activation and invalid files.
## Boundaries
There is no web content editor, watcher, automatic commit/push, or implicit source refresh.
The API exposes administration reads and consolidation; the archive's revision-checked
save/remove operations remain available for the later explicit workflow corrections.
These gates are not claimed as implemented by E2. Initialized local archives retain
structural/review checks but bypass the legacy fixed retrieval-evaluation fixture so
its old expected IDs cannot veto deliberate deletions. A general retrieval benchmark
is outside the agreed scope.
@@ -1,100 +0,0 @@
# Evidence E3 — validation
Date: 2026-09-09. Explicit source import/refresh and decisions are implemented. X1,
the integration of deliberate Memory/Evidence corrections into workflow gates, remains next.
## Delivered behavior
The independent Evidence page now offers **Sources and imports**. An operator copies
a specialist's draft into `evidence/incoming/`, then explicitly imports/refreshes.
Original local Markdown and configured HTTP/S3 sources use existing read-only adapters.
Acquisition retains raw bytes, source identity and versioned normalized documents.
The existing Pi authoring refiner prepares typed, editable v4 proposals.
Unchanged hashes skip refinement. All source acquisitions/refinements must succeed
before saving a new set of comparisons. Missing sources are recorded as unavailable,
never interpreted as permission to delete. No runtime lookup, ordinary consolidation
or preprocessing triggers remote refresh once the local archive is initialized.
The administrator sees current and proposed units, scope, content, excerpts, review
items and explicit retirement IDs. **Keep local Evidence** records the retained wording
as a manual declaration with original lineage. **Use proposed Evidence** adopts the
proposal and its source version. Both save and activate through the existing archive
and corpus pipeline; review items block adoption. Comparisons use optimistic checks
on affected file bytes. Interrupted decisions have a durable replay journal and retry
without reacquisition, while intervening external edits are preserved and reported.
Deleted IDs remain reserved. New model-generated identities from sources with curated
deletions are also conservatively suppressed; surviving IDs can still receive reviewed
updates. Deliberate new knowledge can be authored as a manual file. This mechanical
protection does not depend on the model detecting semantic duplication or contradictions.
Installed commands are `workspace evidence refresh` and `workspace evidence decide`,
alongside E2 consolidation. Decision envelopes carry a source identity, comparison
revision and keep/replace choice. Extra URLs, arbitrary paths, forged actors and unknown
fields are rejected at the public API/CLI boundary. HTTP requests bind the authenticated
curator. Source operations do not mutate Catalog readiness or run DWH/schema stages.
The Python source worker is internal; the workflow CLI's visible surface is preserved.
## Checks
- Complete backend suite: **1,359 passed**, 40 skipped. Complete frontend suite:
**641 passed**. Both TypeScript checks passed; native Go CLI/workspace tests passed.
- Harness regression run: **1,256 passed**, one skipped and five deselected, with
portable-path tests run separately without `THT_HOME`. All **24 focused import,
CLI-surface and portable-path checks** passed. These
cover import, unchanged refresh, access failure, missing source, manual correction,
keep/replace, deletion suppression, stale comparisons, failure/retry and interrupted
journal writes. Ruff passed on the changed Python implementation and tests.
- The real-Qdrant test traverses the actual harness source CLI with deterministic
refinement/embedding boundaries: import is absent from recall before a decision,
accepted content becomes searchable, refreshed proposals preserve active manual
corrections, replacement removes the former text, deletion remains absent after
another refresh, and unrelated Schema/Memory canaries survive.
- Source contract fixtures cover controlled HTTP and S3 identities, exact acquired
bytes, and acquisition call counts. Existing adapter tests retain transport/egress
coverage. The test does not claim to exercise a live S3 account.
- React interaction tests verify explicit refresh, comparison content, exact decisions,
saved-decision retry and failure feedback. Route tests cover admin authorization,
workspace isolation, strict inputs and principal attribution. Service tests verify
the trusted config file descriptor and absence of Catalog mutation.
## Local preview
Core/frontend were rebuilt for the existing `thothii-18998cca7b0a` stack. Its persistent
archive and data volumes are retained. The native `/usr/local/bin/tht` was updated;
the previous executable is at `/private/tmp/tht-before-e3`.
The installed refresh command ran against `psd-clinical` successfully: **35 unchanged
sources, zero changed, zero pending comparisons**. All 35 source hashes matched their
existing units, so this probe required no refinement and changed no active Evidence.
Source registry metadata was saved locally; no Git commit or push was performed.
A separate synthetic draft was passed to the configured Pi/model inside core. It
produced one domain proposal with one review item, which was not activated. The probe
exposed a deployment issue: Python wheel modules and Pi skills live in different
directories. The refiner now resolves resources through `THT_HARNESS_DIR`, with the
source-tree location as its development fallback; a regression test covers this layout.
The corrected installed worker was then exercised end to end in a temporary workspace
inside core, using the real configured Pi/model and a synthetic `incoming/orders.md`.
It returned success, one changed source, one comparison and one proposal with a review
item. The active snapshot remained absent. The temporary directory was removed on exit;
the probe did not open the DWH or activate an index. Strict docs build and
`git diff --check` also passed.
The in-app browser still showed the login page with the prior credential error.
Authenticated visual verification remains manual; no credentials were reset or retried.
## Operational instructions and boundaries
See [Import drafts and refresh sources](../contracts/curated-evidence-v4.md#import-drafts-and-refresh-sources)
for commands, local paths, review, retry and backup/Git requirements. Preserve the
complete Evidence tree, including source comparisons, journals and acquired versions.
Local activation and transfer to the remote Git repository remain separate operator steps.
No web content editor, automatic Git, background watcher, new job queue, general
retrieval benchmark or automatic source merge was introduced. Review/refinement is
sequential and bounded by per-source limits plus 200 documents/100 MiB per refresh.
New changed-source decisions and failure scenarios use isolated test data; live PSD
curated content was kept unchanged. X1 is not included in this increment.
@@ -1,96 +0,0 @@
# M2 — Ricerca ibrida e collegamenti
Data: 2026-09-09. Implementazione locale dell'incremento M2 del
[piano Memory approvato](2026-09-08-memory-management.md#piano-esecutivo).
## Risultato
La ricerca Memory ed exemplar usa embedding dense e BM25 in Qdrant. I filtri sono
applicati in entrambi i rami prima della selezione dei candidati. Il core espande
i collegamenti uscenti, deduplica, limita cicli e visite, riordina insieme i risultati
e restituisce il contenuto corrente verificato in PostgreSQL. Non usa un database
a grafo né una chiamata LLM per il riordinamento.
Il contesto fisico distingue database, schema, tabella e colonna e richiede che
corrispondano alla stessa dipendenza strutturata. Le card senza dipendenze valgono
per il workspace; un riferimento a un antenato fisico si applica ai suoi discendenti.
Ambito descrittivo e concetti possono restringere ulteriormente la ricerca tramite
`--filters`. La CLI impedisce di sostituire database/schema del runtime. Il dettaglio
dei limiti e della formula di ranking è nel [contratto operativo](../gestione-memory.md#hybrid-recall-and-links).
L'incremento conserva i confini dei gate correnti: F2 riceve chiarimenti di dominio,
gli exemplar restano consultativi. Un collegamento non autorizza a consumare una
famiglia diversa o una card fuori ambito. Il riepilogo finale, i nuovi gate e la
pulizia delle dipendenze dopo sincronizzazione fisica appartengono a M3.
## Transizione e recupero
La migrazione versionata `002_hybrid_projection.sql` aggiunge il formato delle
proiezioni. Le card M1 restano autorevoli e consultabili; le loro proiezioni dense
risultano pendenti e non possono alimentare il recall. Un retry esplicito o
`tht memory index -c <runtime.yaml>` costruisce dense e BM25 dal contenuto corrente.
Il formato della proiezione e la revisione della card sono verificati prima dell'uso.
Il salvataggio può aggiungere il vettore sparse mancante nella sola collezione
Memory. Non sostituisce configurazioni incompatibili e non modifica Reference.
Il rebuild esplicito ricrea anche una collezione Memory assente; il test elimina
la collezione, ricostruisce da PostgreSQL e verifica il ritorno della sola card conservata.
Fallimenti lasciano il lavoro di propagazione persistito e recuperabile. Nessuna
importazione da JSONL, sessioni storiche o vecchi payload Qdrant.
## Verifiche
| Controllo | Esito |
| --- | --- |
| Suite harness senza L0/L2, escluso il file dei percorsi portabili | 1.152 test passati nell'esecuzione finale. |
| Suite mirata Memory, adapter e CLI, con embedding reale | 78 test passati. |
| Verifica aggiuntiva del rebuild con collezione assente e adapter | 54 passati; il solo test del modello reale era escluso in questa riesecuzione. |
| API Fastify Memory | 13 test passati, compresa propagazione della lingua del workspace. |
| Browser amministrativo integrato | Passato: creazione, modifica, riavvio, rilettura, cancellazione e verifica del recall. |
| Wheel e casi CLI | 10 test passati; il wheel include entrambe le migrazioni Memory. |
| Build e controlli statici | Build/typecheck backend, Ruff sui file Python interessati e build documentale strict passati. |
- Test deterministici: collegamenti necessari, contenuto corrente, duplicati,
cicli, profondità e limiti, card mancanti o pendenti, rifiuti già registrati,
famiglie e ambiti esclusi, dipendenze omonime e filtri CLI vincolati al runtime.
- Adapter: stesso filtro nei prefetch dense/BM25 per Memory ed exemplar; restano
coperti i contratti Evidence esistenti.
- PostgreSQL/Qdrant: salvataggio, modifica, cambio famiglia, cancellazione,
ricostruzione, riferimento separato, isolamento, RLS, errori, retry e transizione
delle proiezioni M1 al formato ibrido.
- Recupero effettivo: client Ollama di produzione con il modello configurato
`qwen3-embedding:0.6b`, dimensione 1024, e Qdrant dell'immagine fissata in Compose.
La domanda sulla chiave commessa fra esercizi recupera la regola attesa e la
granularità collegata, escludendo un altro database, un altro ambito e dipendenze
che corrispondono soltanto combinando riferimenti distinti. Verifica separatamente
dense, BM25 e fusione, poi recall, cancellazione e rebuild.
Il test effettivo avvia un processo Ollama separato, montando il volume del modello
installato in sola lettura. PostgreSQL e Qdrant sono container temporanei dedicati,
eliminati a fine test. Non usa ID di risultati predisposti. Il browser amministrativo
usa invece embedding deterministici: verifica il collegamento fra UI, autenticazione,
Fastify, ThtRunner e persistenza, senza essere una misura di qualità del recupero.
Non è una valutazione generale della qualità semantica su un corpus di produzione;
verifica i casi di recupero richiesti da M2, con il percorso reale configurato.
## Riproduzione
Dalla directory `harness`, con Docker disponibile:
```sh
THT_HOME=/private/tmp/thothii-m2-test-home \
THT_MEMORY_TEST_OLLAMA_VOLUME=<volume-modelli-installazione> \
THT_MEMORY_TEST_MODEL=qwen3-embedding:0.6b \
THT_MEMORY_TEST_DIMENSIONS=1024 \
.venv/bin/pytest -q tests/memory/test_administration.py \
tests/memory/test_retrieval.py tests/memory/test_recall.py \
tests/test_qdrant_vector_store.py tests/test_solved_search_cli.py
```
Senza il volume esplicito, il solo test con modello reale viene escluso; gli altri
test restano eseguibili. Il volume deve contenere il modello indicato. Le immagini
Qdrant e Ollama del test sono lette da `compose.yaml`.
Consegna locale: non sono stati eseguiti deploy, migrazioni delle installazioni
attive o aggiornamenti remoti dell'issue tracker.
@@ -1,78 +0,0 @@
# Memory M3 — validation
Implemented on 2026-09-09 in the rapid-harbor worktree.
## Delivered behavior
- F8 presents an editable summary of proposed additions, explicit updates and links.
Only selected content is saved, including the optional solved-question exemplar.
- Proposals reference effective approved decisions. Exact existing content is reused.
Concurrent edits invalidate an update; manual identities and origins are preserved.
- Selected cards and links commit atomically. Durable receipts recover repeat delivery
and the gap before the session review marker. Finalization does not add Memory.
- F4/F6/F7 retrieve SQL rules and explained errors for the existing approval gates.
Retrieval is consultative and does not write an approval decision.
- Successful Catalog physical sync deletes cards with matching removed dependencies.
The same Catalog transaction marks pending Memory cleanup. Retry keeps the original
removals and does not rescan; deletion receipts and projection tombstones survive restarts.
- Migration 003 adds minimal review and physical-cleanup receipts.
## Executed checks
| Check | Result |
| --- | --- |
| Harness deterministic suite, excluding portable-path environment cases | 1,152 passed |
| Portable-path suite without the temporary THT_HOME override | 9 passed |
| Memory service/retrieval with isolated PostgreSQL and Qdrant | 41 passed, 1 optional real-embedding case skipped, 1 L2 case excluded |
| Pi gate suite | 195 passed |
| Backend full suite | 1,346 passed; one auth timing test exceeded 5 seconds under concurrent load |
| Isolated auth and Catalog route rerun | All 40 passed, including the timed-out case |
| Catalog PostgreSQL integration after adding atomic cleanup-marker coverage | All 5 passed |
| Frontend full suite | 635 passed |
| Chromium summary review, desktop and 390px mobile | Passed; no page errors or horizontal overflow |
| Configured real GLM 5.3 generation | Passed on synthetic PostgreSQL data |
| Backend/frontend production builds, modified Python lint, strict docs build | Passed |
The existing local Docker preview was rebuilt from this worktree, migration 003
was applied, and core/frontend were recreated with the existing persistent volumes.
The preview remains at `http://127.0.0.1:8080`.
The browser check uses the production widget in an isolated Vite fixture. It edits
the rule, declines the exemplar, submits only the selected card and checks responsive
layout. Gate tests separately verify request ordering through the production Pi
composition root; service and Catalog tests use real PostgreSQL. This is not a claim
of an automated complete live Pi conversation.
The L2 case retrieves an approved SQL rule, excludes a card bound to another database,
and asks the configured GLM 5.3 model to generate a query. Order IDs repeat between
financial years; the correct composite join returns 120 on the synthetic fixture.
The generated SQL is validated and executed in a read-only PostgreSQL transaction.
No real DWH rows are sent. Embeddings in this case are deterministic; the real
embedding/hybrid retrieval evidence remains documented in M2.
## Reproduction
From the harness:
```sh
THT_HOME=/private/tmp/thothii-m1-harness-home .venv/bin/pytest -q -m 'not l0 and not l2' --ignore=tests/test_portable_paths.py
.venv/bin/pytest -q tests/test_portable_paths.py
THT_HOME=/private/tmp/thothii-m1-harness-home .venv/bin/pytest -q tests/memory/test_administration.py tests/memory/test_retrieval.py -m 'not l2'
npm test
```
The optional generation case requires an installation YAML path and its running
core container. It resolves the model credential inside core without printing it:
```sh
THT_MEMORY_L2_INSTALLATION=<installation.yaml> THT_MEMORY_L2_CORE=<core-container> \
.venv/bin/pytest -q -s -m l2 tests/memory/test_administration.py -k real_model
```
From frontend: `npx playwright test e2e/memory-review.spec.ts`.
Screenshots are written to `/private/tmp/thothii-m3-summary-desktop.png` and
`/private/tmp/thothii-m3-summary-mobile.png`.
Evidence authoring and the joint X1 persistent Memory/Evidence conflict repair remain
outside M3. This increment does not infer knowledge from unexplained failures or
promise general improvements in SQL-generation accuracy.
+43
View File
@@ -0,0 +1,43 @@
# What ThothII does
ThothII helps turn a natural-language question into reviewed SQL. It is intended for
people who know the meaning of their data and need to make the assumptions behind a
query explicit. The model proposes; a human reviewer approves, corrects or rejects.
## From a question to a reusable result
The [eight-phase workflow](skills.md) clarifies the question, consults reusable knowledge,
selects relevant Evidence and database objects, prepares a query plan, and produces SQL
for review. Approved knowledge can be saved for later questions.
- **Workspace:** the domain context and its Evidence configuration.
- **Database catalog:** tables, columns, relationships and reviewed descriptions.
- **Evidence:** domain sources and rules with provenance that a reviewer can inspect.
- **Memory:** reusable clarifications, SQL rules, solved questions and explained errors.
- **Session:** the saved artifacts and decisions for one question, not a permanent chat transcript.
Resuming a session uses its saved state. Model output is a proposal, not a guarantee of
correctness: review the scope, assumptions, sources and SQL before relying on the result.
## What an installation includes
The Docker stack includes the web application, its runtime, a PostgreSQL metadata/Memory
catalog, Qdrant and the embedding service. A browser is the normal user interface; the
host `tht` command is used to configure and operate the installation.
The data warehouse and generative model endpoints are configured separately. Docker
does not supply their credentials, network access or domain data. A standalone installation
is therefore self-hosted, but is not automatically offline or independent of those services.
Review data-access permissions and the model-provider configuration before using real data.
## Choose your next step
- Install on Mac, Windows through WSL2, or Linux: [Italian](install/standalone-manual-it.md)
or [English](install/standalone-manual-en.md) manual procedure.
- Configure [display mode and language](install/shell-and-language.md),
[authentication](install/authentication-local.md) and [models](general/pi-configuration.md).
- Prepare [workspaces](operations/workspaces.md) and [databases](operations/database-management.md).
- Start a reviewed question using the [user guide](guida-utente.md).
The installation guides state the platform tests still to complete. Availability of a
procedure is not a certification that every target machine has been tested.
@@ -0,0 +1,165 @@
# Rilascio server ThothII embedded in Omics — 2026-09-14
## Esito
Il rilascio tecnico è stato eseguito il 2026-09-14 e i controlli automatici e
server-side descritti sotto sono superati. L'accettazione funzionale non è ancora
chiusa: lingua, tema, fullscreen, logout, ruoli e continuità SSE devono essere
provati da browser con account Omics autorizzati.
Finestra autorizzata dall'operatore e conclusa alle 16:17 CEST. Le ammissioni
ThothII sono state riaperte dopo il collaudo (`active: false`, `admissions: 0`).
## Revisioni e immagini distribuite
- ThothII, checkout operativo `/srv/thothii-v2/source/ThothII`:
`49333a2d35664b7237c3ddc2a9f10a605dcc84ce`, branch `main`, pulito e allineato
a `origin/main`.
- Omics Portal, checkout `/home/chirone/omics_portal`:
`fca10901a73666ca257d8f4cc4b77066295c400a`, branch `master`, pulito e due
commit avanti a `origin/master`. Include la consegna funzionale
`95154e179144e2453b37ef2a63a65d6f377e4cf8`.
- CLI nativo `/usr/local/bin/tht`: commit
`49333a2d35664b7237c3ddc2a9f10a605dcc84ce`, build
`2026-09-14T15:08:47+02:00`, `linux/amd64`.
- Core: `thothii-v2-core:49333a2d`, image ID
`sha256:d62bf17dd1345e6a459edabe4b559333b396ef30c523dedf1b842506c147efbd`.
- Frontend: `thothii-v2-frontend:49333a2d`, image ID
`sha256:dd57745143fc282562ec6d4c2cd8fc493eb2078494eeb17099d49bea8eae218b`.
- Omics web: `omics_portal-web`, image ID
`sha256:11e99905c41b38cd68d0e726f25a4174b8eb65db27fb1d887238a7bd255074da`.
- Nginx: image invariata `sha256:b3c656d55d7ad751196f21b7fd2e8d4da9cb430e32f646adcf92441b72f82b14`.
## Configurazioni modificate
- `deploy/psd-server-v2/thothii-installation.yaml`: Installation Model Catalog
schema v2; shell `embedded`, locale predefinito `en`, adapter `omics-portal`;
provider DeepSeek unificato e default interaction `zai/glm-5.3`. Percorsi,
profilo server, workspace, DWH e provider reali del server sono stati
preservati.
- `/srv/thothii-v2/operator/compose.portal-upstream.yaml`: `AUTH_MODE=upstream`,
alias `thothii-core` e `thothii-frontend` sulla rete Omics; eliminato il mount
della copia manuale di `config.js`. Nessun `auth.yaml` e nessuna runtime auth
projection.
- `/srv/thothii-v2/operator/operator.env`: tag applicativo aggiornato a
`49333a2d`; nessun valore segreto copiato dal Mac o riportato in questo report.
- `deploy/psd-server-v2/generated/`: proiezioni rigenerate dal descriptor. Il
frontend riceve in sola lettura `generated/frontend/config.js`, che espone
soltanto `backendBaseUrl: /api` e il contratto shell embedded.
- Omics: integrati template embedded, lingua Django, topbar/fullscreen, adapter
JavaScript, traduzioni e test della consegna GitHub.
- `nginx/nginx.conf`: non modificato. La configurazione già presente conteneva
l'`auth_request` Django, derivazione server-side degli header `X-Thoth-*`,
rimozione di cookie/Authorization/header client, origin esatta e SSE senza
buffering.
Configurazione risolta verificata:
- core e frontend usano le immagini `49333a2d`;
- `AUTH_MODE=upstream`;
- core senza porta host pubblicata;
- frontend pubblicato soltanto su `127.0.0.1:18020`;
- alias Omics risolti rispettivamente a `thothii-core` e `thothii-frontend`;
- `config.js` generated montato read-only;
- `THOTH_PUBLIC_EXPOSURE=false` e storage sessioni locale, preservando la
topologia server già approvata.
## Backup e rollback
Backup protetto:
`/srv/thothii-v2/backups/20260914-pre-embedded-release`, directory `0700`, tutti
i file `0600` e owner `root:root`.
Contiene:
- immagini applicative precedenti core/frontend/Omics;
- binario CLI precedente;
- descriptor, override, config manuale e proiezioni precedenti;
- bind `data`, `workspace-registry`, `pi-state`, `operator` e `secrets`;
- snapshot raw dei volumi catalogo, Qdrant ed embedding;
- dump logico PostgreSQL del catalogo ThothII;
- dump logico PostgreSQL del database usato da Omics;
- snapshot dei volumi statici e media Omics;
- `SHA256SUMS`.
Tutti i checksum sono risultati validi. Gli archivi tar sono stati elencati
integralmente senza errori e i due dump sono stati validati con
`pg_restore --list`. Le vecchie immagini restano disponibili; Omics precedente
è inoltre etichettata `omics_portal-web:pre-fca1090-aff75817`.
Non sono state eseguite migrazioni ThothII: tra `82e2c91f` e `49333a2d` non
esistono nuove migrazioni catalogo, Memory o sessioni. L'entrypoint Omics ha
eseguito `migrate` con risultato `No migrations to apply`. Il rollback normale
è quindi applicativo e non richiede ripristino dati; dump e snapshot raw sono
conservati per un recupero separato solo in presenza di corruzione accertata.
## Verifiche superate
### Prima del rilascio
- frontend ThothII: 768/768 test;
- backend auth/config/session/model: 205/205 test mirati;
- estensione Pi, lingua e ripresa: 7/7;
- harness lingua sessione e repository PostgreSQL: 24/24;
- CLI Go: tutte le package superate;
- Omics embedded shell isolata: 15/15;
- build delle tre immagini candidate completata;
- generazione delle proiezioni validata prima in staging isolato;
- build documentale strict completata dopo la scrittura di questo report.
### Sul server distribuito
- `tht status`: exit 0;
- `tht doctor --json`: `ok: true`, 13/13 controlli superati, inclusi descriptor,
proiezioni, permessi, Docker/Compose, autenticazione, health, HTTP, registry,
workflow e Pi;
- core, frontend, catalog-db, Qdrant, embedding e Omics web in stato healthy;
- `nginx -t` superato prima e dopo la ricreazione;
- catalogo Superset Omics valido: 32 dashboard;
- `migrate --check` post-rilascio: exit 0;
- nessun traceback, fatal, panic, HTTP 500 o errore nginx nei log recenti;
- pagina senza sessione: `302` verso `/accounts/login/`;
- `/datamart-builder/api/me` senza sessione: `403`;
- richiesta pubblica con header principal/admin falsificati: ancora `403`;
- `config.js`: `200`, `Cache-Control: no-store`, contenuto embedded corretto;
- manifest Vite risolto da Django: 368 entry; entrypoint corrente
`index-ktEFlKPi.js` e stylesheet `index-BtNkA4QL.css`;
- asset JavaScript attraverso `/datamart-builder/assets/`: `200` e cache
`public, immutable`;
- il core non ascolta sulla porta host 8787; dalla rete interna senza principal
risponde `401`;
- nginx risolve i nuovi indirizzi degli alias, senza dipendere dai precedenti IP
Docker.
Il probe HTTPS verso l'hostname pubblico, eseguito dal server stesso, è andato
in timeout prima della connessione (`HTTP 000`): è un limite di raggiungibilità
hairpin/rete e non viene contato come verifica superata. I probe equivalenti
attraverso nginx locale con Host e forwarded protocol reali sono invece passati.
È rimasto intenzionalmente intatto il container orphan storico
`omics_portal-web-run-a0bc36025e52`, creato circa tre mesi prima del rilascio ed
exited da due settimane; non è stato rimosso perché estraneo alla consegna.
## Prove manuali ancora necessarie
Usare account di prova autorizzati e dati non operativi:
1. utente Omics autorizzato apre Datamart Builder senza secondo login e vede un
solo header, quello Omics;
2. `/datamart-builder/api/me` restituisce issuer `portal`, subject Django stabile,
ruoli corretti e `session`/`csrfToken` null;
3. confronto utente normale/amministratore e rifiuto utente senza capability;
4. IT/EN prima dell'apertura e cambio tramite form Omics, senza tradurre SQL o
contenuti authored;
5. light/dark con popup, menu e griglia aperti;
6. ingresso/uscita fullscreen, compresa uscita con Esc e rifiuto browser;
7. logout Omics, seconda scheda e nuova verifica `/me`;
8. nuova sessione, flusso SSE, riconnessione dopo scadenza, reload senza avvio
automatico e ripresa con `interaction_language` invariata;
9. richiesta cross-origin autenticata su operazione fittizia e comportamento
distinto di un singolo `403` operativo;
10. accesso HTTPS reale da una postazione client, perché il server non raggiunge
l'hostname pubblico in hairpin.
L'installazione non va dichiarata funzionalmente accettata finché questa matrice
manuale non è stata eseguita e registrata.
@@ -0,0 +1,142 @@
# Rilascio coordinato ThothII / Omics — 26 settembre 2026
**Distribuito; accettazione automatica superata. Collaudo browser positivo per accesso, interfaccia e avvio/interruzione/ripresa sessione; prove estese residue sotto.**
Finestra esplicitamente confermata dall’utente in chat («confermo»), avvio alle
19:28 Europe/Rome. Riferimento: [piano approvato](2026-09-26-server-release-plan.md).
Maintenance disattivata alle **19:35:27** dopo tutti i controlli automatici.
## Versioni e stato finale
| Componente | Risultato |
| --- | --- |
| ThothII sorgente | Main `497ab84031e285464fdbb73e6e0ce9252687e3ab`, checkout pulito in `/srv/thothii-v2/releases/497ab84031e285464fdbb73e6e0ce9252687e3ab` |
| Core | `thothii-v2-core:497ab840-preflight`, ID `sha256:182f4e0d63404cb7ed6a0e272441e9c55ed27e2bb16fdbf4d7162f892ac76dc7`, healthy |
| Frontend | `thothii-v2-frontend:497ab840-preflight`, ID `sha256:5f5a4f81139432d0ac41025c3006edaf1258b623ce388242700b1b20fcce6421`, healthy |
| Omics checkout | Fast-forward a `928f7e9fff2aba895416776fecf5668ee957d237`; nessuna modifica tracciata, file locali preservati |
| Omics web | Stesso container e immagine `sha256:27aa100690b110bf596322b1b0a31acc178ca9d1c709bc10382cbeecbfb5aeca`, riavviato e healthy; risposta HTTP Django verificata |
| Omics nginx | Ricreato col fix asset e immagine precedente fissata `sha256:b3c656d55d7ad751196f21b7fd2e8d4da9cb430e32f646adcf92441b72f82b14`; `nginx -t` positivo |
| Supporti | Catalogo PostgreSQL, Qdrant e Ollama healthy; immagini e volumi invariati |
| CLI host | `/usr/local/bin/tht` aggiornato; SHA256 `0d39a93fb0a3b2147541a746c75832db20302f3a604bf6510da69d3731bcd783` |
| Maintenance / Pi | Maintenance inattiva, zero processi Pi RPC al controllo finale delle 19:36 |
Descrittore mantenuto nel percorso originale:
`/srv/thothii-v2/source/ThothII/deploy/psd-server-v2/thothii-installation.yaml`.
Project Compose sempre `thothii-7f901b48fe35`; shell embedded/en/omics-portal,
auth upstream e storage locale invariati. Cambiati solo projectDirectory e
percorso dell’overlay git-ssh nel descrittore, tag immagini in env/overlay.
Proiezioni rigenerate col nuovo CLI e UID 10001. Descriptor/env 10001:10001 0600;
overlay 1013:1014 0640; CLI root:root 0755.
Il vecchio checkout ThothII dirty resta al suo posto per rollback. Nessun reset,
force push, eliminazione di volumi, cambio identità, credenziali, DWH o Authentik.
Nessun push Omics eseguito. Nessuna nuova migrazione applicata; i controlli Django
prima e dopo il riavvio non rilevano migrazioni o modifiche dei modelli pendenti.
L’entrypoint web ha rieseguito i normali comandi di startup, statici e traduzioni.
## Backup ed esecuzione
Backup protetto root 0700:
`/srv/thothii-v2/backups/20260926-coordinated-release`.
Completato e **VERIFIED alle 19:31:57**, circa **2,89 GiB**, **23 checksum**,
archivi tar leggibili e indici dei dump verificati. Non è una prova di restore.
Include configurazioni/CLI/generated, sorgenti e Git con file locali,
immagini precedenti, bind data/registry/Evidence/Pi/segreti, volumi
catalogo/Qdrant/Ollama/static/media, dump catalogo e dump PostgreSQL condiviso.
Il DB condiviso è rimasto operativo: il suo dump è una snapshot transazionale,
non un backup raw del database fermo. Il volume raw del catalogo ThothII è stato
archiviato a servizio fermo. Nessun restore dati eseguito.
Cronologia Europe/Rome:
- 19:28: controllo del piano approvato positivo; riserva 20,9 GiB, liberi 27,8 GiB.
- 19:29:21: configurazione di rollback salvata e verificata prima delle mutazioni.
- 19:29:42: maintenance e chiusura nginx Omics; due controlli Pi negativi.
- 19:30:03: applicazioni ferme; dump, fermo supporti e archiviazione dati.
- 19:31:57: backup verificato.
- 19:32:10: deploy avviato; core 19:32:17, frontend 19:32:23,
web Omics 19:32:29, nginx 19:32:36.
- 19:35:27: accettazione automatica completata e maintenance disattivata.
Il periodo tra chiusura e riavvio nginx è stato circa tre minuti; le nuove
ammissioni sessione sono rimaste bloccate fino al completamento dei controlli.
## Correzione del controllo durante il rilascio
L’accettazione iniziale si è fermata su un falso negativo nello script:
`config.js` invia due header `Cache-Control`, `no-cache` e `no-store`; il controllo
leggeva solo il primo. Configurazione e risposta HTTP erano corrette.
Riproduzione isolata rossa, correzione di una riga con `get_all`, **16 test verdi**,
compreso il rifiuto quando `no-store` manca davvero. Nessuna modifica applicativa
necessaria. Maintenance mantenuta attiva fino alla ripetizione completa
dell’accettazione. Non sono stati ripetuti backup, fast-forward o ricreazione.
Directory script/evidenze:
`/srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/`.
- Script inizialmente approvato conservato in `release.approved.py`, SHA256
`eb2c677c8adbee0c9febf919001f654c8535e71f064d9fb7c7883afd02383a41`.
- Script corretto `release.py`, SHA256
`20314e68a6a42701456d454d2a0881652cfdbe5e00e9a912a22df9e78129aa00`.
- Test di regressione: `test_release_reviewed.py`, `reviewed-tests-cache-fix.log`.
- Stato finale filtrato: `deployed-evidence.json`; avanzamento nel
`journal.jsonl` del backup. Manifest degli artefatti aggiornato, originale conservato.
## Accettazione automatica
| Verifica | Esito |
| --- | --- |
| Digest reali dei container, readiness, doctor | Positivi |
| Alias Docker | Univoci e risolti ai container attuali; manifest raggiungibile da Omics |
| API senza cookie e con principal falsificati | 403 su HTTP e HTTPS locale verificato |
| Bypass asset e traversal codificati | Negati; `/datamart-builder/assets/api/me` restituisce 404 |
| Config | 200, embedded e no-store su entrambi i percorsi |
| Asset | Tutti i file elencati dal manifest disponibili su HTTP/HTTPS |
| Esposizione core | Nessuna porta pubblicata sull’host |
| Modelli, Pi e credenziali | Hash invariati rispetto alla baseline |
| Documenti persistiti | 2 manifest sessione, 51 file sessione/artifact e 221 file workspace/Evidence confrontati col backup: nessuna differenza |
| Migrazioni e catalogo Omics | Nessuna migrazione pendente; catalogo Superset valido |
| Log startup | Zero occorrenze nelle categorie fatal/config/auth/permessi controllate; nessun contenuto sensibile riportato |
Le prove HTTPS usano la CA interna e risoluzione locale del nome pubblico;
non sostituiscono il collaudo dalla postazione esterna. Il controllo degli hash
riguarda i file elencati, non certifica da solo l’intera semantica degli archivi.
## Rollback disponibile
Usare lo script corretto, dopo controllo di eventuali sessioni Pi attive:
```bash
sudo -n python3 /srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/release.py rollback --window-confirmed
```
Il rollback previsto dalla finestra approvata ripristina CLI/config/proiezioni e
immagini ThothII precedenti, mantiene il fix proxy Omics, riavvia e verifica le
applicazioni. Conserva dati e volumi; nessun restore del DB condiviso, reset Git
o ritorno al nginx vulnerabile. Se l’utente ha avviato sessioni, prima salvarle e
fermarle. Il rollback non è stato necessario né eseguito durante questo rilascio.
## Collaudo browser — aggiornamento 27 settembre 2026
Conferme dell’utente:
- Omics → Datamart Builder si carica senza problemi, in risposta alla prova di
apertura senza secondo login.
- Il 27 settembre: «tutto bene, compreso l’avvio e l’interruzione di una sessione».
Nel contesto dei controlli richiesti, registrato esito positivo per IT/EN,
tema, fullscreen/Esc e per avvio/interruzione di una sessione.
- Successivamente, sempre il 27 settembre: «ho anche ripreso una sessione interrotta.
tutto ok». Confermata anche la ripresa riuscita; il ciclo funzionale
avvio → interruzione → ripresa è collaudato dall’utente.
Il collaudo funzionale richiesto ha esito positivo. Su richiesta dell’utente,
le verifiche estese sono trasferite a un’attività successiva nel
[handoff del 27 settembre](2026-09-27-server-acceptance-handoff.md): lingua
persistita, identità/ruoli, logout, origine delle scritture, amministrazione,
reload e HTTPS esterno. Il documento contiene passi, responsabili, risultati
attesi e registro delle prove; nessuno di questi casi è dichiarato già superato.
La [matrice di accettazione](../testing/authentication-manual-acceptance.md)
resta il riferimento. Nessuna ulteriore modifica ai servizi è stata eseguita
per registrare il collaudo o preparare l’handoff del 27 settembre.
@@ -0,0 +1,246 @@
# Handoff — rilascio coordinato ThothII / Omics
**Rilasciato il 26 settembre 2026; accettazione automatica superata alle 19:35 Europe/Rome.**
Per riprendere, leggere il [report di esecuzione](2026-09-26-server-release-execution.md):
servizi avviati, maintenance inattiva, backup verificato e rollback disponibile.
La finestra era stata confermata esplicitamente dall’utente; non richiederla di
nuovo per completare il collaudo già autorizzato. Il 27 settembre l’utente ha
confermato accesso, controlli dell’interfaccia e avvio/interruzione/ripresa di una
sessione: collaudo funzionale positivo. Per le verifiche residue usare il
[nuovo handoff del 27 settembre](2026-09-27-server-acceptance-handoff.md), con
passi e registro delle prove. L’utente le ha affidate a un’attività successiva.
Usare lo script corretto documentato nel report: durante il rilascio è stata
corretta soltanto la lettura degli header Cache-Control ripetuti nel controllo.
Non rieseguire le fasi pre-deploy sullo stato già rilasciato.
## Snapshot storico della sospensione delle 17:40
Il resto di questo file conserva lo stato precedente alla ripresa e al rilascio.
Il report di esecuzione sostituisce le indicazioni operative e le attività residue
qui sotto; il [piano approvato](2026-09-26-server-release-plan.md) descrive le scelte.
## Prima azione alla ripresa
Leggere questo handoff, quindi `AGENTS.md`, `PROJECT_STATE.md`,
`docs/operations/server-codex-handoff.md`, `docs/install/authentication-upstream.md`
e `docs/testing/authentication-manual-acceptance.md`. Per l'inventario completo
e i digest delle immagini operative, leggere
`docs/reports/2026-09-26-server-release-preflight.md`.
Quel preflight è storico: la correzione proxy allora mancante è ora preparata e
testata **soltanto in isolamento**, come descritto sotto.
La prossima attività è **revisionare e completare lo script di rilascio in bozza**,
validare il piano senza mutazioni operative e presentarlo all'utente con backup
e rollback. Solo dopo la sua conferma eseguire le fasi operative e il collaudo.
## Richiesta dell'utente e confini
- Aggiornare ThothII e Omics secondo il runbook, preservando dati, workspace,
Pi, provider, modelli e credenziali già presenti sul server.
- Usare main ThothII includente `497ab84031e285464fdbb73e6e0ce9252687e3ab`;
conservare i progressi server Omics successivi alla consegna GitHub `fca10901…`.
- Conservare checkout sporchi e file locali. Nessun reset, force push, rimozione
volumi o `tht setup --complete`; non usare `codex/guided-standalone-install`.
- L'utente ha autorizzato preparazione, correzione e test isolati. Prima del
fermo/proxy/migrazioni/recreate operativi vuole vedere il piano risolto e
confermare la finestra. Il suo «procediamo, fai la tua parte» ha avviato la
preparazione, non approvato uno script ancora inesistente/incompleto.
- L'utente può fare il collaudo da browser: avvisarlo quando sarà il momento
e fornire prove precise. Non chiedergli password in chat.
- Ha autorizzato a cercare credenziali di `akadmin` / `mpancotti`: trovata solo
la presenza di `AUTHENTIK_BOOTSTRAP_PASSWORD` in
`/home/chirone/chirone-authentik/docker/.env`. Valore non mostrato, non copiato,
**nessun login tentato e validità attuale non verificata**. Nessuna password
mpancotti trovata. I due utenti sono locali Authentik, non LDAP.
## Stato operativo, invariato
- Checkout di lavoro `/home/chirone/Thoth`: main `497ab840…`, allineata Gitea;
il report `2026-09-14-server-embedded-omics-release.md` era già non tracciato.
Sono stati aggiunti solo i report locali di questa attività.
- Checkout ThothII operativo `/srv/thothii-v2/source/ThothII`: main
`b1723c34c467980d007094af078d966d460cde2b`, 30 file modificati + due non tracciati,
preservato integralmente. Le modifiche sono già recepite dalla nuova main,
che contiene anche le correzioni successive.
- Omics operativo `/home/chirone/omics_portal`: master pulito
`1cf7ea90a669a26bf3bc51749eab4d07981da472`. Comprende la consegna shell GitHub
`fca10901a73666ca257d8f4cc4b77066295c400a` e la correzione Superset successiva.
**Non ripetere l'integrazione e non tornare a fca10901.**
- Core ancora `thothii-v2-core:49333a2d-session-memory-fix`; frontend ancora
`thothii-v2-frontend:b1723c34-session-dialogs-20260914`; Omics web ancora
`omics_portal-web` image ID `sha256:27aa100690b110bf596322b1b0a31acc178ca9d1c709bc10382cbeecbfb5aeca`.
- Ultimo controllo: zero processi Pi RPC, maintenance `active=false`, admissions 0.
Ricontrollare alla ripresa e prima del fermo.
- Backup nuovo `/srv/thothii-v2/backups/20260926-coordinated-release` **non esiste**.
Backup storico 14 settembre verificato (14 checksum, tar e indici dump), non
un backup dello stato odierno, nessuna prova di restore eseguita.
## Installazione e preservazione comprovata
Descrittore effettivo, il cui **percorso va mantenuto** per preservare project identity:
`/srv/thothii-v2/source/ThothII/deploy/psd-server-v2/thothii-installation.yaml`.
Project `thothii-7f901b48fe35`, schema 2, profile server, embedded/en/omics-portal,
upstream, session storage local, `THOTH_PUBLIC_EXPOSURE=false` già preesistente.
Non cambiare modalità auth o disattivare controlli per far partire l'app.
Preservati e confrontati:
- default interaction `zai/glm-5.3`;
- `deepseek/deepseek-v4-pro`, `deepseek/deepseek-v4-flash`;
- `local-qwen/qwen3.6-35b-a3b`;
- embedding `ollama/qwen3-embedding:0.6b`, dimensione 1024;
- `catalog.json`, `pi/models.json`, `pi/settings.json`, `frontend/config.js`:
proiezioni nuove **identiche byte per byte** a quelle operative;
- Compose candidato: environment, reti, porte, secret/config mount e mount
persistenti uguali. Solo due bind di script versionati identici cambiano
percorso seguendo il nuovo checkout (`catalog-db-init.sql`, `embedding-model-init.sh`).
Il CLI rifiuta correttamente i segreti se eseguito da UID diverso da 10001.
Per diagnostica usare UID 10001 con gruppi operator/Docker e DOCKER_CONFIG
leggibile; non cambiare ownership dei segreti. Esempio verificato:
```bash
sudo -n setpriv --reuid=10001 --regid=10001 --groups=1014,988 \
env DOCKER_CONFIG=/srv/thothii-v2/operator/releases/20260926-coordinated/docker-config \
/usr/local/bin/tht \
--installation /srv/thothii-v2/source/ThothII/deploy/psd-server-v2/thothii-installation.yaml \
doctor --json
```
## Correzione proxy pronta, non applicata
Il proxy operativo è ancora vulnerabile: senza cookie,
`/datamart-builder/assets/api/me` con header `X-Thoth-Trusted-*` inventati
restituisce 200 e principal sintetico. API canonica restituisce 403.
Confermato anche via nginx HTTPS host con CA verificata e DNS locale.
Il test ha usato un soggetto inesistente, nessun dato reale o scrittura.
Causa: il prefisso pubblico degli asset inoltra alla radice del frontend,
che espone `/api/` e converte gli header Trusted in principal del core.
Correzione candidata Omics:
- branch `codex/thothii-assets-proxy-isolation`;
- commit locale **`928f7e9fff2aba895416776fecf5668ee957d237`**;
- parte da `1cf7ea90…`, nessun push eseguito;
- checkout stabile pulito:
`/srv/thothii-v2/releases/omics-928f7e9fff2aba895416776fecf5668ee957d237`;
- clone di lavoro: `/tmp/thothii-release-20260926/omics-source`;
- sei file cambiati: nginx, test statico, tre file di test dinamico, docs integrazione.
Il filtro accetta solo file Vite con hash ed estensioni previste, alla radice
o sotto `assets/` per compatibilità. Le route pubbliche config/asset rimuovono
Cookie, Authorization e tutte le famiglie di header identità; solo GET/HEAD.
Il frontend attuale emette file alla **radice**, non tutti in `assets/`:
il primo filtro eccessivamente stretto è stato corretto grazie al test reale.
Test completati:
- test dinamico nuovo riproduceva il difetto prima della correzione;
- **6/6** test della catena nginx Omics → nginx frontend reale → core/Django
sintetici, rete Docker interna, senza porte host o volumi operativi;
- verificati traversal codificati, canonical auth, Origin esatta, config,
tutti i file del manifest reale, diniego scritture alle route statiche;
- gli stessi **6/6** passano anche con l'immagine frontend precedente:
il rollback può e deve mantenere la correzione del proxy;
- suite Omics shell/auth/nginx/Superset **28/28** dopo la modifica;
- nessun container/rete `omics-proxy-check-*` rimasto al momento della sospensione.
Esecuzione test ripetibile, solo se necessaria per nuove modifiche:
```bash
cd /srv/thothii-v2/releases/omics-928f7e9fff2aba895416776fecf5668ee957d237
THOTHII_TEST_FRONTEND_IMAGE=thothii-v2-frontend:497ab840-preflight \
OMICS_TEST_IMAGE=omics-portal:proxy-isolation-tests \
OMICS_TEST_NGINX_IMAGE=sha256:b3c656d55d7ad751196f21b7fd2e8d4da9cb430e32f646adcf92441b72f82b14 \
python3 test_support/thothii/run_proxy_integration.py
```
## Artefatti pronti e bozza da revisionare
Directory stabile: `/srv/thothii-v2/operator/releases/20260926-coordinated`.
Contiene:
- `descriptor.next.yaml`: cambia soltanto projectDirectory verso la nuova main
e il percorso del suo overlay git-ssh; modelCatalog/auth/shell/workspace invariati.
- `operator.next.env`: cambia soltanto tag immagini in `497ab840-preflight`.
- `compose.portal-upstream.next.yaml`: aggiorna anche il tag frontend letterale,
che non seguiva la variabile del core.
- `baseline.json`: hash protetti dei file operativi per rilevare drift prima
della finestra; non contiene valori segreti.
- `compose-preservation.json`: esito positivo del confronto Compose candidato.
- `tht.next`: nuovo binario non installato; SHA256
`0d39a93fb0a3b2147541a746c75832db20302f3a604bf6510da69d3731bcd783`.
- `nginx.safe.conf`: copia della configurazione candidata testata.
- `proxy-green.log`, `proxy-rollback-test.log`, `omics-proxy-tests.log`.
- **`release.DRAFT.py`**: bozza appena scritta, **non revisionata, non compilata,
non eseguita neppure in modalità check**. Non lanciarla prima di una revisione
completa e della conferma della finestra per le fasi mutanti.
Originale della bozza: `/tmp/thothii-release-20260926/release.py`.
Non trattare il flag `--window-confirmed` come un'approvazione dell'utente.
Sorgenti ThothII pronti, main pulita e fetch Gitea con divergenza 0/0:
`/srv/thothii-v2/releases/497ab84031e285464fdbb73e6e0ce9252687e3ab`.
Vecchio checkout dirty lasciato al suo posto per rollback.
Immagini candidate già costruite, non distribuite:
- core `thothii-v2-core:497ab840-preflight`,
`sha256:182f4e0d63404cb7ed6a0e272441e9c55ed27e2bb16fdbf4d7162f892ac76dc7`;
- frontend `thothii-v2-frontend:497ab840-preflight`,
`sha256:5f5a4f81139432d0ac41025c3006edaf1258b623ce388242700b1b20fcce6421`.
Test ThothII già superati: 782 frontend, 152 backend su Node 24.16, 31 browser,
build/typecheck core/frontend, build CLI e generazione isolata, doctor attuale 13/13.
Log in `/tmp/thothii-release-20260926`. La sottodirectory `generation` contiene
copie sensibili protette root:root 0600 sotto directory 0700: non pubblicarla.
## Lavoro residuo e criteri per avanzare
1. **Revalidare solo ciò che può essere cambiato** durante la pausa: immagini,
SHA/stato checkout operativo, configurazioni contro baseline, processi Pi,
migrazioni Omics pendenti e raggiungibilità. Se c'è drift, preservarlo e
aggiornare il piano prima di proseguire.
2. **Revisionare lo script DRAFT**, inclusi escaping del controllo processi Pi,
gestione errori/parzialità backup, health/readiness reale Omics (il suo
healthcheck verifica solo catalogo, non Gunicorn), generazione con UID corretto,
digest image vs container, conservazione proprietari e dati, rollout/rollback
della correzione proxy. Validare sintassi e fase read-only solo dopo revisione.
Aggiungere prove HTTPS locali, risoluzione alias dopo recreate e controllo
log redatto: la bozza non copre ancora integralmente il runbook.
3. **Finalizzare scelta lifecycle Omics.** La bozza mantiene l'esatta immagine
web operativa perché l'unica modifica applicabile è nginx (shell/Superset
già presenti); fa fast-forward del checkout al fix, stop/start web per backup
consistente e recreate nginx. Lo start riesegue il suo entrypoint, compresi
migrate/compilemessages/collectstatic. Spiegare questa scelta nel piano finale.
Se si decide di ricostruire web, il suo `.dockerignore` è minimale: creare un
contesto pulito, aggiungere il catalogo Superset reale valido, escludere segreti.
4. **Finalizzare backup odierno.** La bozza salva immagini/code/config, ferma
ingressi e applicativi dopo maintenance e controllo Pi, crea dump catalogo
e PostgreSQL Omics condiviso, ferma supporti ThothII per snapshot raw coerenti,
archivia bind/volumi e verifica checksum/tar/indici dump. Valutare spazio e
interruzioni; il vecchio backup verificato non basta. Il DB Omics è condiviso
(`postgres` su `supabase-db`, app in `kokoro`, metadata in `chirone_meta`):
un restore dell'intero DB non fa parte del rollback ordinario.
5. **Presentare piano risolto, comandi, backup e rollback all'utente**, chiedendo
conferma della finestra. Solo allora installare CLI/config, generare proiezioni,
ricreare le app e applicare nginx. Nessuna migrazione nuova rilevata finora.
6. **Collaudare e poi coinvolgere l'utente.** Login unico, `/me` e ruoli,
IT/EN, tema/fullscreen, logout/seconda scheda, sessione fittizia con Pi/SSE,
stop/save/ripresa e lingua immutabile, diniego cross-origin autenticato,
Database/Memory/Evidence e accesso HTTPS da postazione esterna.
Rollback: ripristinare immagini ThothII, CLI, descriptor/env/override e
proiezioni salvati; preservare dati e **mantenere il fix proxy**, già testato
col frontend precedente. Ripristinare il vecchio nginx riaprirebbe il bypass.
Nessun restore dati automatico, reset Git o eliminazione volumi.
## Ambiente strumenti
La sandbox exec fallisce prima dell'avvio (`bwrap: loopback … Operation not
permitted`): i comandi sono stati eseguiti con `require_escalated` e motivazione.
Auto-review li ha consentiti; nessun rifiuto pendente. Non sono stati usati
subagenti. Skill applicate: `diagnosing-bugs`, `writing-for-agents` per questo handoff.
Nessun goal formale attivo. Per lo stato successivo alla ripresa, usare il piano revisionato collegato in apertura.
@@ -0,0 +1,184 @@
# Piano eseguibile — rilascio coordinato ThothII / Omics
Data: 26 settembre 2026. Ripresa del [passaggio di consegne](2026-09-26-server-release-handoff.md).
**Piano approvato dall’utente; esecuzione avviata il 26 settembre 2026 alle 19:28 Europe/Rome.**
Stato operativo e risultati successivi nel [report di esecuzione](2026-09-26-server-release-execution.md).
Il testo seguente registra la preparazione precedente alla conferma.
Nessun fermo, modifica di configurazione operativa, migrazione, ricreazione,
login di prova o cambio credenziali eseguito durante questa ripresa.
## Risultato proposto
- ThothII: distribuire core/frontend già costruiti dalla main
`497ab84031e285464fdbb73e6e0ce9252687e3ab`, conservando percorso del descrittore,
project identity, dati e configurazione server.
- Omics: fast-forward da `1cf7ea90a669a26bf3bc51749eab4d07981da472` al commit locale
`928f7e9fff2aba895416776fecf5668ee957d237`, che aggiunge il filtro proxy degli asset.
Conservare la stessa immagine e lo stesso container web: shell embedded e fix
Superset sono già operativi. Riavviare web dopo il backup e ricreare soltanto
nginx Omics, fissandone l'immagine all'ID esistente.
- Mantenere il fix proxy anche nel rollback: la configurazione precedente consente
il bypass documentato nell'handoff e non deve essere ripristinata.
L'entrypoint web rieseguirà `makemigrations`, `migrate`, controllo superuser,
`compilemessages` e `collectstatic`. Verificati `makemigrations --check --dry-run`
e `migrate --check`: nessuna modifica o migrazione pendente. L'utente che lo
script creerebbe automaticamente esiste già. Questi controlli saranno ripetuti
prima del fermo. Nessuna nuova migrazione di catalogo, Memory o sessioni emerge
anche dal confronto ThothII `49333a2d..497ab840`; nessun job di migrazione è previsto.
## Rivalidazione alla ripresa
| Controllo | Esito |
| --- | --- |
| Immagini/container operativi | Stessi ID e date di avvio registrati nell'handoff |
| Baseline descriptor/env/override/Pi/modelli/segreti | Tutti gli hash invariati |
| Checkout ThothII candidato e Omics candidato | SHA attesi, puliti |
| Checkout ThothII operativo | Sempre dirty; preservato, 30 modifiche e due file non tracciati |
| Checkout Omics operativo | Tracciati invariati; nuovi file locali sotto `docs/prd/.claude/`, preservati |
| Checkout di lavoro ThothII | Nuova directory locale `.claude/`, estranea al rilascio e preservata |
| Pi RPC | Zero processi, rilevamento effettivo in `/proc` |
| Maintenance | Inattiva; il contatore `admissions` del CLI è locale a quel processo e non certifica il drenaggio del server |
| Doctor | Positivo con UID 10001 e gruppi operator/Docker |
| Omics | Gunicorn/Django rispondono; catalogo Superset valido; nessuna migrazione pendente |
| HTTPS locale | Certificato verificato con CA interna e risoluzione del nome a 127.0.0.1; config 200, API anonima 403 |
| Alias Docker | Univoci; nginx risolve core/frontend/web; Omics legge il manifest |
| Compose candidato | Environment, reti, porte, mount persistenti/segreti invariati; cambiano immagini, contesti build e due script con contenuto identico |
| Descrittore candidato | Cambiano solo `projectDirectory` e il percorso dell'overlay git-ssh |
| Spazio | 27,8 GiB liberi; stima non compressa 12,6 GiB; riserva richiesta 20,9 GiB |
Il bypass asset operativo non è stato ritestato in questa ripresa: resta quello
accertato nell'handoff; il fix resta non distribuito. Le prove HTTPS locali non
sostituiscono l'accesso da una postazione esterna o un login reale.
## Script revisionato e verifiche
Percorso definitivo di preparazione:
`/srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/release.py`.
SHA256: `eb2c677c8adbee0c9febf919001f654c8535e71f064d9fb7c7883afd02383a41`.
La precedente `release.DRAFT.py` resta conservata e non va eseguita.
Correzioni principali:
- Parsing NUL degli argomenti Pi, rilevamento `--mode rpc` e `--mode=rpc`, esclusione
del processo di controllo; test reale in container isolato.
- Gate espliciti anche con Python ottimizzato; lock tra esecuzioni; controllo
dei digest dei tag, dei container e degli artefatti preparati.
- Baseline estesa a CLI, configurazione Omics, CA/proxy host e file locali.
- Checksum dedicati della configurazione di rollback, creati prima delle mutazioni;
journal delle fasi, file parziali conservati e nessun marker VERIFIED su errore.
- Preservazione proprietari/permessi, `.git`, file ignorati/non tracciati, ACL/xattr
negli archivi. Solo cache dipendenze ricostruibili escluse.
- Readiness HTTP di Omics, alias dopo ricreazione, controlli HTTPS con CA,
tutti gli asset del manifest e riepilogo log per categorie senza contenuti sensibili.
- Recupero `reopen` per rendere nuovamente raggiungibile una sessione Pi comparsa
durante il drenaggio: riapre solo nginx con il fix, senza fermare web/core/Pi.
Validazione: sintassi Python, **14 test isolati**, fase `check` read-only positiva.
I test coprono anche conferma mancante, backup parziale/corrotto, checksum con
path traversal, ownership, readiness HTTP e recupero senza stop di Pi.
Le fasi mutanti non sono state eseguite né rappresentano un restore provato.
Evidenze in `/srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/`:
`reviewed-tests.log`, `reviewed-check.log`, script e test; manifest protetti
`artifacts.json`, `baseline-extra.json`, `source-state.json`.
Restano valide le prove precedenti: 6/6 proxy con ciascun frontend nuovo/vecchio,
28/28 Omics, 782 frontend, 152 backend e 31 browser; nessuna modifica a quelle
implementazioni durante questa ripresa.
## Comandi risolti per la finestra
Riservare indicativamente **30–45 minuti**, da confermare dall'operatore: la durata
reale dipende soprattutto dal dump del database condiviso. L'interruzione interessa
Omics e ThothII; non vengono fermati Authentik, il database condiviso o il DWH.
La prima parte del backup (codice/config/immagini) avviene con le app ancora attive.
Eseguire ciascun comando solo dopo exit 0 del precedente, nella finestra approvata:
```bash
sudo -n python3 /srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/release.py check
sudo -n python3 /srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/release.py backup --window-confirmed
sudo -n python3 /srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/release.py deploy --window-confirmed
```
`--window-confirmed` registra l'intenzione del comando, **non sostituisce il consenso**.
Lo script contiene tutti i percorsi/ID e l'ordine Compose risolti; non usa pull,
build implicite, setup completo, reset o cancellazione volumi.
Sequenza concreta:
1. Rivalidare baseline, assenza Pi e spazio. Salvare e verificare configurazioni,
CLI, sorgenti e immagini correnti.
2. Attivare maintenance, chiudere nginx Omics, attendere e controllare due volte
Pi. In caso di sessioni attive fermare la procedura senza ucciderle.
3. Fermare Omics web/core/frontend, creare i dump, fermare i soli supporti ThothII,
archiviare bind e volumi. Verificare checksum, lettura tar e indici dump.
4. Solo con backup VERIFIED: fast-forward Omics; aggiornare descriptor/env/overlay;
installare CLI verificato e generare proiezioni con UID 10001. Controllare che
modelli, Pi, shell e credenziali restino identici.
5. Riavviare supporti; ricreare core/frontend con stesso progetto
`thothii-7f901b48fe35`; avviare lo stesso web Omics; ricreare nginx col fix e
immagine fissata. `--no-build --pull never --no-deps` limita il lifecycle.
6. Verificare health/HTTP, DNS, `nginx -t`, attendere 31 secondi, eseguire prove
automatiche HTTP/HTTPS e log. Disattivare maintenance solo dopo esito positivo.
## Backup e gestione delle interruzioni
Destinazione nuova, protetta root 0700:
`/srv/thothii-v2/backups/20260926-coordinated-release`.
**Non esiste ancora**: verrà creata nella finestra. Se esiste già, lo script si
ferma senza riutilizzare o cancellare il contenuto.
Contiene CLI/descriptor/env/override/generated, sorgenti e Git, configurazioni
Omics e proxy, immagini per ID, bind data/registry/Evidence/Pi/segreti,
volumi catalogo/Qdrant/Ollama/static/media, dump catalogo e dump PostgreSQL condiviso.
Il dump condiviso è una snapshot transazionale coerente mentre gli altri servizi
che usano quel DB continuano a operare; non è un backup raw a database fermo.
Il volume raw del catalogo ThothII viene copiato solo dopo averlo fermato.
Su errore: niente retry cieco né prosecuzione verso deploy. Leggere journal e stato
container. Prima di una mutazione ai servizi, questi restano operativi; dopo la
quiescenza possono rimanere fermi. Il manifest `rollback-config.json` permette
il rollback applicativo anche quando il backup dati è incompleto. Nessun restore
automatico dei dati; nessuna prova di restore dichiarata.
Se Pi compare dopo chiusura ingressi e prima del fermo applicativo:
```bash
sudo -n python3 /srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/release.py reopen --window-confirmed
```
Questo applica il solo proxy sicuro, mantiene le applicazioni e Pi accesi e lascia
maintenance attiva: l'utente può salvare/fermare la sessione. La procedura si arresta
poi per rivalutare backup parziale e baseline; non tenta automaticamente un nuovo
backup o deploy.
## Rollback applicativo
Se la nuova applicazione o i controlli falliscono, e non ci sono Pi da salvare:
```bash
sudo -n python3 /srv/thothii-v2/operator/releases/20260926-coordinated/reviewed/release.py rollback --window-confirmed
```
Ripristina configurazioni/CLI/generated e immagini core/frontend precedenti,
riavvia lo stesso web Omics, mantiene il commit e il proxy corretto, quindi ripete
il collaudo automatico. Ripristina UID/GID/mode originali. Non sovrascrive dati,
non ripristina l'intero DB condiviso e non torna al nginx vulnerabile. Non fa reset
Git. Rifiuta di interrompere Pi attivi. Dopo un backup parziale conserva gli
artefatti per analisi: non li presenta come backup completo.
## Collaudo dell'operatore dopo il rilascio
Avvisare l'utente quando i controlli automatici saranno verdi, poi verificare
con account autorizzati e dati fittizi:
1. Login unico Omics → Datamart Builder, `/me` con identità/ruoli corretti,
nessun secondo login; utente normale e admin coerenti.
2. IT/EN, tema, fullscreen/Esc e persistenza dopo reload.
3. Sessione di prova, eventi SSE, stop/save/ripresa e lingua immutabile.
4. Logout e seconda scheda; diniego cross-origin autenticato su risorse di prova.
5. Database/Memory/Evidence leggibili, layout previsto, HTTPS da client esterno.
Queste prove restano aperte e sono necessarie prima di dichiarare concluso il
rilascio, secondo la [matrice di accettazione](../testing/authentication-manual-acceptance.md).
@@ -0,0 +1,285 @@
# ThothII / Omics — preflight server del 26 settembre 2026
Stato: **rilascio fermo al gate di isolamento del proxy**. Nessuna finestra
richiesta o autorizzata; nessun servizio fermato, ricreato o aggiornato, nessuna
migrazione e nessuna modifica di proxy, descrittore, credenziali, DWH o IdP.
Questo documento è un inventario e piano parziale, **non un piano eseguibile
di rilascio approvato**. Applicare il runbook `docs/operations/server-codex-handoff.md`.
## Blocco verificato
Senza cookie/sessione Omics:
| Ingresso | Richiesta | Risultato |
| --- | --- | --- |
| Nginx Omics, localhost:8080 con Host pubblico | `/datamart-builder/api/me` | 403 |
| Stesso ingresso, principal normalizzato inventato | `/datamart-builder/api/me` | 403 |
| Stesso ingresso, header `X-Thoth-Trusted-*` sintetici | `/datamart-builder/assets/api/me` | **200, JSON `/me` con identità sintetica e permesso** |
| Nginx TLS host, HTTPS con CA verificata e DNS locale | API canonica / percorso alternativo | 403 / **200** |
Il soggetto di prova era `release-probe-nonexistent`, con flag admin falso;
non sono stati usati utenti reali o richieste di scrittura. La risposta alternativa
conteneva issuer/subject, ruoli, permessi e session/CSRF null: non era il fallback HTML.
Causa circoscritta: la location pubblica `/datamart-builder/assets/` inoltra
qualsiasi suffisso alla radice del frontend. Il suffisso `api/me` raggiunge quindi
`/api/me` del frontend, che converte gli header Trusted in principal del core.
Quel percorso non attraversa `auth_request`. La configurazione Omics operativa e
quella della consegna mantengono questo percorso; la nuova immagine frontend
conserva l'endpoint `/api/`. Un aggiornamento di ThothII da solo non corregge il difetto.
Riproduzione non mutante, exit 1 sul difetto:
```bash
python3 /tmp/thothii-release-20260926/evidence/check-proxy.py
```
Serve una revisione candidata Omics del proxy che impedisca l'accesso alle API
attraverso gli asset e rimuova gli header di fiducia dai percorsi pubblici.
Va preparata dal codice server `1cf7ea90…`, collaudata in isolamento includendo
varianti dei percorsi, asset/manifest/config e dinieghi, quindi inclusa nel piano
da approvare **prima** del reload operativo. Nessuna correzione è stata applicata.
Non indebolire auth o Origin, né cambiare le identità degli utenti.
L'origine pubblica via DNS, dal server, va in timeout (HTTP 000, curl exit 28).
Il probe TLS con `--resolve …:443:127.0.0.1` è invece riuscito. Resta da verificare
il percorso completo dal client esterno/bilanciatore; non è attestato dal probe locale.
## Revisioni e conservazione delle modifiche
- Checkout di consegna `/home/chirone/Thoth`: branch `main`, HEAD e `origin/main`
`497ab84031e285464fdbb73e6e0ce9252687e3ab`, fetch Gitea eseguito, divergenza `0 0`;
entrambi gli ancestor richiesti (`497ab840…`, `0d2e573e`) presenti.
- All'inizio non era pulito: solo `docs/reports/2026-09-14-server-embedded-omics-release.md`
non tracciato. Quel file è stato conservato. Questo report è un ulteriore output locale.
- Copia pulita per test `/tmp/thothii-release-20260926/source`, branch `main`, stesso SHA;
clone locale senza hardlink e senza importare il report non tracciato.
- Checkout ThothII operativo `/srv/thothii-v2/source/ThothII`: branch `main`,
HEAD `b1723c34c467980d007094af078d966d460cde2b`, 30 file modificati e due file
non tracciati. Nessun aggiornamento/reset/stash eseguito in questo checkout.
Dei 32 file locali, 29 sono identici alla main approvata; le differenze negli
altri tre sono le nuove correzioni di main a `AppShell.tsx`, al suo test di
gestione sessioni e al test visuale dello scroll. Non occorre ricostruirle a mano.
- Omics `/home/chirone/omics_portal`: `master`, pulito e coincidente col riferimento
locale `origin/master`, HEAD `1cf7ea90a669a26bf3bc51749eab4d07981da472`.
Non è stato eseguito fetch di master: questa coincidenza non attesta il master remoto attuale.
- Fetch esplicito del branch GitHub `codex/thothii-embedded-shell`: esattamente
`fca10901a73666ca257d8f4cc4b77066295c400a`; contiene `95154e179144e2453b37ef2a63a65d6f377e4cf8`
ed è già antenato di HEAD Omics. **Non ripetere il merge e non tornare a fca10901.**
Il commit successivo corregge la risoluzione delle dashboard Superset rispetto
alla lingua. Gli 11 file verificati nel container (shell/auth e correzione
Superset) hanno hash uguali al checkout operativo.
- Il branch incompleto `codex/guided-standalone-install` non è stato usato;
`tht setup --complete` non è stato eseguito.
## Immagini effettivamente operative, non candidate
| Servizio | Tag | Image ID |
| --- | --- | --- |
| core | `thothii-v2-core:49333a2d-session-memory-fix` | `sha256:ff4c435abd67c57e1e91e6e560dae73e67350ca499a5aedca3ffa517b9f59ee0` |
| frontend | `thothii-v2-frontend:b1723c34-session-dialogs-20260914` | `sha256:d2ed3dec42f8a56536ebbc8d74f13892d4c42c9a7f2fb67c476ff39f43fe7af9` |
| Omics web | `omics_portal-web` | `sha256:27aa100690b110bf596322b1b0a31acc178ca9d1c709bc10382cbeecbfb5aeca` |
| Omics nginx | `nginx:alpine` | `sha256:b3c656d55d7ad751196f21b7fd2e8d4da9cb430e32f646adcf92441b72f82b14` |
| catalog-db | PostgreSQL 17.6 bookworm | `sha256:f3bd19c606e442c3d7bdfa8002e03fe260a1023351e0ea4598032022b68dd6e3` |
| qdrant | v1.18.2 | `sha256:75eab8c4ba42096724fdcfde8b4de0b5713d529dde32f285a1f86fdcb2c9e50c` |
| embedding | Ollama 0.32.0 | `sha256:57f573b47f1f71ebb445789f279fe3e596a8beab182f7cf486db9205bad87c5a` |
Il frontend dichiara revision `b1723c34-working-tree`; core e Omics non hanno
label OCI revision. Perciò lo SHA esatto del sorgente baked del core non può
essere certificato dalla sola immagine: tag, ID e checkout sono evidenze distinte.
CLI operativo `/usr/local/bin/tht`, binario root:root 0755; non sostituito.
## Installazione, Compose e persistenza
Descrittore invariato:
`/srv/thothii-v2/source/ThothII/deploy/psd-server-v2/thothii-installation.yaml`.
Schema 2, profile server, file 0600 owner UID/GID 10001, projectDirectory
`/srv/thothii-v2/source/ThothII`, envFile `/srv/thothii-v2/operator/operator.env`.
Shell già `embedded/en/omics-portal`. Project Compose `thothii-7f901b48fe35`.
Ordine dei file applicativi, ricavato dalle label dei container:
1. `/srv/thothii-v2/source/ThothII/compose.yaml`
2. `/srv/thothii-v2/source/ThothII/deploy/compose.server.yaml`
3. `/srv/thothii-v2/source/ThothII/deploy/compose.git-ssh.yaml`
4. `/srv/thothii-v2/operator/compose.portal-upstream.yaml`
5. `/srv/thothii-v2/source/ThothII/deploy/psd-server-v2/generated/compose.models.yaml`
I supporti sono stati creati con i primi quattro file. Il percorso del descrittore
determina l'identità Compose nel CLI: mantenerlo anche usando in futuro una
directory sorgente pulita distinta. Non spostare il descrittore in un nuovo checkout.
L'override contiene un tag frontend letterale: il solo cambio di
`THTII_RELEASE_IMAGE_TAG` non aggiorna entrambe le immagini.
Core: `AUTH_MODE=upstream`, `THT_AUTH_CONFIG_FILE=/run/thothii-auth/upstream-disabled.yaml`,
nessuno dei due file auth (`auth.yaml`, `upstream-disabled.yaml`) presente;
directory host `/srv/thothii-v2/operator/auth`, nessuna runtime auth projection.
`THOTH_PUBLIC_EXPOSURE=false`, `THT_SESSION_STORAGE=local`: configurazione
preesistente, non modificata. Non è una prova di isolamento: il gate proxy è fallito.
Se si decide di impostare public exposure true, serve prima un piano separato
per storage sessioni PostgreSQL; non attivare l'overlay come falsa migrazione automatica.
Bind e archivi:
- `/srv/thothii-v2/data` → `/data`: settings, sessioni, artifact, indici e workspace secrets.
- Sessioni PSD: `/data/sessions/psd-clinical/{sessions,artifacts,indexes,memory}`;
snapshot catalogo `/data/sessions/psd-clinical/preprocessing/catalog-metadata.json`.
- `/srv/thothii-v2/workspace-registry` → `/data/workspace-registry`; workspace
`psd-clinical`, installation ID `psd-server-v2`. Evidence local archive
`/data/workspace-registry/repo/psd-clinical`.
- `/srv/thothii-v2/pi-state` → `/home/thoth/.pi`; auth provider read-only
`/srv/thothii-v2/secrets/pi-auth.json` → `/home/thoth/.pi/agent/auth.json`.
- Bundle `/srv/thothii-v2/secrets/thothii.secrets`; chiavi workspace SSH,
known-hosts e password catalogo in `/srv/thothii-v2/secrets/`, valori mai riportati.
- Generated catalog/Pi/frontend sotto il descrittore; `config.js` montato read-only.
- Catalogo e Memory autorevoli PostgreSQL nel volume
`thothii-7f901b48fe35_catalog-data`; indici derivati
`thothii-7f901b48fe35_qdrant-data`; modelli `thothii-7f901b48fe35_embedding-models`.
- Binding DWH diretto già operativo verso `host.docker.internal:5438`, utente
read-only `thoth_dwh_reader`. Nessuna query DWH o sincronizzazione avviata.
Omics: project `omics_portal`, file ordinati
`/home/chirone/omics_portal/docker-compose.yml`,
`/home/chirone/omics_portal/docker-compose.override.yml`, env `.env.docker`.
Volumi `omics_portal_static_volume` e `omics_portal_media_volume`; mount CA
`/etc/nginx/ssl/policlinicosandonato.it.fullchain.crt` read-only.
Database portale: `supabase-db`, database `postgres`, search_path `kokoro,public`,
endpoint host 5438. Non confonderlo con il DWH o col catalogo ThothII.
Il server PostgreSQL è condiviso: un eventuale restore dell'intero database
non è un rollback ordinario di Omics e richiede un piano separato.
Reti: core su `thothii-7f901b48fe35_thothii`, `omics_portal_omics_network`
(alias `thothii-core`) e `localllm_default`; frontend su prime due reti (alias
`thothii-frontend`). Nessuna porta host core; frontend `127.0.0.1:18020`.
Omics web solo porta interna 8000; nginx `0.0.0.0:8080`. TLS host nginx su 443
per `https://aritmolab.policlinicosandonato.it`, poi localhost:8080.
File host `/etc/nginx/sites-available/policlinicosandonato`, collegato in sites-enabled;
SSE canonico con buffering disabilitato e timeout 86400 su entrambi gli nginx.
La mappa Origin esatta HTTPS→HTTP è presente. Il tratto esterno non è verificato.
Inventario JSON filtrato completo (label, mount, reti, porte):
`/tmp/thothii-release-20260926/inventory.json`.
## Verifiche eseguite e candidati
- ThothII frontend: **782/782** test, build/typecheck riusciti.
- Browser isolato: **31/31** scenari visuali Playwright riusciti. Il primo
tentativo non aveva il binario Chromium; installato soltanto nello staging
`/tmp/thothii-release-20260926/browsers`, quindi suite rieseguita con successo.
- Backend: build/typecheck riusciti; **152/152** test mirati su Node 24.16 in
container senza rete o dati operativi. I tentativi host Node 23 fallivano
per Argon2 non disponibile; i primi container di test avevano UID/mount
incompleti. Il risultato valido è `backend-node24-tests.log`.
- Omics shell/auth/nginx isolati: **15/15**; regressioni Superset: **12/12**,
entrambe le esecuzioni con `--network none`, SQLite in-memory, nessun volume operativo.
- Omics operativo: `migrate --check` exit 0, catalogo Superset valido (32 dashboard),
`nginx -t` exit 0. Warning allauth deprecati presenti.
- CLI corrente: status exit 0, doctor **13/13**, maintenance `active=false`, admissions 0.
Il primo tentativo con sudo/root era rifiutato dal controllo ownership dei segreti.
Con UID 10001 e gruppi 1014,988, più DOCKER_CONFIG leggibile, nessun errore:
non mancavano credenziali e non sono stati cambiati permessi.
- CLI aggiornato costruito in `/tmp/thothii-release-20260926/cli/tht-linux-amd64`;
generazione riuscita su copie root:root 0600 in directory 0700
`/tmp/thothii-release-20260926/generation` (contiene copie sensibili, non pubblicare).
Proiezione frontend `/api`, shell embedded/en/omics-portal corretta.
- Immagine core candidata `thothii-v2-core:497ab840-preflight`:
`sha256:182f4e0d63404cb7ed6a0e272441e9c55ed27e2bb16fdbf4d7162f892ac76dc7`.
- Immagine frontend candidata `thothii-v2-frontend:497ab840-preflight`:
`sha256:5f5a4f81139432d0ac41025c3006edaf1258b623ce388242700b1b20fcce6421`.
Entrambe portano la revision OCI `497ab84031e285464fdbb73e6e0ce9252687e3ab`.
**Costruite, non distribuite**. Non è stata costruita una nuova immagine Omics operativa.
Log dei test e build in `/tmp/thothii-release-20260926/`; log Omics
`/tmp/omics-release-tests-20260926.log` e `/tmp/omics-release-superset-tests-20260926.log`.
Log browser: `/tmp/thothii-release-20260926/browser-tests.log`.
## Backup e rollback: stato e vincoli
Backup storico `/srv/thothii-v2/backups/20260914-pre-embedded-release`:
directory root:root 0700, file 0600. Verificati **14 checksum**, leggibilità di
sette archivi tar.gz e indice dei due dump con `pg_restore --list`: tutto exit 0.
**Non è stata eseguita una prova di restore e non è un backup dello stato odierno.**
Contiene anche dati condivisi/sensibili: accesso protetto, nessun contenuto mostrato.
Altri backup presenti: `20260914-session-memory-fix`, `20260914-session-dialogs`,
`memory-evidence-20260910`; non attestati dai controlli del primo backup.
Il nuovo punto di rollback andrà creato nella finestra, dopo gestione delle
sessioni in corso e quiescenza delle scritture dei due applicativi. Percorso
previsto `/srv/thothii-v2/backups/20260926-coordinated-release` (non creato).
Deve includere:
1. Binario CLI, descriptor/env/override/generated, checkout operativo dirty
completo o bundle Git + patch + file non tracciati, configurazioni Omics,
catalogo Superset runtime e configurazione nginx host; proprietari/permessi preservati.
2. Immagini attuali core/frontend/web/nginx per ID e checksum dell'archivio.
3. Bind data, registry, Evidence, Pi, secrets/settings e volumi static/media Omics.
4. Dump coerente del catalogo e backup portale con ambito esplicito rispetto
al database PostgreSQL condiviso; snapshot Qdrant/Ollama e, se richiesto,
snapshot raw catalogo **solo a database fermo**. Non archiviare un PGDATA live
come se fosse un backup consistente.
5. SHA256SUMS, elenco integrale archivi senza errori, `pg_restore --list` e
restore isolato prima di eventuali migrazioni non reversibili.
Fra il riferimento operativo core `49333a2d` e la main approvata non risultano
nuove migrazioni catalogo/sessioni; Omics `migrate --check` non segnala pendenti.
Rivalutare dopo l'eventuale nuova revisione proxy: nessuna migrazione autorizzata ora.
L'entrypoint Omics esegue anche `makemigrations`, `migrate`, creazione superuser,
`compilemessages`, `collectstatic`: non usarlo come test preliminare su dati operativi.
Il `.dockerignore` Omics è minimale: preparare un contesto di build pulito che
includa il catalogo valido ed escluda env/backup/segreti prima della build operativa.
Rollback applicativo previsto: ripristino della coppia core/frontend e Omics
sopra registrata, del CLI e dei file di configurazione/proiezione salvati,
con gli stessi project name, bind e volumi; ricreazione mirata delle sole app,
`nginx -t`, aggiornamento DNS/reload del proxy e collaudo accesso/manifest/SSE.
Non fare downgrade dati, restore dell'intero Supabase o cancellazioni di volumi.
**Il ritorno alla vecchia configurazione proxy ripristinerebbe il bypass noto:**
il rollback approvato deve conservare una chiusura di sicurezza verificata,
oppure mantenere indisponibile Datamart Builder fino alla correzione.
## Comandi ricostruiti e piano da completare
Queste funzioni ricostruiscono il lifecycle corrente; non sono state usate per mutazioni:
```bash
thoth_compose() {
sudo -n docker compose --project-name thothii-7f901b48fe35 \
--project-directory /srv/thothii-v2/source/ThothII \
--env-file /srv/thothii-v2/operator/operator.env \
-f /srv/thothii-v2/source/ThothII/compose.yaml \
-f /srv/thothii-v2/source/ThothII/deploy/compose.server.yaml \
-f /srv/thothii-v2/source/ThothII/deploy/compose.git-ssh.yaml \
-f /srv/thothii-v2/operator/compose.portal-upstream.yaml \
-f /srv/thothii-v2/source/ThothII/deploy/psd-server-v2/generated/compose.models.yaml "$@"
}
omics_compose() {
docker compose --project-name omics_portal \
--project-directory /home/chirone/omics_portal \
-f /home/chirone/omics_portal/docker-compose.yml \
-f /home/chirone/omics_portal/docker-compose.override.yml "$@"
}
# Sola diagnostica, con identità del proprietario dei file protetti:
sudo -n setpriv --reuid=10001 --regid=10001 --groups=1014,988 \
env DOCKER_CONFIG=/tmp/thothii-release-20260926/docker-config \
/usr/local/bin/tht \
--installation /srv/thothii-v2/source/ThothII/deploy/psd-server-v2/thothii-installation.yaml \
doctor --json
```
Prima di chiedere la finestra occorre risolvere il gate proxy, selezionare la
nuova revisione Omics, completare il contesto di build e il backup odierno,
e verificare il rollback che non riapra il bypass. La revisione ThothII pulita
può essere collocata in `/srv/thothii-v2/releases/497ab84031e285464fdbb73e6e0ce9252687e3ab`
senza modificare il checkout dirty; mantenendo invariato il percorso del
descrittore, il project name CLI resta uguale. Questo trasferimento e gli
aggiornamenti del descriptor/override non sono stati eseguiti.
Solo dopo questi prerequisiti presentare comandi finali risolti (nuovo CLI,
generazione, build/up mirati, proxy, verifiche e rollback) e chiedere conferma
della finestra. La sospensione attuale deriva dall'istruzione dell'operatore
e dal runbook: «Ferma il passaggio interessato se manca … un prerequisito;
non aggirare i controlli» e «Nessuna route diretta aggira il proxy».
Restano aperti tutti i gate reali con account Omics autorizzati: login unico,
ruoli/capability, tema/IT-EN/fullscreen, logout e seconda scheda, sessione di
prova e SSE/ripresa, rifiuto cross-origin autenticato, lettura amministrazione
e accesso HTTPS da client esterno. I test isolati non li sostituiscono.
@@ -0,0 +1,90 @@
# Handoff — verifiche estese ThothII / Omics
Aggiornato il 27 settembre 2026. L’utente ha chiesto di affidare a un’attività
successiva le verifiche residue e di pubblicare la documentazione del rilascio.
**Aggiornamento e collaudo funzionale conclusi con successo; matrice estesa aperta.**
Questo documento è il punto di ripresa per le sole prove ancora da registrare.
## Stato acquisito e confini
Leggere [PROJECT_STATE.md](../../PROJECT_STATE.md), il
[report del rilascio](2026-09-26-server-release-execution.md), la
[matrice di accettazione](../testing/authentication-manual-acceptance.md) e il
[contratto upstream](../install/authentication-upstream.md).
Il 26 settembre sono stati distribuiti ThothII `497ab840` e il fix proxy Omics
`928f7e9f`, conservando l’immagine web Omics. Backup verificato e rollback sono
registrati nel report. L’utente ha confermato accesso senza secondo login,
controlli IT/EN, tema, fullscreen/Esc e avvio → interruzione → ripresa di una
sessione. Le prove automatiche di health, proxy anonimo/header falsificati,
asset, TLS locale e preservazione dati/configurazioni sono passate.
Queste sono evidenze del rilascio, non una nuova attestazione dello stato live.
Non ripetere backup/deploy né attivare maintenance per questo collaudo. Usare
account autorizzati e sessioni fittizie; DWH read-only. Non modificare ruoli,
Authentik, credenziali, modelli, archivi o configurazioni per far passare una prova.
Un problema che richieda un nuovo rilascio va prima diagnosticato e pianificato.
## Prima azione alla ripresa
1. Rileggere le conferme nel report: non chiedere all’utente di ripetere il ciclo
funzionale già riuscito, salvo regressioni o cambio di versione.
2. Verificare in sola lettura revisioni/container e stato dell’installazione.
Distinguere eventuale drift dalla baseline pubblicata; preservare il lavoro
e le sessioni in corso. Il `check` dello script di rilascio si aspetta lo stato
**precedente** al deploy: non usarlo come controllo corrente.
3. Concordare con l’operatore browser e account già disponibili: autorizzato,
normale, amministratore e, se disponibile, senza capability Datamart Builder.
Accedere dal normale login Omics; non chiedere password/cookie/token in chat.
Se manca un profilo, registrare quel caso come non eseguito senza crearne uno.
**Completato quando:** sono registrati data, revisioni effettive, modalità
embedded/upstream, browser e disponibilità dei profili, senza dati personali.
L’agente può proseguire con le letture tecniche mentre attende l’operatore.
## Prove residue
Tutti i casi sotto partono da **non eseguito**. La colonna “chi” indica chi compie
la parte principale; l’agente prepara i controlli tecnici e registra i risultati.
| ID | Chi | Azione concreta | Risultato necessario |
| --- | --- | --- | --- |
| V1 — lingua persistita | Operatore + agente | Creare una sessione fittizia con UI italiana, annotarne `interaction_language` tramite il normale stato sessione, interromperla, cambiare UI in inglese e riprendere **la stessa** sessione. | UI inglese, lingua della sessione ancora italiana; domande/scelte nella lingua persistita, SQL e contenuti authored invariati. La ripresa generica già provata non chiude questo caso. |
| V2 — identità e ruoli | Operatore + agente | Aprire `/datamart-builder/api/me` dalla sessione Omics autenticata; confrontare profilo normale e admin con le capability attese. Provare una richiesta amministrativa di sola lettura con il profilo normale. Se disponibile, provare pagina/API con un account senza capability. | Issuer `portal`, subject Django stabile, ruoli coerenti, `session` e `csrfToken` null. Profilo normale senza accesso amministrativo anche lato server; account senza capability rifiutato. Registrare solo esiti e codici, non identità o payload completi. |
| V3 — logout e riconnessione | Operatore | Aprire due schede Omics/Datamart Builder con una sessione fittizia; fare logout in una, tornare nell’altra e provocare un ricontrollo con reload/riconnessione. Rientrare attraverso Omics. | La nuova richiesta `/me` e le nuove aperture SSE non riusano l’accesso scaduto; UI protetta rimossa al ricontrollo. Non richiedere la chiusura istantanea di uno stream già aperto: non è il contratto. |
| V4 — origine delle scritture | Agente, con login dell’operatore | Preparare una coppia di richieste autenticate equivalenti su una risorsa fittizia autorizzata: prima same-origin, poi con origine estranea, attraversando il proxy pubblico. Confrontare lo stato della risorsa prima/dopo. Usare un harness HTTP locale con credenziali solo in memoria o file protetto; non affidarsi a `fetch` per impostare manualmente `Origin`. | La scrittura same-origin riesce e quella cross-origin è negata senza mutazioni. Dimostrare che la seconda richiesta è autenticata: un 403 dovuto alla sola assenza di cookie non prova la difesa Origin. I test isolati già verdi sono evidenza complementare, non sostituiscono questo caso. |
| V5 — lettura amministrazione | Operatore autorizzato | Aprire Database, Memory ed Evidence e controllare disponibilità dei dati preesistenti, selezione, pannelli e scroll. | Viste leggibili, nessun errore e nessuna scrittura/sincronizzazione necessaria per aprirle. Annotare quale area è stata verificata senza copiare contenuti clinici. |
| V6 — reload e preferenze | Operatore | Con sessione selezionata e Pi fermo, scegliere lingua e tema dal portale e ricaricare. | Preferenze e selezione coerenti; i documenti si riaprono senza avviare automaticamente Pi o una nuova generazione. La lingua persistita della sessione resta quella originale. |
| V7 — HTTPS esterno | Operatore | Confermare se le prove precedenti sono state svolte da una postazione esterna al server. Se non attestato, aprire il portale dal client abituale attraverso il nome pubblico e verificare config, asset e connessione eventi. | Accesso HTTPS senza avvisi di certificato, mixed content o errori di rete. Annotare browser e tipo di accesso, senza IP personali. Il curl locale con CA e risoluzione a 127.0.0.1 non chiude questo caso. |
Per V4 preparare e rendere verificabile il probe prima di eseguirlo: deve agire
solo sulla risorsa di prova concordata e controllare l’assenza di mutazioni nel
caso negato. In assenza di credenziali utilizzabili localmente, lasciare il caso
non eseguito; non estrarre sessioni di altri utenti o cambiare le regole Origin.
I comandi e gli endpoint concreti vanno derivati dalla versione effettivamente
installata, non inventati a partire da questo elenco.
## Registrazione e criterio di chiusura
Aggiornare questa tabella dopo ogni prova, collegando evidenze redatte o una
conferma esplicita dell’operatore. Un caso parziale resta aperto per i profili o
scenari mancanti. In caso di difetto, annotare riproduzione, atteso/ottenuto e
revisione; correggere e riprovare il caso interessato prima di dichiararlo superato.
| Caso | Stato iniziale | Data / versione / evidenza |
| --- | --- | --- |
| V1 | Non eseguito | — |
| V2 | Non eseguito | — |
| V3 | Non eseguito | — |
| V4 | Non eseguito | — |
| V5 | Non eseguito | — |
| V6 | Non eseguito | — |
| V7 | Non eseguito | — |
**Chiusura delle verifiche residue:** ogni caso V1–V7 ha esito e prova registrati;
per dichiarare la matrice estesa superata devono essere tutti verdi. Un rinvio
esplicito o un prerequisito mancante va riportato come tale, non come successo.
Aggiornare quindi report di rilascio, questo handoff e PROJECT_STATE.md.
L’aggiornamento applicativo e il collaudo funzionale già confermati restano conclusi;
questo follow-up non richiede di reinstallare né di ripetere il rilascio.
@@ -0,0 +1,53 @@
# Knowledge archives: consolidated implementation evidence
Consolidated on 15 September 2026 from the M1–M3, E1–E3 and X1 records of 8–9 September.
This is historical verification evidence, not a claim that these test counts describe today's
tree or that every installation has passed acceptance. Original commands, detailed outcomes and
local deployment details remain under `docs/plans/` in Git commit
`5f3a7f5975b96fae1e1cdd0da08b4d60d41064cc`.
| Increment | Delivered boundary | Recorded verification |
| --- | --- | --- |
| M1 | PostgreSQL Memory authority, administration, durable indexing retry | 1,134 harness + 9 portable cases; 17 real PostgreSQL/Qdrant cases; 190 Pi gates; 632 frontend. One backend auth timeout passed its isolated rerun. |
| M2 | Dense/BM25 recall, scoped links and explicit projection rebuild | 1,152 harness; 78 targeted cases with real embedding; 13 Fastify API cases; 10 wheel/CLI cases. |
| M3 | Human Memory promotion, receipts and physical-schema dependency cleanup | 1,152 harness + 9 portable; 41 Memory integration, 1 optional embedding skipped, 1 L2 excluded; 195 Pi gates; 635 frontend; 5 Catalog integration. Backend timing case passed isolated rerun. |
| E1 | Editable file authority and explicit consolidation | 1,180 harness + 9 portable; real Qdrant including optional 35-unit PSD probe. No actual PSD authoring checkout conversion in E1. |
| E2 | Evidence administration, host CLI and pending activation recovery | 1,355 backend, 639 frontend, 1,248 harness; portable-path rerun passed with THT_HOME unset. Initial authenticated visual acceptance remained manual. |
| E3 | Draft import, explicit source refresh and protected manual corrections | 1,359 backend, 641 frontend, 1,256 harness; 24 focused checks. Synthetic filesystem/HTTP/S3 behavior, not a live S3-account certification. |
| X1 | Human-reviewed Memory/Evidence conflict correction and durable recovery | 1,264 harness + 9 portable; 1,366 backend, 645 frontend, 199 Pi gates; desktop/mobile widget probe and later real authenticated administration probe. |
X1's configured `zai/glm-5.3` probe used synthetic conflicting knowledge. It was not a full
autonomous Pi-session certification or acceptance of PSD semantics. Later administration
checks exercised real authentication and a disposable synthetic workspace, including persistence
across backend restart. Existing PSD archive content was not rewritten by those probes.
## Current authorities and repeatable checks
- [Memory implementation and storage](../gestione-memory.md), ADR 0018.
- [Editable Evidence](../contracts/curated-evidence-v4.md), ADR 0019.
- [Session corrections](../contracts/archive-repair.md).
- [Evidence lifecycle acceptance](../testing/evidence-lifecycle-test-plan.md).
- Harness tests under `tests/memory/`, Evidence integration tests, backend route/service tests,
Pi gate tests and frontend tests are the executable regression boundaries. Run portable-path
tests without a THT_HOME override; optional L2/provider tests require separate authorization.
Do not equate passing synthetic tests with real-provider quality, live S3 coverage, PSD-domain
acceptance or permission to migrate another installation. Those remain explicit operator gates.
## Catalog/model design records consolidated at the same time
The old metadata-catalog exploration and description-generation plan/spec are no longer
current-state authorities. PostgreSQL/Fastify ownership, installation bindings, description
generation and source-sampling constraints now live in ADRs 0001–0016,
[architecture](../architecture/overview.md), [Database Management](../operations/database-management.md)
and the [description acceptance checklist](../testing/2026-08-29-ai-catalog-description-generation-acceptance.md).
The installation-model plan is replaced by ADR 0013 and the
[current model catalog guide](../general/pi-configuration.md).
Preserve these outstanding questions rather than infer acceptance from implementation:
real provider/DWH acceptance recorded by the owner, licensing review of shipped optional models,
future dialect/multi-schema support, and any desired semantic aliases/value descriptions.
The former proposed catalog-to-core cutover and separate database permissions are already
represented by current ADR 0016 and `database.manage`; do not reopen them as unfinished steps.
The original description-generation plan referred to issue #4 for final delivery gates;
this consolidation neither changes nor closes its tracker state.
@@ -1,420 +0,0 @@
# Inventario delle funzionalità legacy di gestione metadati di ThothAI
Data dell'inventario: 2026-08-23
Ultima verifica rispetto a ThothII: 2026-08-31
Stato: **inventario storico verificato; non è una specifica dello stato corrente**.
Issue originaria: [mptyl/ThothII#6](https://git.tylconsulting.it/mptyl/ThothII/issues/6)
Fonte primaria: repository legacy annidato `Thoth/ThothAI`, commit
`55855de0f18e5cb4bc72f0a2ab0a7317995186dd`.
Le citazioni che iniziano con `/Thoth/ThothAI/` sono relative alla radice di quella copia legacy
fissata al commit indicato. Alla verifica del 2026-08-31 tutti i 68 riferimenti univoci puntavano a
file esistenti e a intervalli di righe validi.
> Questo documento conserva l'inventario e le evidenze degli anti-pattern di ThothAI. Le frasi di
> requisito nelle sezioni successive descrivono la baseline proposta il 2026-08-23; non prevalgono
> su ADR, contratti, codice o `PROJECT_STATE.md` correnti.
## Stato rispetto al codice corrente
### Fonti autorevoli correnti
Per capire cosa esiste oggi, usare nell'ordine:
- `PROJECT_STATE.md`, per lo snapshot operativo aggiornato;
- `CONTEXT.md`, per il modello di dominio corrente;
- il [piano accettato del Metadata Catalog](../plans/2026-08-26-metadata-catalog-from-thothai.md)
e il [contratto dello schema snapshot](../contracts/catalog-schema-snapshot.md);
- gli ADR del catalogo, in particolare
[0001](../adr/0001-postgres-metadata-catalog.md),
[0004](../adr/0004-fastify-kysely-metadata-catalog.md),
[0006](../adr/0006-separate-physical-and-logical-relationships.md),
[0007](../adr/0007-durable-authoritative-schema-synchronization.md),
[0008](../adr/0008-allow-manual-catalog-metadata-cleanup.md),
[0009](../adr/0009-use-one-sequential-description-generation-run.md),
[0010](../adr/0010-allow-bounded-real-source-samples-for-description-generation.md) e
[0011](../adr/0011-gate-source-samples-with-a-sensitive-data-flag.md);
- l'implementazione, soprattutto `backend/src/catalog/types.ts`,
`backend/src/routes/catalog-databases.ts`, `backend/src/routes/catalog-schema.ts`,
`backend/src/routes/catalog-description-generation.ts`,
`backend/src/routes/catalog-description-consolidation.ts` e
`frontend/src/shell/DatabaseManagementPage.tsx`.
### Disposizione della baseline storica
| Capacità inventariata | Disposizione al 2026-08-31 | Evidenza corrente o nota |
|---|---|---|
| Configurazione per workspace, secret separati e test connessione | **Adottata e implementata** | PostgreSQL interno, binding `postgres_direct`, `rest_api` o `ssh_tunnel`, secret write-only; ADR [0001](../adr/0001-postgres-metadata-catalog.md)–[0004](../adr/0004-fastify-kysely-metadata-catalog.md) e `backend/src/routes/catalog-databases.ts`. |
| Inventario fisico di tabelle, colonne, PK e FK | **Adottato e implementato** | La struttura osservata è distinta dai contenuti curati; `backend/src/catalog/types.ts`, `backend/src/routes/catalog-schema.ts` e ADR [0006](../adr/0006-separate-physical-and-logical-relationships.md). |
| Riconciliazione completa e osservabile dello schema | **Implementata; semantica storica parzialmente superata** | I durable Catalog Sync Runs sono autoritativi, fail-closed, atomici e richiedono conferma per diff distruttivi. Non esiste il lifecycle `drift/removed` proposto qui: la sincronizzazione riconcilia la membership e ADR [0008](../adr/0008-allow-manual-catalog-metadata-cleanup.md) consente anche cleanup manuale esplicito, superando ADR-0005. |
| Descrizione curata e Generated Description separate | **Adottata e implementata** | Tabelle e colonne espongono entrambi i campi in `backend/src/catalog/types.ts`; la copia selettiva AI → curato è in `backend/src/routes/catalog-description-consolidation.ts`. |
| Generazione AI selettiva con run, stato e log | **Adottata e implementata** | Un run asincrono installazione-wide, sequenziale, con eventi persistiti e senza resume automatico; ADR [0009](../adr/0009-use-one-sequential-description-generation-run.md) e `backend/src/routes/catalog-description-generation.ts`. I thread daemon legacy sono **esclusi**. |
| Campioni reali per la generazione e protezione dei campi sensibili | **Implementata come estensione correttiva** | ADR [0010](../adr/0010-allow-bounded-real-source-samples-for-description-generation.md) e [0011](../adr/0011-gate-source-samples-with-a-sensitive-data-flag.md): campioni bounded per colonne non sensibili e valori sintetici deterministici per quelle sensibili. |
| Proposte persistenti, diff e versioni dei testi AI | **Escluse** | Resta un solo Generated Description modificabile. La cronologia dei run è operativa: non conserva prompt, output grezzo, proposta per colonna o audit della decisione umana. |
| Relazioni fisiche | **Adottate e implementate** | Sono constraint immutabili con coppie ordinate di colonne; ADR [0006](../adr/0006-separate-physical-and-logical-relationships.md). |
| Relazioni logiche curate o inferite | **Differite/aperto** | ADR-0006 riserva un modello e un lifecycle separati; non sono ancora parte del catalogo corrente. |
| Alias semantici, descrizioni dei valori, sinonimi e concetti | **Differiti/aperti** | `PROJECT_STATE.md` li assegna a slice dedicate; non vanno dedotti dai campi fisici già implementati. |
| Scope AI, ERD Mermaid e documentazione aggregata | **Differiti/aperti** | Restano capacità legacy inventariate, non una feature corrente del Metadata Catalog. Un eventuale lavoro dovrà avere contratto e gate propri. |
| Export CSV e script SQL dei commenti | **Differiti/aperti; varianti insicure escluse** | Non risultano nella API corrente. Export di segreti, CSV incoerenti e SQL ricostruito da tipi incompleti restano vietati dagli anti-pattern sotto. |
| Inferenza euristica e validazione di relazioni candidate | **Differita/aperta** | Non va confusa con la sincronizzazione delle FK fisiche; dipende dal futuro lifecycle delle relazioni logiche. |
| Pubblicazione del catalogo al core/schema-linking/Qdrant | **Differita e richiesta come design gate successivo** | Il runtime NL→SQL continua a usare configurazione e annotations del workspace; il cutover è esplicitamente rinviato in `PROJECT_STATE.md`. |
| Sette motori database del legacy | **Baseline superata** | La prima versione corrente supporta PostgreSQL; l'aggiunta di altri dialetti è una decisione futura, non parità automatica. |
| GDPR e import da installazioni ThothAI | **Fuori dalla baseline iniziale; import differito** | GDPR resta escluso. Un eventuale import richiede una iniziativa idempotente e un cutover separati, non il riuso degli ID Django. |
| Django Admin, modifica manuale della struttura sorgente, password nel catalogo, duplicazione opaca dei database | **Esclusi** | La UI e le API correnti amministrano il catalogo e non eseguono DDL sul DWH esterno; binding e segreti hanno ownership separata. |
## Sintesi storica
La baseline di parità proposta il 2026-08-23 comprende un catalogo dei database, l'inventario
completo di tabelle, colonne e relazioni fisiche, metadati descrittivi modificabili, relazioni
logiche, introspezione dello schema, generazione AI di descrizioni, scope, ERD Mermaid,
documentazione ed esportazioni operative. La UI Django Admin è soltanto l'interfaccia legacy:
non è un requisito architetturale da riprodurre.
Il flusso AI per le descrizioni individuato come baseline è volutamente semplice:
1. l'AI scrive nel campo `generated_comment` della tabella o colonna;
2. l'utente seleziona gli elementi desiderati;
3. un'azione copia il testo generato nel campo descrittivo canonico, sovrascrivendolo.
Nel codice esaminato **non esiste un'azione inversa che copi la descrizione canonica in
`generated_comment`**. Le due azioni inverse presenti sulle colonne copiano invece il nome
originale nel nome espanso e viceversa. Non risultano proposal, versioni, diff o workflow di
approvazione: aggiungerli non sarebbe parità con ThothAI.
La parità deve essere di capacità e comportamento utile, non dei difetti del legacy. In
particolare non vanno replicate esportazioni di password, SQL costruito con identificatori non
quotati, job in thread daemon, incoerenze CSV e azioni admin non funzionanti.
## 1. Modello dati legacy
### Database
`SqlDb` contiene:
- identità e connessione (`name`, host, tipo motore, nome database, porta, schema, utente e
password);
- configurazione SSH e ambiente (`dev`, `test`, `prod`);
- lingua per database, scope testuale e JSON, ERD Mermaid, direttive e report GDPR;
- stato e log separati per generazione commenti tabella/colonna, introspezione, scope, ERD,
documentazione e GDPR.
Evidenze: `/Thoth/ThothAI/backend/thoth_core/models.py:298-350`,
`/Thoth/ThothAI/backend/thoth_core/models.py:355-439`.
I motori dichiarati sono sette: Informix, MariaDB, MySQL, Oracle, PostgreSQL, SQL Server e
SQLite (`/Thoth/ThothAI/backend/thoth_core/models.py:116-123`).
Il modello `Workspace` collega un solo `SqlDb`; contiene inoltre configurazioni degli agenti e
duplica parte degli stati operativi (`/Thoth/ThothAI/backend/thoth_core/models.py:731-754`,
`/Thoth/ThothAI/backend/thoth_core/models.py:847-877`).
### Tabelle e colonne
`SqlTable` contiene nome, descrizione canonica, commento generato e riferimento al database
(`/Thoth/ThothAI/backend/thoth_core/models.py:454-465`).
`SqlColumn` contiene nome originale, nome espanso, formato dati normalizzato, descrizione
canonica, commento generato, descrizione dei valori e indicazioni PK/FK testuali
(`/Thoth/ThothAI/backend/thoth_core/models.py:468-485`).
La cancellazione usa le cascade Django: database → tabelle → colonne e relazioni. Nei modelli
non sono definite unicità composte per database/tabella/colonna. Ogni database ha un solo
campo `schema`; tabelle e colonne non portano una propria identità di schema. Questi limiti
non devono diventare vincoli del nuovo catalogo.
### Relazioni
`Relationship` collega tabella e colonna sorgente a tabella e colonna destinazione
(`/Thoth/ThothAI/backend/thoth_core/models.py:488-503`). Il metodo
`update_pk_fk_fields` ricostruisce stringhe descrittive PK/FK sulle colonne coinvolte
(`/Thoth/ThothAI/backend/thoth_core/models.py:505-526`).
Il form admin verifica che le due tabelle appartengano allo stesso database e che ogni colonna
appartenga alla tabella selezionata
(`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:19-165`).
Per ThothII occorre distinguere:
- struttura fisica importata dal database, da trattare come inventario non modificabile;
- descrizioni e metadati semantici, modificabili con CRUD completo;
- relazioni logiche aggiunte dall'utente, modificabili con CRUD completo.
Questa separazione conserva le capacità utili del legacy senza permettere che la UI alteri o
falsifichi accidentalmente lo schema fisico osservato.
## 2. Capacità esposte dal Django Admin
La registrazione degli admin fornisce il CRUD Django standard per database, tabelle, colonne e
relazioni (`/Thoth/ThothAI/backend/thoth_core/admin.py:13-24`). Oltre al CRUD, le azioni
specifiche sono le seguenti.
### Database
L'admin di `SqlDb` espone:
- export/import CSV;
- scansione delle tabelle, creazione delle relazioni e introspezione completa;
- validazione FK e inferenza di relazioni candidate;
- test della connessione;
- duplicazione della configurazione;
- generazione commenti AI per tabelle e colonne;
- generazione sincrona o asincrona di scope, ERD e documentazione;
- report GDPR;
- export della struttura e script SQL dei commenti.
Evidenza: `/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:237-274`.
I campi di configurazione, contenuto e stato sono esposti in fieldset distinti
(`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:275-462`). In ThothII i segreti di
connessione non devono essere campi ordinari del catalogo né essere restituiti dalle API di
gestione metadati.
### Tabelle
L'admin delle tabelle permette export/import CSV, introspezione colonne, validazione e pulizia
PK/FK, copia del commento AI nella descrizione, generazione commenti sincrona/asincrona e
download dello script SQL (`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqltable.py:133-150`).
L'azione di applicazione AI opera solo sulle righe selezionate, ignora commenti generati vuoti
e sovrascrive direttamente `description`
(`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqltable.py:165-190`).
### Colonne
L'admin delle colonne permette export/import CSV, copia del commento AI nella descrizione,
copia bidirezionale fra nome originale e nome espanso, validazione FK e generazione AI sulle
colonne selezionate (`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqlcolumn.py:167-196`).
L'applicazione AI sovrascrive `column_description` soltanto per le colonne selezionate con
`generated_comment` valorizzato
(`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqlcolumn.py:219-245`). Le azioni inverse riguardano
esclusivamente i due campi nome
(`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqlcolumn.py:247-261`).
### Relazioni
L'admin delle relazioni offre il CRUD standard, filtri e ricerca, export/import e dichiara
un'azione per aggiornare i campi PK/FK
(`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:219-245`). L'ultima azione è però un
difetto: `RelationshipAdmin` non implementa il metodo dichiarato; esiste soltanto il metodo
statico sul model. La capacità da conservare è la ricostruzione coerente degli indicatori, non
l'azione admin rotta.
## 3. Introspezione e riconciliazione dello schema
Il factory del database manager associa i sette motori ai rispettivi adapter e gestisce anche
il tunnel SSH (`/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:77-227`).
L'introspezione legacy:
- riduce i tipi nativi a un insieme generico limitato, con fallback `VARCHAR`
(`/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:237-266`);
- legge e crea le colonne mancanti, ma non aggiorna tipo o commento di quelle già note
(`/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:269-351`);
- crea le tabelle mancanti e aggiorna la descrizione di quelle esistenti
(`/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:431-481`);
- legge le foreign key fisiche, crea al bisogno colonne mancanti e crea relazioni assenti
(`/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:484-641`);
- esegue l'intero processo nell'ordine tabelle → colonne → relazioni
(`/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:718-891`).
Il comportamento è additivo: gli oggetti scomparsi dal database sorgente non sono eliminati o
marcati come obsoleti. Per ThothII la capacità richiesta è una scansione completa e
riconciliabile, con provenienza, data dell'osservazione e stato degli oggetti rimossi o cambiati;
non la semantica incompleta del refresh legacy.
L'inferenza di relazioni candidate combina convenzioni sui nomi, individuazione euristica delle
PK e campionamento dei valori, con soglia di corrispondenza del 70%
(`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:605-801`). Deve rimanere una proposta
esplicita da convalidare, non essere confusa con una foreign key fisica.
## 4. Generazione AI delle descrizioni
### Tabelle
La generazione procede in batch da dieci, utilizza lingua e contesto del database, schema
osservato, descrizioni disponibili e fino a cinque righe di esempio; il risultato JSON aggiorna
soltanto `table.generated_comment`
(`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_table_comments.py:181-267`,
`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_table_comments.py:269-482`).
### Colonne
La generazione usa contesto della tabella, nomi delle colonne selezionate ed esempi reali, e
scrive soltanto `column.generated_comment`
(`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_column_comments.py:185-280`,
`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_column_comments.py:283-487`).
Provider e modello AI provengono dalla configurazione globale del backend; la lingua è invece
per database (`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/comment_generation_utils.py:156-219`).
### Comportamento di parità proposto il 2026-08-23
Il contratto funzionale minimo è esattamente questo:
- due campi per elemento: descrizione canonica e commento generato;
- comando AI su elementi selezionati o su un insieme più ampio esplicitamente scelto;
- scrittura/sovrascrittura del solo commento generato;
- azione separata, su selezione dell'utente, che copia il generato nel canonico;
- nessuna proposta persistente aggiuntiva, diff obbligatorio, approvazione multilivello o
versionamento del testo.
Non è stata trovata una copia `descrizione → commento generato`. Se si desiderasse in futuro,
sarebbe una nuova capacità, non parità legacy.
## 5. Scope, Mermaid e documentazione
### Scope
La generazione dello scope raccoglie nomi e descrizioni di tabelle e colonne, nome e lingua del
database, richiede JSON al modello e persiste sia il JSON sia una resa Markdown. Se il JSON non
è valido conserva il testo grezzo e svuota `scope_json`
(`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_db_scope.py:25-163`).
### ERD Mermaid
L'ERD è prodotto dall'AI usando tabelle, colonne, PK/FK e relazioni. Il codice estrae un blocco
Mermaid oppure usa la risposta grezza e la salva in `SqlDb.erd`, senza una validazione
sintattica o semantica preventiva
(`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/generate_db_erd.py:18-145`).
Il rendering attivo usa un servizio Mermaid locale: health check, POST a `/svg` o `/png`,
timeout e file temporanei
(`/Thoth/ThothAI/backend/thoth_ai_backend/mermaid_utils.py:29-72`,
`/Thoth/ThothAI/backend/thoth_ai_backend/mermaid_utils.py:281-464`). Il servizio Express espone
`/health`, `/svg` e `/png` ed applica un limite al body
(`/Thoth/ThothAI/docker/mermaid-service/src/server.js:13-23`,
`/Thoth/ThothAI/docker/mermaid-service/src/server.js:70-115`). È distribuito come servizio
Compose autonomo con health check
(`/Thoth/ThothAI/docker-compose.yml:184-201`).
La vista ERD renderizza il Mermaid memorizzato in SVG tramite tale servizio e poi elimina il
file temporaneo (`/Thoth/ThothAI/backend/thoth_ai_backend/views.py:227-288`).
### Documento del database
La documentazione HTML combina scope, relazioni, tabelle e colonne. Per le descrizioni usa il
campo canonico e, quando vuoto, il commento generato
(`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/generate_db_documentation.py:35-188`).
Il processo di generazione chiede all'AI il diagramma Mermaid, lo salva nell'ERD e costruisce
deterministicamente l'HTML corrente; l'HTML generato non incorpora il diagramma
(`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/generate_db_documentation.py:767-922`). La UI espone il
documento del database del workspace corrente e le route per HTML/PDF ed ERD/PDF
(`/Thoth/ThothAI/backend/thoth_ai_backend/views.py:86-179`,
`/Thoth/ThothAI/backend/thoth_ai_backend/urls.py:179-188`).
Per la parità ThothII servono quindi contenuto documentale strutturato, sorgente Mermaid
persistita e rendering locale. La validazione Mermaid prima della pubblicazione è una
correzione necessaria, non un cambiamento di scopo.
## 6. Export e comandi operativi
Le capacità utili rilevate sono:
- CSV di tabelle e colonne selezionate
(`/Thoth/ThothAI/backend/thoth_core/utilities/utils.py:150-195`);
- export CSV generico dei modelli e della struttura completa
(`/Thoth/ThothAI/backend/thoth_core/utilities/utils.py:198-261`,
`/Thoth/ThothAI/backend/thoth_core/utilities/utils.py:399-569`);
- script SQL dei commenti per PostgreSQL, SQL Server, MySQL e MariaDB
(`/Thoth/ThothAI/backend/thoth_core/admin_utils/sql_comment_script.py:25-316`);
- comandi `generate_scope` e `generate_documentation`, indirizzabili per workspace, database o
tutti i database (`/Thoth/ThothAI/backend/thoth_core/management/commands/generate_scope.py:12-89`,
`/Thoth/ThothAI/backend/thoth_core/management/commands/generate_documentation.py:12-89`);
- export globale e per workspace
(`/Thoth/ThothAI/backend/thoth_core/management/commands/export_models.py:19-64`,
`/Thoth/ThothAI/backend/thoth_core/management/commands/export_workspace.py:19-96`);
- aggiornamento delle descrizioni colonna da CSV per workspace
(`/Thoth/ThothAI/backend/thoth_ai_backend/management/commands/update_column_descriptions.py:24-92`).
I comandi di importazione dalla produzione legacy sono deliberatamente esclusi da questo
inventario di parità: costituiscono un progetto CLI `tht` separato, con mapping, validazione e
cutover propri.
## 7. Esecuzioni asincrone e stato
Scope, ERD, documentazione e commenti possono essere lanciati in background. Il legacy crea
thread daemon e memorizza task id, stato e log sui modelli
(`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/async_scope.py:21-62`,
`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/async_erd.py:21-30`,
`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/async_documentation.py:21-30`,
`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/async_table_comments.py:39-87`).
La capacità da mantenere è: operazione lunga non bloccante, esclusione di esecuzioni
confliggenti, stato osservabile e log. Il meccanismo daemon non è durevole, non permette
recovery o cancellazione e non deve essere replicato letteralmente.
## 8. Difetti e comportamenti da non replicare
| Area | Evidenza legacy | Decisione di parità |
|---|---|---|
| Segreti negli export | L'export struttura include `password` in chiaro (`/Thoth/ThothAI/backend/thoth_core/utilities/utils.py:516-559`); l'export generico serializza tutti i campi (`/Thoth/ThothAI/backend/thoth_core/management/commands/export_models.py:19-64`). | Escludere sempre credenziali e segreti. |
| SQL euristico | Il campionamento per inferire FK interpola identificatori senza quoting e usa query costruite come stringhe (`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:697-750`). | Usare adapter sicuri, quoting per dialetto e query read-only con limiti. |
| Refresh incompleto | L'introspezione aggiunge oggetti ma non riconcilia rimozioni e non aggiorna tutte le proprietà (`/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:318-351`, `/Thoth/ThothAI/backend/thoth_core/dbmanagement.py:459-481`). | Scansioni versionate e stato drift/removed. |
| Relazioni denormalizzate | L'aggiornamento ricostruisce stringhe PK/FK solo sulle colonne partecipanti e non ripulisce esplicitamente valori obsoleti (`/Thoth/ThothAI/backend/thoth_core/models.py:505-526`). | Derivare indicatori dalle relazioni correnti. |
| Azione relazione rotta | L'admin dichiara `update_pk_fk_fields` ma non lo implementa (`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_relationship.py:219-245`). | Esporre un comando applicativo testato. |
| CSV colonne incoerente | L'header ha sei colonne, le righe ne emettono otto incluse PK/FK (`/Thoth/ThothAI/backend/thoth_core/utilities/utils.py:165-195`). | Contratto CSV versionato e testato. |
| Comando export rotto | `export_single_model` usa l'app label `toth_be` e un attributo non definito (`/Thoth/ThothAI/backend/thoth_core/management/commands/export_single_model.py:19-67`). | Non portare il comando; sostituirlo con export catalogo coerente. |
| Batch AI colonne | Un batch usa la prima tabella selezionata come contesto per tutte le colonne (`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_column_comments.py:230-268`). | Raggruppare sempre per database e tabella. |
| Contesto AI tabelle | Il fallback al commento generato è calcolato ma non incluso nel dataframe finale (`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_table_comments.py:485-525`). | Costruire il contesto dal valore effettivamente visibile. |
| Duplicazione DB | Duplica configurazione e vector DB, azzerando soltanto una data di aggiornamento (`/Thoth/ThothAI/backend/thoth_core/admin_models/admin_sqldb.py:514-569`). | Se mantenuta, duplicare soltanto metadati esplicitamente scelti, mai segreti o stati. |
| Scope non JSON | Il generatore intercetta internamente il JSON non valido, mentre l'azione può comunque mostrare successo (`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/create_db_scope.py:92-192`). | Stato `failed` o `needs_review`, senza falso successo. |
| Mermaid non validato | La risposta AI viene salvata dopo la sola estrazione del blocco (`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/generate_db_erd.py:88-121`). | Validare prima di pubblicare e conservare l'errore di rendering. |
| HTML non escapato | I valori descrittivi sono interpolati direttamente nell'HTML (`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/generate_db_documentation.py:35-188`). | Escape/sanitizzazione obbligatori. |
| Job daemon | I task sono thread in-process (`/Thoth/ThothAI/backend/thoth_core/thoth_ai/thoth_workflow/async_scope.py:21-62`). | Esecuzione durevole o almeno recuperabile, con idempotenza. |
| Script MySQL | I commenti colonna usano `ALTER ... MODIFY COLUMN` basandosi sul tipo normalizzato (`/Thoth/ThothAI/backend/thoth_core/admin_utils/sql_comment_script.py:244-316`). | Generare solo con tipo nativo completo oppure bloccare l'export rischioso. |
Il modulo contiene anche un helper verso `mermaid.ink`, ma non risultano call site nel tree
legacy; il percorso attivo è il servizio locale. Non va quindi considerata una dipendenza
funzionale da conservare.
## 9. Baseline di parità proposta il 2026-08-23
### Necessario
- database gestiti derivati dalla configurazione dei workspace;
- inventario completo del database, anche oltre il sottoinsieme usato dal workflow core;
- scansione di tabelle, colonne, chiavi e relazioni fisiche con drift osservabile;
- descrizioni canoniche e commenti AI separati per tabelle e colonne;
- generazione AI selettiva e copia selettiva AI → descrizione, senza workflow aggiuntivo;
- CRUD dei metadati semantici e delle relazioni logiche;
- scope strutturato, documento generale strutturato ed ERD Mermaid;
- servizio locale per validazione/rendering Mermaid in SVG/PNG/PDF;
- test connessione, inferenza/validazione delle relazioni e operazioni lunghe con stato/log;
- export CSV sicuro e script SQL commenti solo quando il dialetto può essere ricostruito senza
perdita;
- API e UI dedicate, separate dal workflow NL→SQL;
- pubblicazione esplicita e controllata del sottoinsieme utile al core/Qdrant.
### Estensione, non requisito di parità iniziale
- analisi e report GDPR;
- importazione dei dati da installazioni ThothAI in produzione, da progettare come iniziativa
CLI `tht` separata.
### Esplicitamente escluso
- replica del Django Admin o del suo modello di permessi implicito;
- modifica manuale della struttura fisica osservata;
- memorizzazione o export di password nel catalogo;
- dipendenza del workflow SQL dalla disponibilità della nuova UI;
- proposal/versioning/diff per i commenti AI;
- copia descrizione → commento AI, non presente nelle fonti;
- difetti tecnici e comportamenti insicuri elencati sopra.
## Conclusione
ThothAI fornisce già il perimetro funzionale di un catalogo metadati, ma lo realizza mescolando
configurazione di connessione, inventario fisico, contenuti semantici, job e UI nel modello
Django. ThothII può raggiungere la parità separando questi aspetti e mantenendo invariato il
workflow SQL: il catalogo gestisce l'intero database, mentre il core continua a consumare
soltanto il sottoinsieme dichiarato dal workspace e pubblicato nel proprio indice Qdrant.
La regola di Q6 è semplice e verificata nelle fonti: generazione nel campo AI, selezione
esplicita dell'utente, copia nel campo canonico. Ogni meccanismo ulteriore sarebbe una nuova
funzionalità e non è necessario per la parità.
@@ -1,206 +0,0 @@
# PostgreSQL deployment and failure-isolation constraints for the Metadata Catalog
Original research: 2026-08-23
Last verified against the repository: 2026-08-31
Issue: [#8 — Assess PostgreSQL deployment and failure-isolation constraints](https://git.tylconsulting.it/mptyl/ThothII/issues/8)
> **Status: partially superseded by ADR-0004.** This is historical research, not the current
> architecture contract. The recommendation to run a separate `catalog-api` process was rejected
> by [ADR-0004](../adr/0004-fastify-kysely-metadata-catalog.md). Operational constraints that do
> not depend on that process boundary remain useful, but the status table below is authoritative
> for what the 2026-08-31 code actually adopts, rejects, or leaves pending.
## Question
How can a Metadata Catalog PostgreSQL service be deployed, backed up, diagnosed, and made
non-blocking for the NL→SQL workflow across local and server installations?
## Current decision and implementation
[ADR-0001](../adr/0001-postgres-metadata-catalog.md) selects PostgreSQL as the catalog authority.
[ADR-0003](../adr/0003-installation-local-database-bindings.md) keeps database bindings
installation-local. [ADR-0004](../adr/0004-fastify-kysely-metadata-catalog.md) then places the
catalog in the existing Fastify backend as an isolated Kysely module rather than a microservice.
The implemented dependency graph is therefore:
```mermaid
flowchart LR
UI[Frontend]
Core[Core: Fastify workflow and catalog modules]
Pi[Pi and tht workflow]
PG[(catalog-db PostgreSQL)]
Semantic[Qdrant and embedding]
UI --> Core
Core --> Pi
Core --> PG
Core --> Semantic
```
The catalog module is isolated behind a repository interface, but it is not process-isolated.
`core` owns the catalog pool and Compose waits for `catalog-db` health before starting `core`
(`compose.yaml`, `backend/src/app.ts`, `backend/src/catalog/repository.ts`). Database-management records still do
not feed the NL→SQL handoff, so a running workflow does not read catalog rows; that cutover remains
future work (`PROJECT_STATE.md`).
## Verification result
| Area | Status on 2026-08-31 | Evidence and consequence |
| --- | --- | --- |
| PostgreSQL as canonical catalog store | **Adopted** | ADR-0001 is implemented by the PostgreSQL-backed Kysely repository and the internal `catalog-db` service. |
| One Workspace Database per workspace and installation-local bindings | **Adopted** | ADR-0003 and the catalog migrations enforce the model; workspace identity remains in the workspace registry. |
| Separate `catalog-api` process | **Superseded/rejected** | ADR-0004 explicitly chooses an isolated module inside the existing Fastify process. There is no catalog microservice or separate catalog liveness endpoint. |
| No catalog pool, credential, or Compose dependency in `core` | **Superseded/rejected** | `core` owns the runtime pool, receives the runtime password secret, and declares `depends_on: catalog-db: service_healthy`. The old process-level isolation acceptance criterion is not current architecture. |
| Private PostgreSQL service and durable volume | **Adopted** | `catalog-db` uses a version-and-digest-pinned PostgreSQL 17.6 image, the private `thothii` network, no published host port, a `pg_isready`-based healthcheck, and `catalog-data`. Compose contract tests assert this topology (`scripts/test-default-compose.sh`). |
| Different local and server database topology | **Superseded/rejected** | Both current profiles inherit the same internal `catalog-db` and named volume. `deploy/compose.server.yaml` does not replace it with an operator-provided endpoint. |
| Runtime and migrator role separation | **Adopted** | `core` receives only `thothii_catalog_runtime`; the profile-gated `catalog-migrate` job receives only `thothii_catalog_migrate`. Bootstrap grants runtime DML/sequence privileges without DDL (`docker/catalog-db-init.sql`). |
| Dedicated backup role | **Pending** | There is no `catalog_backup` role or backup secret. |
| Explicit one-shot migrations | **Adopted** | `backend/src/catalog/migrate.ts` registers ordered Kysely migrations and uses a pool of one. `scripts/run-stack.sh` starts PostgreSQL and runs `catalog-migrate` before local startup; migrations are not hidden in backend startup. |
| Checksums, drift/pending refusal, and migration readiness | **Pending** | No repository-owned checksum policy, drift report, or readiness gate exists for catalog migrations. The backend can start without checking the Kysely migration head when launched outside the local helper. |
| Bounded runtime connection pool | **Adopted** | `backend/src/catalog/repository.ts` sets `max: 5` and `connectionTimeoutMillis: 3000`, and closes Kysely with the Fastify lifecycle. |
| `lock_timeout`, `statement_timeout`, and catalog TLS policy | **Pending** | The runtime pool does not set query or lock timeouts. Catalog connection configuration has no explicit CA/hostname-verification contract; the server profile still uses the private Compose network. |
| Process liveness independent of catalog queries | **Adopted** | `GET /health` returns `{status: "ok"}` without probing PostgreSQL, and `backend/test/health.test.ts` preserves that behavior. `/catalog/status` performs the catalog-specific availability check. |
| Stack startup independent of catalog availability | **Superseded/rejected** | Compose blocks `core` on healthy `catalog-db`. The host service health fold does not list `catalog-db`, but it cannot make `core` start while its Compose dependency is unhealthy. |
| Uniform catalog-outage response (`503` plus `Retry-After`) | **Pending** | Routes map the domain `CatalogUnavailableError` to a sanitized `503`, and an omitted catalog configuration uses an unavailable repository. PostgreSQL driver failures are not uniformly translated to that domain error, and no `Retry-After` contract is implemented. |
| Catalog-specific readiness and `tht doctor` checks | **Pending** | There is no `/health/ready` for the catalog and no doctor section for connection, migration head, pool saturation, backup age, or publication lag (`tools/tht/internal/doctor/report.go`). |
| Logical catalog backup and restore | **Pending** | Current backup archives omit `catalog-data` and do not run `pg_dump`; server archives include only Qdrant and embedding volumes. A backup can therefore succeed without preserving the Metadata Catalog (`tools/tht/internal/backup/create.go`). |
| Last-good Publication and catalog-to-Qdrant cutover | **Pending** | The current database-management slice does not change the NL→SQL runtime or publish catalog metadata to Qdrant. |
## Current operational contract
### Deployment and credentials
- Local and server Compose profiles currently use the same installation-owned `catalog-db`
container and `catalog-data` named volume. PostgreSQL is private to the Compose network.
- The database bootstrap login is the migrator. The init script creates the separate runtime login
from a Docker secret and grants only runtime DML and sequence access.
- Runtime and migrator passwords are separate protected host files exposed as separate Docker
secrets. Neither value belongs in tracked environment files
(`deploy/env/local.env.example`, `deploy/env/server.env.example`).
- The Fastify catalog repository uses a five-connection pool with a three-second connection
timeout. Fastify closes the repository pool during shutdown.
### Migrations
The compiled `catalog-migrate` entry point owns schema changes and uses the migrator credential.
The current ordered series is under
`backend/src/catalog/migrations/`. The local launcher runs
it before the normal stack, while production rollout must invoke the profile-gated service
explicitly.
This is weaker than the original recommendation in two ways: there is no catalog readiness check
for pending or unknown migrations, and the repository does not maintain content checksums for
migration drift. Until those checks exist, “the migrator completed” is the available deployment
gate; the application itself does not prove migration compatibility.
### Failure behavior actually provided
| Failure | Current catalog behavior | Current workflow behavior |
| --- | --- | --- |
| Catalog configuration omitted when launching the backend directly | Fastify uses `UnavailableCatalogRepository`; `/catalog/status` reports unavailable and domain-mapped catalog operations return sanitized `503` | `/health`, sessions, and SSE remain available |
| `catalog-db` unhealthy before Compose startup | `core` is not started because its dependency is not healthy | Workflow startup is blocked |
| PostgreSQL becomes unavailable after startup | `/health` remains process-only and `/catalog/status` reports unavailable; route errors are sanitized, but a uniform `503`/`Retry-After` mapping is not guaranteed | Existing session code does not read catalog rows, but both surfaces still share one Fastify process |
| Migrations are pending or incompatible | No dedicated readiness refusal exists; affected catalog operations fail | No catalog-to-workflow handoff exists yet, but local deployment correctness depends on running `catalog-migrate` first |
| Catalog backup is requested through current `tht backup` | No PostgreSQL dump is added and `catalog-data` is omitted | The archive may succeed while being unable to restore catalog state |
The shared process means resource exhaustion, fatal process errors, and startup hooks remain a
common failure domain even though repository calls are separated. Conversely, placing the module
inside Fastify does not require workflow code to consume catalog rows: preserving that data-flow
boundary is still the useful part of the original isolation recommendation.
## Historical recommendations that remain valid backlog
The following constraints survive ADR-0004 because they can be implemented inside the current
Fastify deployment:
1. **Bound every database operation.** Keep the adopted pool and connection timeout; add explicit
request query and lock timeouts. Long model calls and source sampling must not hold catalog
transactions.
2. **Make failures component-specific.** Translate connection, timeout, and pool-exhaustion errors
into one sanitized catalog-unavailable response, add bounded retry guidance, and keep `/health`
process-only.
3. **Make migration compatibility observable.** Report applied, pending, unknown, and drifted
migrations through a catalog readiness check and `tht doctor`; do not put migrator credentials
in `core`.
4. **Define a server TLS topology before externalizing PostgreSQL.** If the server profile moves to
an operator-provided endpoint, use a dedicated database and roles, protected CA material, and
hostname verification. Sharing a cluster leaves connection, maintenance, WAL, and storage blast
radius even when schemas are separate. PostgreSQL documents the relevant connection and TLS
parameters in its
[connection parameter reference](https://www.postgresql.org/docs/current/libpq-connect.html).
5. **Keep derived semantic data non-canonical.** When catalog publication is implemented, activate
complete immutable revisions and retain the last successfully activated revision rather than
exposing mutable catalog rows to the workflow.
## Backup and restore target
The original logical-backup recommendation is still valid and is now a confirmed implementation
gap.
### Backup
Extend `tht backup create` with a catalog step that runs `pg_dump --format=custom` through a
one-shot helper and adds the dump plus checksum to the installation manifest. Do not treat a tar of
a live data volume as a PostgreSQL consistency contract. PostgreSQL documents that `pg_dump`
creates a consistent export while the database remains in use and that custom format supports
selective restore ([`pg_dump`](https://www.postgresql.org/docs/current/app-pgdump.html)).
Use a dedicated least-privilege backup role, never write dumps into a workspace repository, and
record at least the server version, migration head, checksum, creation time, and catalog revision
identifiers. A filesystem/Qdrant archive with a failed or absent catalog dump must be reported as
incomplete.
### Restore
Restore only through an explicit maintenance operation into an empty or deliberately cleaned
target. Validate the archive checksum, run `pg_restore --exit-on-error --single-transaction`, then
verify migration compatibility, referential integrity, bounded entity counts, and an authenticated
catalog smoke test. The controls are documented by PostgreSQL
([`pg_restore`](https://www.postgresql.org/docs/current/app-pgrestore.html)). Keep the old database
until validation passes; rollback should switch the endpoint or retained volume, not dual-write.
## Diagnostics target
- Keep the shared `GET /health` endpoint process-only.
- Treat `/catalog/status` as the current minimal availability surface; add a bounded catalog
readiness check covering connection and migration compatibility before using it as a rollout
gate.
- Add read-only `tht doctor` checks for secret-file presence and permissions, PostgreSQL
reachability, authentication, migration state, pool saturation, and last successful catalog
backup. Sanitize all DSNs and errors.
- Use `pg_isready` only for server transport readiness. Its result does not prove schema or runtime
authorization correctness
([`pg_isready`](https://www.postgresql.org/docs/current/app-pg-isready.html)).
- Add structured metrics for connection acquisition failures, pool use, query/lock timeout,
migration head, background-job backlog, and backup age. Do not log connection strings, source
samples, prompts, or generated descriptions at info level.
## Recommended closure criteria
1. Decide whether ADR-0004's statement that catalog unavailability does not make sessions or SSE
unavailable must also hold at Compose startup. If yes, remove or soften the hard `core` →
`catalog-db` startup dependency without reintroducing a microservice.
2. Prove a configured PostgreSQL outage produces the same sanitized catalog response across every
catalog route while `/health`, session creation, and SSE continue to work.
3. Add catalog migration compatibility to readiness and `tht doctor`, including pending, unknown,
and drifted states.
4. Add a real `pg_dump`/`pg_restore` round-trip to local and server backup tests, and fail backup
publication when the catalog dump is absent or fails.
5. Before enabling a remote server catalog, define TLS verification, credential files, connection
limits, and the accepted cluster-level blast radius.
6. Before the NL→SQL cutover, define and test an immutable last-good Publication boundary; do not
dual-read mutable PostgreSQL rows and legacy metadata as competing authorities.
## Decision summary
PostgreSQL, installation-local bindings, private deployment, role separation, one-shot migrations,
bounded pooling, and process-only liveness are implemented. The separate `catalog-api` process and
the claim that `core` has no catalog dependency are not current design: ADR-0004 chose the existing
Fastify process, and Compose currently blocks `core` startup on `catalog-db` health. Logical
backup/restore, catalog migration readiness and drift detection, consistent outage mapping,
catalog-specific doctor checks, server TLS/external topology, and last-good publication remain
pending. Those open constraints should be treated as backlog, not as capabilities already provided
by the repository.
@@ -1,301 +0,0 @@
# ThothII metadata publication and Qdrant revision seams
**Research question:** Which existing workspace, snapshot, preprocessing, Qdrant,
session-pinning, and runtime read-only contracts constrain metadata publication without changing
the NL→SQL workflow?
**Last verified:** 2026-08-31
**Validity:** Active architectural research; the catalog-to-core integration described below is
still **deferred**, not implemented.
**Current authorities:** `PROJECT_STATE.md`,
[`workspace-evidence-v3.md`](../contracts/workspace-evidence-v3.md),
[`workspace-preprocessing-cli.md`](../contracts/workspace-preprocessing-cli.md),
[`tht-dwh.md`](../contracts/tht-dwh.md),
[`ADR-0001`](../adr/0001-postgres-metadata-catalog.md), and
[`ADR-0004`](../adr/0004-fastify-kysely-metadata-catalog.md). These sources and current code
override this research note if they diverge.
## Revalidation against the current implementation
| Finding | Status on 2026-08-31 | Current evidence and consequence |
| --- | --- | --- |
| PostgreSQL metadata authority in the existing Fastify backend | **Adopted/current** | ADR-0001 and ADR-0004 are implemented; the catalog is the management-plane authority. |
| Git workspace revision, immutable registry snapshot, and revision-pinned runtime | **Adopted/current** | Registry activation and the Workspace Evidence v3 contract still provide the publication boundary for runtime-owned files. |
| Qdrant read isolation for schema and Evidence by `workspace_id` + `workspace_revision` | **Adopted/current** | `QdrantVectorStore.search()` applies `_revision_filter()` to schema/Evidence; Memory and solved questions intentionally remain workspace-wide (`harness/tht/adapters/vector/qdrant.py`). |
| Physical schema as a file in the Git publication | **Superseded clarification** | `physical.yaml` is owned by the immutable `.tht-dwh` generation selected by `ACTIVE`, not by the Git workspace revision ([DWH contract](../contracts/tht-dwh.md)). Git currently supplies the revision-pinned curated `schema/annotations.yaml`. |
| Catalog-to-core publisher | **Open/deferred** | No current route or service projects catalog records into core artifacts. `PROJECT_STATE.md` explicitly defers the schema-linking integration (`PROJECT_STATE.md:160-171`). |
| Explicit Core Schema Selection | **Open/deferred** | The workspace descriptor selects one database and one physical schema, but has no table/column allowlist (`backend/src/workspaces/schema.ts`); no catalog selection is handed to `tht`. |
| Cross-revision hash lookup used by schema synchronization | **Open defect** | Reads are revision-filtered, but `existing_hashes()` is not. A same-key/same-content point from an older revision can suppress the required upsert into the new revision (`harness/tht/adapters/vector/qdrant.py`, `harness/tht/cli/vector_cmd.py`). |
| Deletion/GC | **Current for Evidence; open for schema** | Evidence has generation inventory, retention, compensation, and exact-generation deletion. `sync_canonical_records()` never deletes schema records absent from the new canonical set, and no revision-retention GC exists for schema points. |
| Annotation consumption | **Current but incomplete** | M-Schema rendering and schema embeddings consume `Annotations`; `tht schema columns` still returns only physical comments, so F4 does not display annotation descriptions (`harness/tht/cli/schema_cmd.py`, `harness/.pi/extensions/tht-gate.js`). |
## Conclusion
The lowest-impact **proposed** publication seam is not a new writer inside the NL→SQL workflow and
is not a direct CRUD-to-Qdrant path. The existing core consumes an introspected `PhysicalSchema`
from the active immutable DWH generation and a curated, Git-revision-pinned `Annotations`
document. A future explicit publication operation should project the approved catalog subset into
a new Core Schema Selection contract and the curated annotations, activate a new immutable Git
revision, and then invoke the existing `workspace index-schema` preprocessing operation. Qdrant
remains a derived, rebuildable projection. None of this catalog-to-core handoff exists yet;
`PROJECT_STATE.md:160-164` deliberately defers it to the next design
gate.
The proposed flow would preserve the existing runtime path:
```text
approved catalog data
-> explicit Core Schema Selection + curated annotations projection (future)
-> workspace Git revision (annotations and selection contract; physical.yaml stays DWH-owned)
-> immutable registry snapshot
-> revision-bound runtime configuration
-> existing tht vector index-schema
-> Qdrant records filtered by workspace_id + workspace_revision
-> existing retrieval_pack / schema render / F4 review
```
The complete database inventory remains in the Metadata Catalog. Only an explicit Core Schema
Selection and approved semantic fields should be projected into the artifacts used by the SQL
workflow. ThothII does not currently model or publish that table/column-level selection, so both
the projection contract and its operational publisher are new work.
## 1. Current authority and publication boundary
The workspace descriptor identifies one PostgreSQL database and one physical schema, plus one
workspace-owned Qdrant collection; it has no table or column allowlist
(`backend/src/workspaces/schema.ts`).
Consequently, a full-database metadata catalog and the subset eligible for the SQL core cannot be
represented as the same current descriptor object.
The existing public contract makes the Git workspace repository curator-owned. Changes occur in a
separate authoring clone followed by installation pull, and the API does not write workspace,
schema, or Evidence paths
([Workspace Evidence v3 contract](../contracts/workspace-evidence-v3.md)).
Therefore a browser CRUD service cannot silently make its PostgreSQL state authoritative for the
core without either:
1. exporting/committing a deterministic workspace projection through the existing curator flow;
or
2. deliberately replacing this Git-authority contract.
The first option preserves current architecture and session reproducibility.
Registry activation validates every descriptor at one exact commit, validates and synchronizes
the commit's `schema/annotations.yaml`, and rejects duplicate ownership of a Qdrant collection
(`backend/src/workspaces/registry.ts`,
`backend/src/workspaces/git-repository.ts`,
`backend/src/workspaces/annotations-sync.ts`).
It writes the candidate snapshot into a staging directory, records file digests in
`snapshot.json`, atomically renames the directory, and only then moves active state
(`backend/src/workspaces/registry.ts`).
That is the existing atomic publication boundary to reuse.
## 2. Canonical schema inputs already consumed by the core
The harness separates source facts from curated semantics:
- `PhysicalSchema` contains database/schema identity and tables; table facts include comments,
columns, physical foreign keys, and indexes; columns contain type, nullability, primary-key,
default, source comment, examples, and eligibility
(`harness/tht/mschema/models.py`).
- `Annotations` contains curated table descriptions, concepts and notes, column descriptions,
synonyms, concepts, evidence, notes and eligibility overrides, plus logical foreign keys
(`harness/tht/mschema/models.py`).
Rendering already gives annotations precedence over source comments and merges physical and
logical foreign keys. It also applies column eligibility before producing M-Schema context
(`harness/tht/mschema/render.py`). This makes
`Annotations` the natural narrow projection target for approved descriptions, synonyms, logical
relationships, and eligibility from the new catalog.
There are two current compatibility gaps:
- `schema_records()` embeds table descriptions/concepts and column descriptions/synonyms/examples,
but does not include physical or logical foreign keys in vector record content
(`harness/tht/vectorstore/records.py`).
Relationships still reach the model through deterministic schema rendering, not through schema
candidate embeddings.
- The F4 widget loads columns with `tht schema columns`
(`harness/.pi/extensions/tht-gate.js`), but that
command still returns only `PhysicalSchema.comment` values and does not merge
`Annotations`
(`harness/tht/cli/schema_cmd.py`).
A publisher that writes only annotations would improve vector search and rendered M-Schema but
not the table/column descriptions displayed by this existing reviewer widget. Fixing the command
to use the existing merged description helpers would preserve the workflow shape while closing
the inconsistency.
## 3. Existing preprocessing seam
`workspace index-schema` is already the supported operator seam. It creates a revision-bound
runtime, checks collection compatibility, and runs the harness command
`vector index-schema --json`
(`backend/src/workspaces/preprocessing-service.ts`,
[`workspace-preprocessing-cli.md`](../contracts/workspace-preprocessing-cli.md)).
The full preprocessing operation performs DWH preparation, FK suggestion/review, schema indexing,
and optional Evidence preprocessing as separate resumable stages
(`backend/src/workspaces/preprocessing-service.ts`).
Metadata-only publication should normally use the narrow `index-schema` operation after its
workspace artifacts are valid, rather than coupling catalog CRUD to the full pipeline.
The harness indexer reads the active immutable physical schema and revision-specific annotations,
constructs schema records, embeds only changed content, and writes them through the configured
vector adapter
(`harness/tht/cli/vector_cmd.py`). Its
machine result carries the physical/annotation artifact digests, workspace revision, collection,
and counts
(`harness/tht/cli/vector_cmd.py`). This is
the right place to retain publication evidence and audit linkage.
Preprocessing state is already revision- and binding-aware. A resumed job must match operation,
workspace revision, descriptor/catalog blobs, runtime config and binding digests or it fails with
`preprocessing_resume_mismatch`
(`backend/src/workspaces/preprocessing-state.ts`).
There is an important operational gate: every preprocessing operation calls
`assertSessionInventoryCompatible`; a non-finalized, non-archived session pinned to another
revision blocks preprocessing
(`backend/src/workspaces/preprocessing-service.ts`,
`backend/src/workspaces/preprocessing-state.ts`).
A catalog publication UX must expose this as a pending/blocking condition rather than report a
generic indexing failure.
## 4. Qdrant identity, payload and collection contracts
The collection contract is fixed at the descriptor's dimensions/distance and eight keyword payload
indexes: `content_hash`, `document_id`, `kind`, `record_key`, `record_kind`,
`vector_generation`, `workspace_id`, and `workspace_revision`
(`backend/src/workspaces/qdrant-collection.ts`).
`self_heal` may create a missing compatible collection or indexes; `require_existing` only validates
and refuses an incompatible collection
(`backend/src/workspaces/qdrant-collection.ts`).
The publication path must use this shared manager instead of inventing collection setup.
Schema payloads include both the workspace and workspace revision, record identity, semantic kind,
content and content hash
(`harness/tht/vectorstore/records.py`).
Qdrant point IDs for schema and Evidence also include the revision, and upserts use the same
revision in the payload
(`harness/tht/adapters/vector/qdrant.py`).
Reads always filter by `workspace_id`; schema and Evidence reads additionally filter by the bound
`workspace_revision`, while memory and solved-question records intentionally remain
workspace-wide
(`harness/tht/adapters/vector/qdrant.py:125-145`).
This means an approved semantic change needs a new workspace revision if old sessions must retain
their previous view. Directly overwriting points under the same Git revision would mutate the
meaning of that supposedly immutable revision; adding an independent catalog-publication version
would require changing the current runtime filter contract.
### Qdrant synchronization defect to resolve before catalog publication
The current incremental schema synchronizer compares canonical records by content hash and upserts
changed records, but it has no deletion step
(`harness/tht/cli/vector_cmd.py:77-99`). More
importantly, `QdrantVectorStore.existing_hashes()` filters by workspace and record kind but does not
apply `_revision_filter()`
(`harness/tht/adapters/vector/qdrant.py:266-286`),
even though point IDs and reads are revision-scoped. Therefore a same-key/same-content record from
an older revision may be classified as unchanged and never written under the new revision. This is
an implementation defect/risk inferred directly from the two code paths, and it should be fixed
and regression-tested before the catalog relies on `index-schema` for multi-revision publication.
Deletion/GC must be stated per record family. Evidence cleanup is implemented: the corpus pipeline
retains the configured number of published generations, protects active/job-referenced
generations, and calls exact-generation deletion for evicted or compensated generations
(`harness/tht/evidence/corpus/pipeline.py`,
`harness/tht/adapters/vector/qdrant.py:350-390`). Schema cleanup is
not implemented: `delete_kinds()` exists as a workspace-scoped adapter primitive, but the schema
synchronizer never calls it, it is not revision-scoped, and there is no retention policy for old
schema revisions. Publication therefore still needs exact current-revision deletion semantics and
separate safe GC for unleased historical schema revisions.
## 5. Session pinning and why the SQL workflow can remain unchanged
Normal session creation acquires an immutable registry revision, passes its snapshot path,
workspace ID and commit to `tht session new`, and only releases the retention lease after the
manifest has been persisted
(`backend/src/routes/sessions.ts`). The
manifest stores `workspace_id` and `workspace_revision` next to database/schema identity
(`harness/tht/session/store.py`). Resume and
saved-SQL paths reopen that exact retained snapshot rather than the current installation default
(`backend/src/routes/sessions.ts`,
`backend/src/routes/sql.ts`).
The runtime renderer places the same workspace revision in `runtime_identity`, points the harness
at the descriptor-owned Qdrant collection, and supplies the internal embedding service
(`backend/src/workspaces/runtime-renderer.ts`).
The vector adapter is constructed directly from that configuration, including the revision
(`harness/tht/adapters/factory.py`).
At session bootstrap the backend invokes the existing `search pack` command
(`backend/src/routes/sessions.ts`). That
command queries schema records with the existing schema kinds, ranks tables and persists the
candidate list
(`harness/tht/cli/search_cmd.py`). The Pi
extension reads the persisted retrieval pack through the CLI
(`harness/.pi/extensions/tht-gate.js`),
and F4 starts from those candidates while loading full table/column context through existing schema
commands. Thus catalog publication can improve the inputs without changing phases, gate semantics,
or persisted session artifacts.
## 6. DWH reuse across metadata-only revisions
DWH preparation uses immutable generations selected by an `ACTIVE` pointer
([DWH contract](../contracts/tht-dwh.md)). The effective-configuration
identity deliberately excludes `runtime_identity`, so a content-only Git revision does not force a
database re-introspection
([DWH contract](../contracts/tht-dwh.md)). This is the key enabling property for future metadata
publication: a new Git revision can carry updated curated annotations and a future Core Schema
Selection contract, reuse the compatible DWH-owned `physical.yaml`, and rebuild only the
revision-scoped schema projection. Today only the curated annotations part of that statement exists.
## 7. Constraints for the Metadata Catalog design
The following remain proposed requirements for the deferred catalog-to-core design gate; they are
not claims about current implementation:
1. **Separate full inventory from core projection.** PostgreSQL may hold the complete database
catalog, drafts and AI-generated text. Only an explicit Core Schema Selection and approved
semantic fields are exported to core artifacts/Qdrant.
2. **Publish; do not live-link.** CRUD changes are not visible to the SQL workflow until an explicit,
audited publication succeeds. A failed projection, Git activation or Qdrant index operation
leaves the previous revision active.
3. **Use a new Git revision as the publication identity.** This preserves current snapshot,
retention, session resume and Qdrant filtering semantics. A separate mutable catalog revision
cannot be safely introduced without changing runtime reads.
4. **Respect artifact ownership.** Keep introspected physical facts in the immutable DWH generation;
project the future Core Schema Selection through an explicit contract and approved semantic
edits into `Annotations`. Keep Mermaid and long-form database documentation outside Qdrant
unless a separate record kind and retrieval policy is designed.
5. **Reuse the operator boundary.** Trigger `workspace index-schema`, observe its schema-versioned
result and persist its artifact identities. Do not call Qdrant from browser CRUD handlers.
6. **Keep runtime read-only.** The session/Pi process continues to read the pinned snapshot,
retrieval pack and Qdrant projection. AI generation and catalog writes belong to the separate
management control plane.
7. **Surface publication gates.** UI status must distinguish Git activation, incompatible/missing
collection, resumable-session revision conflict, embedding failure and completed publication.
8. **Repair and test cross-revision schema synchronization first.** Scope `existing_hashes()` to
the bound revision, delete records removed from the current revision's canonical schema, and
define safe schema-revision GC. Reuse rather than duplicate the already implemented Evidence
generation GC.
9. **Close the annotation display gap without changing the workflow.** Make `schema columns` read
the same merged descriptions used by M-Schema/vector rendering, so the current F4 widget sees
the approved catalog text.
## Proposed decision summary for the Wayfinder map
- Keep the Metadata Catalog as a separate management subsystem and source of editable metadata.
- Keep Qdrant derived and revision-scoped; it is not the catalog database or source of truth.
- Publish through immutable workspace revisions plus the existing preprocessing service.
- Preserve the current NL→SQL workflow, Pi extension, phase model and session artifacts.
- Add a new, explicit Core Schema Selection contract because no table/column selection exists in
the current descriptor.
- Treat direct same-revision Qdrant writes, implicit CRUD publication, and bypassing the Git
snapshot boundary as rejected integration paths.
This summary remains design input. The authoritative current state is that catalog-to-core
integration and Sensitive Data Policy enforcement in schema-linking are deferred in
`PROJECT_STATE.md:160-171`.
@@ -114,7 +114,7 @@ La review dei join durante una sessione è distinta dalla curatela globale: il r
| Retrieval | Le relationship entrano nel render M-Schema ma non nei record vettoriali | Va deciso se e come influenzano selezione tabelle, ranking e descrizioni, oltre al rendering finale | | Retrieval | Le relationship entrano nel render M-Schema ma non nei record vettoriali | Va deciso se e come influenzano selezione tabelle, ranking e descrizioni, oltre al rendering finale |
| Drift | Sync fisica è autoritativa; annotazioni sono una revisione separata | Servono semantiche per endpoint rinominati/eliminati, candidate stale, orphan e riapprovazione | | Drift | Sync fisica è autoritativa; annotazioni sono una revisione separata | Servono semantiche per endpoint rinominati/eliminati, candidate stale, orphan e riapprovazione |
Le analisi già presenti nel repository trattano l'integrazione catalogo→core, la selezione pubblicabile e la riparazione degli indici come questioni ancora aperte; le loro proposte non sono decisioni implementate (`docs/research/2026-08-23-thothii-metadata-publication-qdrant-seams.md:20-43`; `docs/research/2026-08-23-thothii-metadata-publication-qdrant-seams.md:239-301`). Anche il piano di migrazione rinvia il lifecycle/admin delle relationship logiche (`docs/plans/2026-08-26-metadata-catalog-from-thothai.md:681-691`). Nota storica: questa analisi precede il passaggio PostgreSQL catalog-to-core. Per il contratto implementato usare [architettura corrente](../architecture/overview.md) e ADR 0016; le vecchie ipotesi di pubblicazione non sono istruzioni operative.
## Direzione confermata nel grill ## Direzione confermata nel grill
@@ -1,122 +0,0 @@
# Google Antigravity e Gemini 3.8 Flash: convenienza costo/qualità
Verifica effettuata il **7 settembre 2026**, privilegiando documentazione e annunci
ufficiali Google. Le prove indipendenti su Gemini 3.8 Flash sono ancora limitate perché il
modello è stato pubblicato il 2 settembre 2026.
## Giudizio sintetico
- **Registrazione gratuita: decisamente conveniente.** Il piano Individual da $0 include
Gemini 3.8 Flash, l'ultima versione Flash, oltre alla CLI e alle funzioni principali di
Antigravity. È difficile ottenere un rapporto costo/qualità migliore di un accesso gratuito
a un modello di questa fascia.
- **Google AI Pro da circa $20/mese: probabilmente conveniente per uso regolare**, ma solo dopo
aver misurato il consumo sul piano gratuito. Google non pubblica un numero fisso di prompt o
token inclusi, quindi non è possibile calcolare un break-even affidabile contro l'API.
- **Ultra da $100 o $200: non lo comprerei per il solo Gemini Flash** senza aver già dimostrato
di saturare Pro. Il piano da $100 offre 5 volte la quota Pro; quello da $200 ne offre 20 volte.
- Per il server dietro CyberArk, Antigravity ha un vantaggio concreto: la CLI ufficiale `agy`
funziona direttamente in Linux e prevede esplicitamente un flusso OAuth remoto tramite URL e
codice, senza tunnel né port forwarding.
## Accesso e prezzi
La [pagina ufficiale dei piani Antigravity](https://www.antigravity.google/docs/plans/) e la
[tabella dei modelli](https://www.antigravity.google/docs/models/) indicano che Gemini 3.8 Flash
è disponibile su Individual gratuito, AI Plus, AI Pro, AI Ultra ed Enterprise. Il piano gratuito
include anche completamenti Tab illimitati e tutte le funzioni del prodotto, inclusa la CLI, ma
ha un limite settimanale di base.
Google AI Pro costa normalmente **$19,99/mese** nella pagina internazionale di
[Google One](https://one.google.com/about/plans) e offre quote Antigravity superiori, con rinnovo
ogni cinque ore finché non viene raggiunto il limite settimanale. Google AI Ultra è offerto a
[$100/mese con quota 5× Pro oppure $200/mese con quota 20× Pro](https://blog.google/products-and-platforms/products/google-one/google-ai-subscriptions/).
Il problema è la misurabilità: Google dichiara che i limiti dipendono dalla capacità disponibile
e dalla quantità di lavoro compiuta dall'agente, possono cambiare e non corrispondono a un numero
pubblico fisso di richieste o token. Pro e Ultra possono acquistare crediti per continuare oltre
la quota base, con consumo ai prezzi della piattaforma Gemini.
Usando direttamente la Gemini API, Gemini 3.8 Flash costa fino al 31 dicembre 2026:
- **$0,75 per milione di token di input**;
- **$3,75 per milione di token di output**, inclusi i token di ragionamento;
- la metà in modalità Batch o Flex.
Dal 1º gennaio 2027 questi prezzi raddoppieranno a $1,50/$7,50. Esiste anche un free tier API,
con limiti, nel quale input e output sono gratuiti ma i contenuti possono essere usati per
migliorare i prodotti Google. Fonte: [pricing ufficiale Gemini API](https://ai.google.dev/gemini-api/docs/pricing).
## Qualità del modello
[Gemini 3.8 Flash](https://ai.google.dev/gemini-api/docs/models/gemini-3.8-flash) è GA, offre un
contesto da 1.048.576 token, output fino a 65.536 token, livelli di ragionamento low/medium/high e
strumenti per code execution, computer use, file search, function calling e search grounding.
I risultati pubblicati da Google lo collocano molto vicino ai modelli flagship su alcuni test di
coding, ma non su tutti:
| Benchmark | Gemini 3.8 Flash | Claude Opus 5 | GPT-5.6 Sol | Lettura onesta |
|---|---:|---:|---:|---|
| DeepSWE v1.1 | 73,7% | 74,0% | 72,7% | Prestazione quasi flagship sul software engineering end-to-end |
| Terminal-Bench 2.1 | 89,4% | 89,1% | 88,8% | Eccellente nel terminale sul benchmark più maturo |
| Terminal-Bench 4.0 | 19,1% | 51,8% | 37,3% | Forte calo sul test nuovo e più difficile: non è universalmente al livello dei flagship |
La [metodologia ufficiale Google](https://deepmind.google/models/evals-methodology/gemini-3-8-flash/)
precisa che diversi punteggi Gemini sono calcolati internamente e che i concorrenti provengono
anche da risultati auto-dichiarati; inoltre DeepSWE usa mini-swe, mentre Terminal-Bench usa un
harness diverso. I numeri vanno quindi letti come indicazione, non come garanzia. Una
[ricostruzione della tabella e dei confronti](https://www.vellum.ai/blog/gemini-3-8-flash-benchmarks-explained)
mostra lo stesso andamento: molto competitivo sui compiti di coding già ben rappresentati, più
debole su alcune prove nuove e aperte.
La conclusione qualitativa è: **ottimo implementatore quotidiano e subagente veloce**, con qualità
da modello molto più costoso in diversi task; per architettura difficile, debugging ambiguo o
lavori ad alto rischio è ancora sensato affiancargli un modello più forte come pianificatore o
revisore.
## Perché Antigravity è particolarmente adatto al server
La [CLI ufficiale Antigravity](https://antigravity.google/docs/cli/overview/) è una TUI interattiva
con editing multi-file, cronologia, tool calling, sandbox e subagenti. La
[guida d'installazione e autenticazione](https://antigravity.google/docs/cli/install/) conferma:
- esecuzione nativa su Linux, macOS e Windows;
- binario `agy` installato in `~/.local/bin` su Linux/macOS;
- quando rileva SSH, stampa un URL da aprire sul Mac e richiede di incollare nel terminale il
codice ottenuto;
- nessuna porta in ascolto e nessun port forwarding sono necessari per questo login.
Questo risolve meglio di ZCode il vincolo CyberArk, purché il server possa effettuare connessioni
HTTPS in uscita e sia consentita l'installazione del binario.
Per consumare la quota della registrazione Antigravity bisogna usare il client ufficiale. I
[termini Antigravity](https://antigravity.google/terms) vietano di riutilizzare il login/OAuth con
Pi, OMP, Claude Code, OpenCode o altri client. Con questi harness si può invece usare una normale
chiave Gemini API, pagando o consumando la quota API separata.
## Privacy e codice aziendale
Con un account personale Google registra le interazioni e può usarle per valutare e migliorare
prodotti e modelli; l'utente può disattivare l'uso dalle impostazioni. I termini Enterprise sono
diversi e la documentazione dichiara che codice, prompt e trascrizioni delle organizzazioni non
sono usati per addestrare i modelli Google. Fonti:
[termini Antigravity](https://antigravity.google/terms) e
[integrazioni Enterprise](https://antigravity.google/docs/ide/extensions/).
Su un server aziendale protetto da CyberArk userei quindi una registrazione personale solo dopo
aver verificato la policy interna; in caso contrario sceglierei l'accesso Antigravity Enterprise
tramite il progetto Google Cloud dell'organizzazione.
## Raccomandazione finale
1. Creare l'account gratuito e usare `agy` sul server per una settimana con task reali.
2. Tenere disabilitato l'uso automatico dei crediti extra e osservare i due indicatori di quota.
3. Passare a Pro soltanto se il limite gratuito interferisce con il lavoro.
4. Non acquistare Ultra finché Pro non viene saturato con regolarità.
5. Usare Gemini 3.8 Flash come modello principale per esplorazione, implementazione e test; per le
decisioni più difficili, mantenere Codex/Sol/Opus o un altro modello forte come revisore.
In breve: **sì, oggi Antigravity gratuito o Pro offre uno dei migliori rapporti costo/qualità per
Gemini 3.8 Flash**, e nel caso specifico la CLI ufficiale senza tunnel aumenta ulteriormente il
valore. Il limite commerciale da accettare è la quota non numerica e modificabile da Google.
@@ -1,154 +0,0 @@
# DeepSeek Harness su un server raggiunto tramite CyberArk
Data della verifica: 7 settembre 2026.
## Risposta breve
Sì. Il progetto che ha attirato molta attenzione è **DeepSeek Harness**, comando
`dsh`, pubblicato da DeepSeek nel repository ufficiale
[`deepseek-ai/deepseek-harness`](https://github.com/deepseek-ai/deepseek-harness).
Può lavorare direttamente sul server senza SSH port forwarding usando il profilo
ufficiale **headless**:
```sh
export DEEPSEEK_API_KEY='...'
npx @deepseek-ai/dsh --profile headless \
"Esamina questo repository e correggi i test che falliscono"
```
Questo profilo esegue un incarico, stampa la risposta e termina. Non avvia GUI,
browser o server HTTP e, soprattutto, **non apre alcuna porta**. Lo documentano sia
il [README del profilo headless](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/bundle/headless/README.md)
sia il [riferimento della CLI](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/reference/README.md).
Pertanto il port forwarding non serve: basta la shell che CyberArk già consente e
connettività HTTPS in uscita.
La limitazione importante è che non si tratta, per ora, di un'interfaccia terminale
interattiva come Pi, OMP, Claude Code o Codex: il profilo ufficiale headless accetta
**un solo task per invocazione e non permette follow-up interattivi**.
## Che cos'è, e cosa non è
DeepSeek lo presenta come un harness open source in *developer preview*, basato su
un'architettura in cui modelli, strumenti, skill, sessioni, sandbox, storage,
subagenti e UI sono plugin componibili. La pagina ufficiale descrive inoltre
modalità Standard, Code, Minimal e Creator e la registrazione append-only delle
esecuzioni. Non è semplicemente il modello DeepSeek e non è uno dei numerosi wrapper
creati dalla comunità. Fonti: [pagina ufficiale DeepSeek Harness](https://www.deepseek.com/harness/en/)
e [README ufficiale](https://github.com/deepseek-ai/deepseek-harness#readme).
L'identità del pacchetto è verificabile anche nel
[`package.json` della CLI](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/package.json):
il pacchetto pubblico è `@deepseek-ai/dsh` e installa l'eseguibile `dsh`.
Il [record ufficiale del repository](https://api.github.com/repos/deepseek-ai/deepseek-harness)
ne data la creazione al 13 agosto 2026; alla data di questa verifica la release
più recente è la prerelease
[`dsh-v0.1.3-alpha.2`](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.3-alpha.2),
pubblicata il 7 settembre 2026. Questo conferma quanto il progetto sia giovane e
rafforza l'avvertenza sulla stabilità delle interfacce.
## Le tre modalità rilevanti nel tuo scenario
| Modalità | Porta/tunnel | Interazione | Utilità con CyberArk |
|---|---:|---|---|
| `dsh --profile headless "task"` | Nessuna | One-shot, non interattiva | **Sì, è la soluzione semplice** |
| `dsh web` | HTTP locale su `127.0.0.1:3080` | Web interattiva | No dal Mac senza forwarding, reverse proxy autorizzato o browser sul server |
| `dsh --profile acp` | Nessuna porta; JSON-RPC su stdin/stdout | Persistente, pilotata da un client ACP | Possibile, ma l'integrazione attraverso CyberArk va provata |
La Web UI ufficiale ascolta per default su `127.0.0.1:3080`; il README afferma
esplicitamente che, durante un lancio SSH, il forwarding è responsabilità del client
SSH o dell'editor. È quindi inadatta al vincolo descritto, a meno di cambiare
l'architettura di accesso con l'approvazione dell'amministrazione
([README ufficiale](https://github.com/deepseek-ai/deepseek-harness#run)).
Il profilo ACP ufficiale è invece un server di automazione persistente su
**JSON-RPC stdio**, senza UI. Un client ACP avvia `dsh --profile acp`, crea una
sessione indicando una directory di lavoro assoluta e scambia richieste e risposte
su standard input/output
([README ACP ufficiale](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/bundle/acp-app/README.md)).
Da ciò segue una possibilità tecnica: un client locale potrebbe usare come comando
qualcosa di equivalente a `ssh <server> dsh --profile acp`, trasportando lo stdio
senza alcun port forwarding. Questa è però un'**inferenza architetturale**, non una
configurazione dichiarata compatibile con CyberArk da DeepSeek. Banner di login,
MFA interattivo, testo aggiunto su stdout, divieto di `ssh host command`, timeout o
riscrittura dei flussi da parte del proxy CyberArk possono corrompere JSON-RPC o
impedire del tutto l'avvio. Se CyberArk offre soltanto una console web/interattiva,
ACP non collega automaticamente Zed sul Mac al processo remoto.
## Requisiti di rete e di sistema
- Il repository richiede Node.js `^22.19.0` oppure `>=24.0.0`, come specificato
nel [`package.json` ufficiale](https://github.com/deepseek-ai/deepseek-harness/blob/master/package.json).
- Il primo `npx` deve poter scaricare `@deepseek-ai/dsh` dal registry npm. In un
ambiente bloccato occorre un mirror aziendale o un'installazione preventiva
autorizzata.
- Per usare il provider predefinito occorrono `DEEPSEEK_API_KEY` e traffico HTTPS
in uscita verso `https://api.deepseek.com`. `DEEPSEEK_BASE_URL` può sostituire
l'endpoint, per esempio con un proxy OpenAI-compatible
([adapter DeepSeek ufficiale](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm-deepseek/README.md)).
- Se la rete aziendale impone un proxy HTTP, il riferimento della CLI indica
`NODE_USE_ENV_PROXY=1` affinché una versione Node compatibile rispetti
`HTTP_PROXY` e `HTTPS_PROXY`
([riferimento della CLI, sezione Source execution](https://github.com/deepseek-ai/deepseek-harness/blob/master/apps/cli/reference/README.md#source-execution)).
- Le operazioni richieste dall'agente possono necessitare altri host in uscita
(`git`, registry dei pacchetti, documentazione). Non sono necessari per il
trasporto DSH in sé, ma possono esserlo per il task affidato.
In altre parole, serve **HTTPS outbound**, non una connessione in ingresso verso il
server e non un tunnel dal Mac.
## Limiti pratici specifici di CyberArk
1. **Accesso alla shell:** se CyberArk consente di aprire una normale sessione shell
e di eseguire Node, `headless` funziona concettualmente come qualunque altro
comando. Se applica allowlist ai binari, servirà l'autorizzazione per `node`,
`npx`/`dsh` e per gli strumenti che l'agente vuole eseguire.
2. **Egress:** firewall e proxy devono consentire almeno l'endpoint del modello;
CyberArk non sostituisce questa autorizzazione di rete.
3. **Durata della sessione:** un timeout o la chiusura della sessione privilegiata
può terminare il task. `headless` non lascia un demone dietro di sé, ma incarichi
lunghi vanno confrontati con i limiti della sessione CyberArk.
4. **Credenziali e registrazione:** evitare di digitare o stampare la chiave API in
una sessione registrata. Conviene usare il meccanismo aziendale approvato per
iniettare il secret e verificare cosa CyberArk registra.
5. **Approvals:** l'headless ufficiale non ha un interlocutore umano integrato. Le
richieste di escalation senza un *answerer* vengono negate in modo fail-closed;
le normali scritture consentite nella workspace restano possibili
([contratto delle approvazioni](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/interaction/user-approval/README.md)).
6. **Sicurezza:** DeepSeek dichiara il progetto non sottoposto a security audit e
non pronto per produzione. Può eseguire codice generato dal modello e accedere
a file, processi, rete e credenziali disponibili al processo. Il progetto stesso
raccomanda privilegi minimi e un ambiente dedicato o usa-e-getta
([Safety notice ufficiale](https://github.com/deepseek-ai/deepseek-harness/blob/master/SAFETY.md)).
Un dettaglio importante in un server aziendale: la sandbox corrente descritta da
DeepSeek governa gli effetti sul filesystem, mentre rete e visibilità dei processi
sono fuori dal suo vocabolario di enforcement. Non considerarla quindi un sostituto
di firewall, container, account dedicato e policy CyberArk
([documentazione sandbox ufficiale](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/sandbox.md)).
## TUI interattiva: stato reale
Il prodotto ufficiale offre oggi Web UI, headless one-shot e interfacce di
automazione SDK/ACP. Una TUI a schermo intero chiamata `dsh-tui` è stata presentata
nell'area community del repository, ma il pacchetto e il repository appartengono a
terzi, non a DeepSeek
([discussione nella community DSH](https://github.com/deepseek-ai/deepseek-harness/discussions/3715)).
Non va confusa con la CLI ufficiale e, dato che DSH avverte di possibili cambiamenti
incompatibili durante la developer preview, non la considererei la prima scelta su
un server aziendale protetto.
## Giudizio
Per il tuo vincolo concreto la risposta è **sì, ma in modalità one-shot**:
`dsh --profile headless` è utilizzabile dalla normale shell CyberArk, non apre porte
e non richiede tunnel. È una soluzione tecnicamente più adatta della Web UI, ma
meno comoda di Pi/OMP o Codex CLI per un dialogo iterativo.
La proverei inizialmente su una copia non sensibile del repository, con
`workspace-write`, egress ristretto e secret iniettato secondo le regole aziendali.
Se vuoi un'esperienza persistente dal Mac, ACP su stdio merita un piccolo test di
compatibilità con il gateway CyberArk; non lo darei per funzionante finché non si
verifica che il gateway permetta un comando remoto non interattivo e mantenga
stdin/stdout completamente puliti.
-127
View File
@@ -1,127 +0,0 @@
# OMP con GPT-5.6 Sol, Codex ufficiale e ZCode CLI
_Verifica effettuata il 7 settembre 2026 su documentazione e codice/fonti primarie correnti._
## Risposta breve
Se l'obiettivo è usare **GPT-5.6 Sol attraverso la quota inclusa nel piano ChatGPT/Codex**, la
scelta consigliata è **Codex ufficiale**: CLI per il lavoro da terminale, app desktop per più task,
worktree e revisione visuale. OMP è un harness molto capace e può essere preferibile per ACP/Zed,
multi-provider, LSP/DAP e orchestrazione dei subagenti, ma il suo accesso “OpenAI Codex OAuth” non
è una superficie che OpenAI documenta ufficialmente come client supportato.
Se invece OMP usa una **chiave API OpenAI**, l'integrazione è tecnicamente normale e GPT-5.6 Sol è
disponibile tramite Responses API con function calling, structured outputs e diversi tool. In quel
caso, però, il consumo è fatturato come API e non attinge alla quota inclusa nel piano ChatGPT.
Per **ZCode di Z.ai**, la documentazione ufficiale corrente presenta un'app desktop/ADE, non una
CLI pubblica autonoma. Esiste `zcode-app-cli`, ma è un progetto comunitario che si dichiara non
ufficiale ed estrae il runtime distribuito con l'app. Non esiste una garanzia ufficiale che riceva
un presunto bonus di quota del 50%; la documentazione corrente non promette neppure un diritto
fisso “150%” per tutti gli utenti ZCode.
## 1. OMP + GPT-5.6 Sol oppure Codex?
### Il modello è solo una parte del risultato
Usare lo stesso modello non rende equivalenti due agenti. L'harness decide prompt di sistema,
selezione e schema dei tool, raccolta del contesto, compaction, gestione degli errori, permessi,
parallelismo e verifica. Non risultano benchmark first-party che confrontino direttamente
GPT-5.6 Sol dentro OMP contro lo stesso modello dentro Codex: un vincitore assoluto non è quindi
dimostrabile.
GPT-5.6 Sol è il modello flagship general-purpose della famiglia 5.6 e supporta Responses API,
function calling, structured outputs, hosted shell, apply patch, skills, MCP e tool search.
[Scheda ufficiale GPT-5.6 Sol](https://developers.openai.com/api/docs/models/gpt-5.6-sol).
### Accesso tramite abbonamento
OMP dichiara un provider **OpenAI Codex OAuth** e consente il login dal proprio harness.
[README OMP](https://github.com/can1357/oh-my-pi/blob/daf07999c2fee9b22edc7bf8fea1fb6272e0df5e/README.md).
La documentazione OpenAI, però, indica il login ChatGPT per l'accesso in abbonamento soltanto per
l'app desktop ChatGPT/Codex, Codex CLI e l'estensione IDE. Non include OMP tra i client supportati.
Questo non prova che OMP non funzioni, ma significa che compatibilità, continuità dell'accesso e
interpretazione della quota non sono garantite da OpenAI per quel percorso.
[Autenticazione ufficiale Codex](https://learn.chatgpt.com/docs/auth).
Con una chiave API la situazione è diversa: l'accesso al modello è ufficiale, ma è fatturato ai
prezzi API. OpenAI specifica inoltre che l'autenticazione API usa il pricing API invece dei crediti
inclusi nel piano ChatGPT.
[Pricing Codex](https://learn.chatgpt.com/docs/pricing),
[pricing del modello](https://developers.openai.com/api/docs/models/gpt-5.6-sol).
### Confronto operativo
| Priorità | Scelta migliore | Motivo |
|---|---|---|
| GPT-5.6 Sol con quota ChatGPT e supporto prevedibile | Codex ufficiale | Login, quota e aggiornamenti sono first-party |
| Esperienza terminale, scripting e CI | Codex CLI | Loop locale, `codex exec`, review e handoff cloud |
| Task paralleli, worktree e review visuale | App desktop Codex | Gestione visuale dei task e isolamento mediante Git worktree |
| ACP/Zed, molti provider, LSP/DAP e Agent Hub | OMP | Harness più estensibile e ricco di strumenti |
| OMP con stabilità contrattuale dell'accesso | OMP + API key | API ufficiale, ma costo separato dal piano ChatGPT |
La CLI ufficiale è consigliata per terminale, automazione e CI.
[Codex CLI](https://learn.chatgpt.com/docs/codex/cli). L'app desktop è più comoda per task
concorrenti isolati, diff visuali e passaggio fra checkout locale e worktree.
[Worktree Codex](https://learn.chatgpt.com/docs/environments/git-worktrees).
CLI e app non danno due quote separate: quando si accede con ChatGPT, fanno parte dello stesso
ecosistema Codex/ChatGPT e consumano la stessa quota del piano. Il consumo concreto dipende da
modello, lunghezza del contesto e complessità del task.
[Pricing e limiti](https://learn.chatgpt.com/docs/pricing).
### Giudizio
Per un flusso principale basato su GPT-5.6 Sol sceglierei **Codex CLI**; affiancherei l'app quando
servono task paralleli, worktree e review visuale. Sceglierei OMP come harness principale soltanto
se le sue capacità specifiche — soprattutto Zed/ACP, routing multi-provider o Agent Hub — valgono
più del supporto first-party. Se OMP deve essere affidabile nel tempo, userei una API key anziché
fondare il workflow sul login OAuth non documentato da OpenAI per client terzi.
## 2. Il “150%” di Codex non è quota aggiuntiva
Nella documentazione OpenAI, **1,5× indica la velocità del Fast mode**, non un aumento della quota.
Con GPT-5.6, Fast mode consuma crediti a **2,5×** il tasso Standard. È disponibile nei client
Codex ufficiali quando si accede con ChatGPT.
[Fast mode](https://learn.chatgpt.com/docs/agent-configuration/speed).
## 3. Esiste una CLI ufficiale ZCode?
La guida ufficiale ZCode offre download desktop per macOS, Windows e Linux e descrive ZCode come
ADE con terminale integrato. Non documenta un comando autonomo ufficiale equivalente a `codex`.
La presenza di directory chiamate `~/.zcode/cli/` riguarda il runtime/config interno e non equivale
alla pubblicazione di una CLI supportata.
[Installazione ZCode](https://zcode.z.ai/en/docs/install),
[FAQ ufficiale](https://zcode.z.ai/en/docs/qa).
Esiste il progetto comunitario
[`zcode-app-cli`](https://github.com/kingsword09/zcode-cli), installabile con npm. Il progetto si
definisce esplicitamente non affiliato né approvato da Z.ai e dichiara di estrarre il runtime
dall'app desktop. Va quindi considerato non ufficiale e soggetto a possibili rotture e problemi di
compatibilità o licenza.
### Ha il 150% della quota?
Non c'è una conferma ufficiale corrente. Le pagine ZCode attuali descrivono quote Coding Plan su
finestre di cinque ore e settimanali, quota MCP mensile, crediti e reset card promozionali/dinamiche;
non dichiarano un moltiplicatore fisso 150% applicabile alla CLI.
[Statistiche e quota ZCode](https://zcode.z.ai/en/docs/usage-stats),
[connessione al Coding Plan](https://zcode.z.ai/en/docs/configuration).
Alcuni benefici sono esplicitamente legati all'app e al login ZCode: per esempio le reset card
richiedono di essere connessi a ZCode, mentre gli idle-time task gratuiti sono una funzione
dell'app in rollout. Questo non autorizza a concludere che un client comunitario riceva gli stessi
benefici.
[ZCode Usage Stats](https://zcode.z.ai/en/docs/usage-stats),
[ZCode overview](https://zcode.z.ai/en/docs/welcome).
Inoltre, i termini del GLM Coding Plan avvertono che l'uso tramite strumenti non autorizzati o non
supportati può comportare restrizioni di alcuni benefici. Di conseguenza non userei una CLI
comunitaria con l'obiettivo specifico di ottenere quota extra.
[Termini del GLM Coding Plan](https://docs.z.ai/legal-agreement/subscription-terms).
La scelta prudente è usare l'app ZCode ufficiale — incluso il suo terminale integrato o Remote
Development — e considerare valido soltanto il saldo mostrato in tempo reale nell'app. Se Z.ai
pubblicherà una CLI ufficiale o una regola “+50%”, servirà una dichiarazione esplicita applicabile
alla versione e al piano usati.
@@ -1,148 +0,0 @@
# Pi + `pi-config` di Amos vs Oh My Pi
_Ricerca aggiornata al 7 settembre 2026. Fonti: esclusivamente repository, documentazione, sorgenti, issue tracker e release ufficiali dei progetti._
## Risposta breve
**Per la maggior parte degli sviluppatori che vuole un agente completo e pronto all'uso, sceglierei Oh My Pi (OMP), ma non con le impostazioni di sicurezza predefinite.** OMP integra provider, routing per ruolo, LSP/DAP, subagent, web, browser, sessioni, memoria, marketplace e una UX terminale molto più ampia. È un prodotto coerente, installabile e aggiornabile come tale.
**Sceglierei invece Pi con pezzi selezionati di `pi-config` se volessi un nucleo piccolo, leggibile e fortemente personalizzabile**, accettando di assemblare, verificare e mantenere personalmente ogni componente. Il vantaggio non è avere più funzioni: è sapere con precisione quali funzioni si stanno aggiungendo.
La prima distinzione è fondamentale:
- [`amosblomqvist/pi-config`](https://github.com/amosblomqvist/pi-config) **non è una distribuzione alternativa di Pi**. È la configurazione personale di Amos Blomqvist: una raccolta di estensioni e skill da copiare selettivamente sopra il [Pi ufficiale](https://github.com/earendil-works/pi). Il README invita esplicitamente a non installarla come un unico pacchetto e a non clonarla sopra la propria configurazione.
- Per “Oh My Pi” qui si intende [`can1357/oh-my-pi`](https://github.com/can1357/oh-my-pi), il fork integrato di Pi che si presenta come agente “batteries included”. Non è un semplice tema o dotfile pack.
Di conseguenza il confronto corretto è **Pi ufficiale + componenti scelti da `pi-config` e dai repository companion** contro **OMP come fork/prodotto integrato**.
## Confronto spalla a spalla
| Area | Pi + `pi-config` | Oh My Pi | Valutazione |
|---|---|---|---|
| Installazione | Prima si installa [Pi](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/README.md), poi si copiano singole estensioni/skill in `~/.pi/agent/`; alcuni componenti richiedono `npm install`, Chromium, Python o tool di sistema. Il [README di `pi-config`](https://github.com/amosblomqvist/pi-config#installation) raccomanda la selezione manuale. | Installer shell/PowerShell, Homebrew, Bun, Nix e `mise`, più binari multipiattaforma nelle [release](https://github.com/can1357/oh-my-pi/releases). | **OMP**: onboarding e aggiornamento più coerenti. |
| Filosofia | Pi è un harness terminale minimale, esteso tramite TypeScript, skill, prompt template, temi e pacchetti; evita intenzionalmente alcune funzionalità integrate, inclusi subagent e plan mode, per lasciarle alle estensioni ([README Pi](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/README.md)). `pi-config` porta questa filosofia all'estremo: si prendono solo i pezzi voluti. | Fork “batteries included”: molte capacità sono native o integrate e configurabili da una superficie comune ([README OMP](https://github.com/can1357/oh-my-pi)). | **Dipende**: controllo e semplicità a Pi; completezza a OMP. |
| Provider e modelli | `pi-config` non aggiunge provider. Eredita da Pi login per Anthropic/OpenAI/Copilot, numerosi provider API, servizi cloud, OpenRouter e modelli locali/custom ([provider Pi](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/README.md#providers-and-models)). | Dichiara oltre 60 provider e modelli locali/remoti; assegna modelli distinti ai ruoli `default`, `smol`, `slow`, `plan`, `commit`, `vision`, `task`, `advisor` e `tiny`, con fallback e credenziali multiple ([model routing OMP](https://github.com/can1357/oh-my-pi#sixty-plus-providers-a-thousand-models-one-model-away)). | **OMP** per ampiezza e routing; Pi resta già provider-agnostic. |
| Prompt e istruzioni | Pi supporta `AGENTS.md`, `SYSTEM.md`, `APPEND_SYSTEM.md` e prompt template. `prompt-snippets` aggiunge piccoli frammenti attivabili per singolo messaggio, poi azzerati ([sorgente/README](https://github.com/amosblomqvist/pi-config/tree/main/extensions/prompt-snippets)). | Stessi concetti di personalizzazione del prompt, con override globali/progetto/modello ([documentazione](https://github.com/can1357/oh-my-pi/blob/main/docs/system-prompt-customization.md)); aggiunge ruoli modello, advisor e agent personalizzati. | **OMP** per orchestrazione; **pi-config** ha la migliore micro-UX per regole effimere per messaggio. |
| Subagent | Non nativi nel core. Il companion [`pi-interactive-subagents`](https://github.com/amosblomqvist/pi-interactive-subagents) avvia agent asincroni in pannelli tmux, persistenti e pilotabili; include `scout`, `researcher` e `worker`, loadout a allowlist e nesting esplicito. | Subagent di prima classe con batch, modalità sincrona/asincrona, output strutturato, Agent Hub, steering/revive/kill e ricorsione controllata. L'isolamento del workspace esiste, ma è **opt-in**, non una proprietà automatica di ogni spawn ([task](https://github.com/can1357/oh-my-pi/blob/main/docs/tools/task.md)). | **OMP** per orchestrazione complessiva; **Amos** per pannelli tmux e minimo privilegio più semplice da verificare. |
| Tool di coding | Il core Pi espone un set volutamente piccolo; `pi-config` aggiunge soprattutto browser, fetch/search, guard e UI di domande. | Lettura/scrittura/editing e AST, grep/glob, shell ed evaluator persistenti, LSP, DAP, code review, security scan, checkpoint/rewind e altri tool elencati nel [README](https://github.com/can1357/oh-my-pi#thirty-one-first-class-tools). Alcuni sono disattivati inizialmente. | **OMP**, nettamente, per intelligence sul codice e debug. |
| Web e browser | [`browser`](https://github.com/amosblomqvist/pi-config/tree/main/extensions/browser) usa Playwright/Chromium headless ed è spento di default; è una singola pagina senza download/upload. `web-search` usa Google Custom Search e richiede API key/CSE ([sorgente](https://github.com/amosblomqvist/pi-config/blob/main/extensions/web-search/index.ts)); c'è anche `web-fetch`. | Browser/computer integrati e ricerca con numerosi backend, inclusi servizi a pagamento, locali/pubblici e motori specializzati ([README](https://github.com/can1357/oh-my-pi#web-search)). | **OMP** per copertura; `pi-config` è più piccolo e comprensibile. |
| Skill e plugin | Skill file-based native di Pi più quattro skill incluse: analisi sessioni, PDF, web debug e trascrizione YouTube ([inventario](https://github.com/amosblomqvist/pi-config#skills)). `learn`, dictation, memoria e subagent sono repository separati. | Skill caricate progressivamente tramite metadata e URI `skill://` ([skill docs](https://github.com/can1357/oh-my-pi/blob/main/docs/skills.md)); marketplace per plugin Git/local/catalogo, con skill, comandi, agent, hook, tool, MCP e LSP ([marketplace](https://github.com/can1357/oh-my-pi/blob/main/docs/marketplace.md)). | **OMP** per distribuzione e composizione. |
| MCP e interoperabilità | Dipende dalle capacità/estensioni del Pi base; `pi-config` non offre un livello MCP proprio. | Configurazione MCP utente/progetto e discovery di configurazioni provenienti anche da altri editor/agenti ([MCP docs](https://github.com/can1357/oh-my-pi/blob/main/docs/mcp-config.md)). | **OMP**. |
| UX/TUI | TUI Pi pulita con editor, fuzzy file search, immagini, shell, steering/follow-up e alberi di sessione. `ask-user-question` aggiunge un dialogo strutturato; snippets e pannelli tmux sono distintivi. [`pi-dictate`](https://github.com/amosblomqvist/pi-dictate) aggiunge dettatura Deepgram. | TUI più ricca con card dei tool, preview/accettazione edit, picker, Agent Hub e time-travel; include sia sintesi vocale sia STT tramite scorciatoia `Alt+H` ([README OMP](https://github.com/can1357/oh-my-pi)). | **OMP** in generale; la semplicità di Pi può essere un pregio. La voce non è esclusiva della configurazione Amos. |
| Sessioni | Pi salva JSONL ad albero, consente resume/fork/clone/tree e compaction conservando lo storico ([sessioni Pi](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/README.md#sessions)). | JSONL append-only, struttura ad albero, blob esterni e ricostruzione versionata ([formato](https://github.com/can1357/oh-my-pi/blob/main/docs/session.md)); dump/export/share/fork/resume sono documentati come operazioni native ([operazioni](https://github.com/can1357/oh-my-pi/blob/main/docs/session-operations-export-share-fork-resume.md)). | **OMP**, di poco, per operazioni integrate; i due condividono una base concettuale simile. |
| Memoria | [`pi-observational-memory`](https://github.com/amosblomqvist/pi-observational-memory) è opzionale e spento di default: observer LLM paralleli distillano i turni, una compaction deterministica crea un ledger e un consolidatore produce file Markdown per sessione. È auditabile, ma aggiunge costo e complessità. | Memoria spenta di default con backend `local`, Hindsight, Mnemopi e Sharpshooter; sommari/lezioni possono attraversare sessioni e alimentare skill, con esplicita avvertenza che la memoria può essere obsoleta ([memory docs](https://github.com/can1357/oh-my-pi/blob/main/docs/memory.md)). | **OMP** per scelta e integrazione; **Amos** per un modello per-sessione semplice da ispezionare. |
| Sicurezza applicativa | Pi dichiara di non avere un permission system integrato per filesystem, processi, rete o credenziali e consiglia container/microVM ([security Pi](https://github.com/earendil-works/pi#permissions--containerization)). [`bash-guard`](https://github.com/amosblomqvist/pi-config/tree/main/extensions/bash-guard) intercetta euristicamente solo chiamate al tool `bash`: non protegge `write`, `edit` né i comandi `!`. | Ha policy per tool e tre approval mode, ma il default è **`yolo`**; in tale modalità gli override di comandi bash critici non forzano il prompt. Anche quando si approva un comando non c'è contenimento di filesystem, rete o subprocessi ([approval docs](https://github.com/can1357/oh-my-pi/blob/main/docs/approval-mode.md)). L'offuscamento dei segreti esiste ma è spento di default ([secrets docs](https://github.com/can1357/oh-my-pi/blob/main/docs/secrets.md)). | **Nessun vincitore sicuro di default**. OMP offre controlli migliori, ma sceglie un default molto permissivo. |
| Isolamento | Nessun sandbox OS o worktree per-agent documentato; l'allowlist del loadout limita i tool, non il filesystem raggiungibile dai tool concessi. | Gli spawn normali condividono la `cwd` del parent. Workspace separato e merge patch/branch richiedono `task.isolation.enabled` **e** `isolated: true` sul task; l'opzione non è disponibile in plan mode ([task](https://github.com/can1357/oh-my-pi/blob/main/docs/tools/task.md#inputs)). Neppure questo è un sandbox OS. | **OMP** per isolamento anti-collisione opt-in; **parità negativa** come confine di sicurezza. |
| Portabilità | I componenti sono piccoli file TypeScript/Markdown copiabili, quindi il lock-in concettuale è basso. Però molte estensioni Amos importano ancora il vecchio scope `@mariozechner/*`; il Pi attuale usa `@earendil-works/*`. L'[advisory ufficiale](https://github.com/earendil-works/pi/security/advisories/GHSA-r95r-rj6r-c39x) depreca il vecchio pacchetto, quindi oggi serve una verifica/possibile migrazione degli import. | Binari e setup multipiattaforma; importa varie convenzioni esterne. Tuttavia si è allontanato dal Pi upstream: scope `@oh-my-pi`, runtime/test Bun, moduli nativi, auth e API proprie sono differenze dichiarate nella [guida di porting](https://github.com/can1357/oh-my-pi/blob/main/docs/porting-from-pi-mono.md). | **Pi + selezione manuale** per lock-in ridotto; **OMP** per portabilità operativa immediata. |
| Migrazione delle estensioni | È l'ambiente nativo della raccolta Amos, salvo la transizione di package scope appena citata. | Non tratta `.pi/extensions` come root nativa. Può leggere dichiarazioni `pi.extensions` nei manifest, ma il caricamento e le API non rendono la migrazione automaticamente compatibile ([extension loading](https://github.com/can1357/oh-my-pi/blob/main/docs/extension-loading.md)). | **Non è drop-in in nessuna direzione**; verificare ogni estensione. |
| Aggiornamenti e manutenzione | `pi-config` è un piccolo snapshot personale, senza release versionate; il [registro commit](https://github.com/amosblomqvist/pi-config/commits/main/) mostra pochissimi cambiamenti e l'integrazione è responsabilità dell'utente. I companion hanno cicli propri. | Distribuzione versionata con release frequenti e asset per piattaforma; al 7 settembre 2026 la release più recente è [`v18.1.13`](https://github.com/can1357/oh-my-pi/releases/tag/v18.1.13). | **OMP** per manutenzione di prodotto; le release molto rapide aumentano anche il rischio di churn. |
| Maturità pratica | Il Pi sottostante è un progetto attivo e maturo, ma `pi-config` non è testato o pubblicato come distribuzione unitaria. L'autore di `pi-dictate`, per esempio, lo presenta esplicitamente come tool personale mantenuto per il proprio uso ([README](https://github.com/amosblomqvist/pi-dictate)). | Repository ampio, migliaia di commit e cadenza di release elevata ([storia](https://github.com/can1357/oh-my-pi/commits/main/), [release](https://github.com/can1357/oh-my-pi/releases)). Più integrazione e utenti implicano più validazione reale, ma anche superficie di bug e regressioni maggiore. | **OMP** come prodotto; nessuna garanzia che “più grande” significhi “più stabile”. |
## Approfondimento: gestione dei subagenti
**Sì: OMP ha una gestione dei subagenti paragonabile e, come orchestratore automatico, più completa.** La proposta Amos non è però semplicemente inferiore: privilegia un diverso modello operativo, nel quale ogni agente vive in un vero pannello tmux che l'utente può osservare e usare direttamente, con un loadout strettamente autorizzato.
| Capacità | Pi + Amos `pi-interactive-subagents` | Oh My Pi |
|---|---|---|
| Esecuzione e fan-out | Sempre asincrono e non bloccante; più chiamate partono in parallelo e notificano il parent indipendentemente ([README, “How it works”](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#how-it-works)). Non è documentato un limite di concorrenza configurabile. | `task.batch` è attivo di default e accetta `tasks[]`; con `async.enabled=true` gli agenti sono job in background, altrimenti il parent attende. Un semaforo `task.maxConcurrency` limita sia sync sia async ([task: input, modi e limiti](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#modes--variants)). |
| Messaggi fra agenti | `subagent_message` corregge uno spawn in corsa al prossimo confine di turno o riapre quello concluso; `ask_question` permette al child di parcheggiarsi e interrogare il parent ([messaging](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#messaging)). | `hub send` consegna steering/follow-up, anche agli agenti parcheggiati, che vengono riattivati; la messaggistica peer è disponibile anche ai child ([task, “Notes”](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#notes)). Limite documentato: lo steering è testo libero, non uno stato condiviso strutturato di goal/todo. |
| Supervisione e intervento umano | Widget con stati `starting/active/waiting/stalled/running`, tool corrente e completamenti espandibili; il pannello tmux è la sessione reale, quindi l'utente può entrarvi e scrivere direttamente ([status widget](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#status-widget--configuration)). | `Alt+A` apre Agent Hub: roster/albero, attività, modello, costo/token, transcript live, steering, revive e kill; l'utente può mettere a fuoco la sessione del child e scrivergli ([Agent Hub](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/agent-hub.md)). **OMP non manca quindi della supervisione interattiva**; Amos la rende più concreta e terminal-native tramite pannelli separati. |
| Resume e persistenza | Registro nome→sessione persistente attraverso i riavvii; il resume ripristina lo snapshot del loadout originale. Supporta sessioni `standalone`, `lineage-only` o `fork` con contesto del parent ([resume](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#messaging), [session mode](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#session-mode)). | Salva output e transcript (`agent://`, `history://`); agenti idle/parcheggiati sono riattivabili anche dopo il resume del parent ([Agent Hub, “Persisted agents”](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/agent-hub.md#persisted-agents-and-advisors)). Eccezione importante: un task eseguito in workspace isolato viene smontato dopo merge/cattura patch e **non è riattivabile** ([task flow](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#flow)). |
| Definizioni e routing modelli | File Markdown in `.pi/agents` o `~/.pi/agent/agents`, con modello, thinking, skill, tool, `cwd` e modalità sessione; il singolo spawn può sovrascrivere il modello. Include tre profili (`scout`, `researcher`, `worker`) ([custom agents](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#custom-agents)). | File Markdown `.omp/agents`, agent inclusi e provenienti da estensioni/plugin; routing con override per nome, lista fallback e alias `modelRoles`, più effort per task, prewalk e advisor opzionali ([agent definition](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/task-agent-discovery.md#agent-definition-shape), [routing](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/task-agent-discovery.md#model-and-structured-output-precedence)). |
| Tool e sicurezza applicativa | Allowlist stretta: il child parte con `--no-extensions` e riceve soltanto i tool e le estensioni esplicitamente elencati; lo snapshot preserva il vincolo al resume ([tool access](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#tool-access-control)). È minimo privilegio applicativo, non sandbox OS. | Ogni definizione può limitare `tools` e `spawns`, e il limite di profondità rimuove `task`; però i child headless forzano `tools.approvalMode: yolo`, perché non hanno una UI locale per le conferme ([task flow](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#flow)). Di conseguenza la qualità dell'allowlist/deny policy del parent è un confine essenziale. |
| Agenti annidati | Solo se `subagent_agents` è presente; la lista autorizza nomi precisi a ogni livello e non esiste uno spawn senza profilo nominato ([tool access](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#tool-access-control)). Il parent attende anche i nipoti prima dell'auto-exit. | Supportati con policy `spawns` e limite `task.maxRecursionDepth` (default `2`); al limite il tool `task` viene rimosso ([recursion gating](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/task-agent-discovery.md#recursion-depth-gating)). Agent Hub conserva la gerarchia parent/child. |
| Filesystem, worktree e conflitti | `cwd` può assegnare una directory diversa, ma il README non documenta workspace/worktree isolati né merge automatici ([role folders](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#role-folders)). **Inferenza:** più worker scriventi nella stessa checkout possono quindi collidere; separare le `cwd` resta responsabilità dell'orchestratore/utente. | Lo spawn ordinario usa la `cwd` del parent. L'isolamento è disponibile soltanto con configurazione globale attiva, `isolated: true`, repository Git e fuori dal plan mode; può applicare patch o cherry-pickare un branch ([task modes](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#modes--variants)). Verifica l'applicabilità della patch; in caso di conflitto lascia l'artefatto per intervento manuale e preserva lo stash in branch mode ([gestione conflitti](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#notes)). |
| Output | Il risultato è l'ultimo messaggio assistant, inoltrato al parent; non è documentato un contratto JSON Schema né un merge di file ([auto-exit](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#auto-exit)). | `outputSchema` per item, modalità `permissive`/`strict`, risultato parsato e artefatti completi tramite `agent://`; il child deve concludere con `yield`, con fino a tre reminder ([task outputs](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#outputs), [flow](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#flow)). |
| Portabilità e prerequisiti | Questa variante è esplicitamente **tmux-only** e richiede Pi + tmux ([requirements](https://github.com/amosblomqvist/pi-interactive-subagents/blob/c3e8b53c0754ae5ccc19fdab5a7481ec039bc2f7/README.md#requirements)); l'upstream HazAT supporta più multiplexer, ma non è il componente qui confrontato. | Il runtime OMP è multipiattaforma; l'isolamento seleziona backend diversi per Linux, macOS e Windows e ricade su copia ricorsiva quando necessario ([backend di isolamento](https://github.com/can1357/oh-my-pi/blob/4c6feacddb6ff26e5219df7283bc371ad7e1de55/docs/tools/task.md#side-effects)). |
### Giudizio mirato
**Per orchestrazione di subagenti sceglierei OMP**, perché combina fan-out controllato, sync/async, Agent Hub, messaggistica peer, output tipizzato, nesting con limiti e isolamento anti-collisione opzionale. Quindi la risposta alla domanda “ce l'ha anche OMP?” è **sì, e sul piano funzionale offre di più**.
**Pi + Amos resta preferibile in due casi:** quando si vuole entrare fisicamente nei pannelli dei worker mentre lavorano, oppure quando si considera prioritaria una politica child “deny by default” molto leggibile. La sua gestione può essere ottimale per un power user tmux; non è però altrettanto completa come orchestratore automatico, soprattutto per output strutturato, concorrenza limitata e gestione/merge degli artefatti.
Due caveat impediscono un verdetto semplicistico:
1. in OMP “subagent isolato” non significa “ogni subagent”: senza entrambi i toggle necessari, gli agenti scriventi condividono la checkout;
2. isolamento e continuità sono in tensione: il child OMP isolato evita collisioni, ma dopo il merge/cleanup non può essere riattivato; uno non isolato può invece essere parcheggiato e ripreso.
## Cosa include davvero `pi-config`
Nel repository principale ci sono:
- `ask-user-question`: richiesta strutturata con popup TUI e serializzazione dell'interazione;
- `bash-guard`: conferma/blocco euristico di comandi shell pericolosi;
- `browser`: automazione Playwright su Chromium headless, disattivata inizialmente;
- `custom-header`: header TUI personalizzato;
- `prompt-snippets`: regole brevi attivabili sul singolo messaggio;
- `web-fetch` e `web-search`;
- skill per analisi sessioni, PDF, debug web e trascrizione YouTube.
L'elenco e i prerequisiti sono nel [README ufficiale](https://github.com/amosblomqvist/pi-config). Le capacità più ambiziose sono in repository distinti:
- [`pi-interactive-subagents`](https://github.com/amosblomqvist/pi-interactive-subagents), subagent interattivi in tmux;
- [`pi-observational-memory`](https://github.com/amosblomqvist/pi-observational-memory), memoria osservazionale per sessione;
- [`pi-dictate`](https://github.com/amosblomqvist/pi-dictate), STT con Deepgram;
- [`learn`](https://github.com/amosblomqvist/learn), ambiente didattico con quiz, log e agent di ricerca/visualizzazione.
Questi componenti **non formano automaticamente una singola installazione testata, aggiornata e versionata insieme**. Considerarli una “suite” è un'inferenza utile per il confronto, non una promessa del maintainer.
## Dove OMP è realmente superiore
1. **È coerente come prodotto.** Installazione, configurazione YAML, schema, tool, sessioni e aggiornamenti fanno parte dello stesso rilascio ([settings](https://github.com/can1357/oh-my-pi/blob/main/docs/settings.md)).
2. **Il routing dei modelli è molto più sofisticato.** Non si sceglie soltanto un modello: si possono assegnare costi/capacità differenti a pianificazione, task, vision, commit, advisor e attività leggere.
3. **L'intelligence sul codice è integrata.** LSP, DAP, editing AST, evaluator persistenti e review non richiedono di costruire un proprio stack di estensioni.
4. **Subagent e memoria sono parti del sistema**, non componenti companion da sincronizzare a mano.
5. **Ha più opzioni di interoperabilità**: MCP, marketplace e discovery di configurazioni da altri strumenti.
Questa superiorità è soprattutto di **copertura e integrazione**, non una prova automatica di qualità superiore per ogni singola funzione. Le quantità dichiarate nel README di OMP sono affermazioni del progetto, non benchmark indipendenti.
## Dove Pi + la configurazione Amos è migliore
1. **È più facile capire il perimetro.** Ogni estensione è piccola, selezionabile e sostituibile. Si può usare `prompt-snippets` senza accettare browser, memoria o subagent.
2. **Ha meno lock-in architetturale.** Le skill Markdown e molte estensioni TypeScript restano vicine all'ecosistema Pi, anche se oggi gli import verso il vecchio package scope richiedono attenzione.
3. **Alcune idee sono più eleganti che “integrate”.** Gli snippet effimeri per messaggio, gli agent visibili nei pannelli tmux e la memoria in file Markdown per sessione sono facili da osservare e modificare.
4. **Favorisce l'apprendimento del sistema.** È una buona base per chi vuole costruirsi il proprio harness anziché adottare una piattaforma già opinionata.
Il prezzo è tempo operativo: installazione, dipendenze, compatibilità, aggiornamenti e test ricadono sull'utente.
## Sicurezza: la conclusione scomoda
**Né Pi + `pi-config` né OMP forniscono, da soli, un sandbox di sicurezza.**
- In Pi, `bash-guard` è un buon guardrail UX, ma non vede tutte le scritture e non contiene il processo. Le estensioni Pi hanno accesso al sistema con i privilegi dell'utente; la documentazione raccomanda esplicitamente container o microVM ([Pi security](https://github.com/earendil-works/pi#permissions--containerization)).
- In OMP esistono più policy, deny list e modalità di approvazione. Tuttavia `tools.approvalMode` parte da **`yolo`**, i safety override bash non diventano prompt in quella modalità, un comando approvato conserva accesso ambientale e le estensioni girano nello stesso processo ([approval mode](https://github.com/can1357/oh-my-pi/blob/main/docs/approval-mode.md), [extension loading](https://github.com/can1357/oh-my-pi/blob/main/docs/extension-loading.md)). Anche la protezione dei segreti è opt-in.
Se scegliessi OMP imposterei subito almeno:
1. `tools.approvalMode: always-ask` per un ambiente sensibile, oppure `write` come compromesso;
2. deny/prompt espliciti per evaluator, browser/computer e tool non necessari;
3. offuscamento segreti abilitato e configurato;
4. esecuzione in container, VM o microVM quando repository, credenziali o rete sono sensibili.
Con Pi farei la stessa cosa a livello OS e tratterei `bash-guard` come seconda cintura, non come sandbox.
## Raccomandazione per profilo
| Profilo | Scelta consigliata | Perché |
|---|---|---|
| Sviluppatore che vuole essere produttivo subito | **Oh My Pi** | Meno assemblaggio; LSP/DAP, modelli, agent e sessioni sono già integrati. |
| Power user multi-model / molti provider | **Oh My Pi** | Routing per ruolo, fallback e credenziali multiple sono capacità native. |
| Team che vuole una configurazione ripetibile | **Oh My Pi**, release fissata | Installer, Nix/mise, config e release versionate sono più riproducibili; fissare la versione riduce il churn. |
| Hacker di Pi che vuole costruire il proprio ambiente | **Pi + componenti `pi-config`** | Superficie ridotta, sorgenti leggibili, composizione libera. |
| Utente che vuole solo snippet, guard o browser | **Pi + singole estensioni** | Non serve adottare un fork molto più grande per tre capacità. |
| Chi apprezza subagent visibili e interattivi in tmux | **Pi + `pi-interactive-subagents`** | È una scelta UX specifica e ben distinta dall'Agent Hub. |
| Ambiente ad alta sicurezza | **Nessuno dei due senza isolamento OS** | I controlli applicativi non sostituiscono container/microVM; OMP va inoltre tolto da `yolo`. |
| Runtime Pi già integrato via RPC, come ThothII | **Restare su Pi salvo progetto di migrazione dedicato** | OMP offre RPC/ACP, ma package scope, caricamento estensioni, eventi e semantiche del fork richiedono test contrattuali: non è una sostituzione drop-in. |
### Nota specifica per ThothII
Per usare un agente nel terminale del repository, OMP può essere valutato senza cambiare l'architettura. **Sostituire invece il processo Pi che ThothII avvia in modalità RPC è un'altra decisione.** ThothII dipende dal contratto degli eventi RPC, dall'estensione gate, dal resume e dal comportamento di sessione. La [guida di porting di OMP](https://github.com/can1357/oh-my-pi/blob/main/docs/porting-from-pi-mono.md) e la sua [documentazione di caricamento estensioni](https://github.com/can1357/oh-my-pi/blob/main/docs/extension-loading.md) mostrano divergenze sufficienti da richiedere almeno una suite di compatibilità end-to-end prima di considerarlo un sostituto.
## Verdetto
**Il migliore in assoluto, per me, è Oh My Pi — con una release fissata e una configurazione iniziale più restrittiva del default.** Vince quasi tutte le categorie funzionali e riduce drasticamente il lavoro di integrazione.
Non lo sceglierei però “alla cieca”: `yolo` come default è un caveat serio, la superficie enorme rende probabile qualche regressione e il fork crea più dipendenza dalle proprie API. Per una workstation con codice e credenziali reali lo metterei dietro approvazioni esplicite e isolamento OS.
**Pi + `pi-config` è la scelta migliore quando l'obiettivo è un ambiente personale minimale e intenzionale**, non quando si cerca il maggior numero di funzioni. Installerei solo i componenti necessari, controllerei gli import dopo la migrazione da `@mariozechner/*` a `@earendil-works/*` e aggiungerei test prima di usarli in un flusso critico.
@@ -1,44 +0,0 @@
# Z.ai Coding Plan: bonus di quota e client CLI
Verifica effettuata il **7 settembre 2026**, esclusivamente su fonti ufficiali Z.ai/ZCode.
## Risposta breve
**No: Z.ai non documenta un bonus permanente del 150% (+50%) per un altro harness CLI.** La sola pagina ufficiale che usa oggi l'espressione “150% quota boost” è quella di **AutoClaw**: lo presenta come vantaggio a tempo limitato per piani Individual e Team, ma non precisa se significhi quota finale al 150% o incremento del 150%. AutoClaw è inoltre descritto come applicazione desktop per macOS e Windows, non come CLI da installare su un server. ([AutoClaw](https://autoclaw.z.ai/))
C'è però una promozione temporanea più interessante e formulata senza ambiguità: dal **3 al 20 settembre 2026**, ogni giorno fra le **23:00 e le 09:00 UTC+8**, GLM-5.3-Flash consuma zero quota in ZCode, mentre negli **altri agenti ufficialmente supportati la quota disponibile è raddoppiata**. In Italia, nel periodo della campagna, la finestra corrisponde alle **17:00–03:00 CEST**. La promozione vale per tutti i piani a pagamento e soltanto per GLM-5.3-Flash; GLM-5.3 continua a consumare la quota normale. ([GLM-5.3-Flash Usage Campaign](https://docs.z.ai/devpack/notice/event-glm-5.3-flash))
## Quali alternative CLI sono ufficialmente ammesse
La pagina corrente dei tool supportati nomina espressamente questi client utilizzabili da terminale:
| Harness | CLI/server | Stato nella documentazione Z.ai | Quota |
|---|---:|---|---|
| **Pi** | Sì | Esplicitamente incluso fra i Coding Agent Tool | Quota ordinaria; **2× su GLM-5.3-Flash nella finestra della campagna** |
| **Codex** | Sì | Esplicitamente incluso fra i Coding Agent Tool | Come sopra |
| **Claude Code** | Sì | Supportato e corredato da guida CLI ufficiale Z.ai | Come sopra |
| **OpenCode** | Sì | Supportato; guida ufficiale intitolata esplicitamente “OpenCode CLI” | Come sopra |
| **Droid** | Sì | Descritto da Z.ai come agente che gira nel terminale | Come sopra |
| **Crush** | Sì | Descritto esplicitamente come CLI/TUI | Come sopra |
| **Goose** | Sì | Incluso fra i Coding Agent Tool, con esecuzione locale | Come sopra |
| **Oh My Pi (OMP)** | Sì, tecnicamente | **Non è nominato** nell'elenco ufficiale; Z.ai nomina Pi, non OMP | Bonus e conformità **non confermati ufficialmente** |
Fonti: [elenco ufficiale dei tool e endpoint Coding Plan](https://docs.z.ai/devpack/tool/others), [guida Claude Code](https://docs.z.ai/devpack/tool/claude), [guida OpenCode CLI](https://docs.z.ai/devpack/tool/opencode).
Z.ai espone endpoint Coding Plan per Anthropic Messages, OpenAI Chat Completions e OpenAI Responses, ma questo **non rende automaticamente autorizzato qualunque client compatibile**: le condizioni limitano la quota ai tool ufficialmente supportati e avvertono che l'uso con integrazioni non autorizzate può comportare restrizioni. ([Subscription Terms](https://docs.z.ai/legal-agreement/subscription-terms), [Usage Policy](https://docs.z.ai/devpack/usage-policy))
## Implicazione pratica per il server
Per il caso in esame sceglierei **Pi + configurazione Amos direttamente sul server**: Pi è ora nominato ufficialmente da Z.ai, funziona da terminale e, durante la campagna attuale, rientra ragionevolmente negli “other supported Agents” con quota raddoppiata per GLM-5.3-Flash. La configurazione Amos estende Pi senza sostituire il client; resta comunque prudente verificare nel pannello consumi che le chiamate vengano classificate come Pi.
La seconda scelta è **OpenCode**, perché Z.ai fornisce una procedura CLI esplicita (`opencode auth login` → `Z.AI Coding Plan`). Claude Code è altrettanto documentato, ma la scelta dipende dalla preferenza per il suo harness.
Non sceglierei OMP confidando nel bonus: benché possa usare endpoint compatibili, **Oh My Pi non compare per nome** nell'elenco autorizzato. La risposta ufficialmente difendibile è quindi: **Pi sì; OMP non confermato**.
Infine, ZCode dispone oggi di pacchetti Linux, ma la documentazione li definisce sempre **desktop app** (`.AppImage`, `.deb`, `.rpm`) da lanciare con interfaccia grafica; non documenta una modalità CLI/headless equivalente a Pi o OpenCode. ([Installazione ZCode](https://zcode.z.ai/en/docs/install))
## Verdetto
- Se si cerca **esattamente un +50% permanente**, non risulta alcun harness CLI ufficialmente documentato.
- Se si vuole sfruttare la **promozione corrente**, Pi, Codex, Claude Code, OpenCode, Droid, Crush e Goose sono alternative CLI ufficialmente supportate; nel periodo e nella fascia indicati ottengono **2×**, non 150%, usando GLM-5.3-Flash.
- Per questa infrastruttura sceglierei **Pi+Amos sul server**, oppure **OpenCode** se si desidera il percorso d'installazione più esplicitamente documentato da Z.ai.
@@ -1,67 +0,0 @@
# ACP di Zed, Oh My Pi e Pi
_Verifica effettuata il 7 settembre 2026 su documentazione e codice sorgente primari._
## In breve
ACP (Agent Client Protocol) è un protocollo aperto che standardizza il collegamento tra un
editor/IDE e un coding agent. Il paragone utile è con LSP: LSP standardizza editor ↔ language
server, ACP standardizza editor ↔ agente. Il client (per esempio Zed) ospita l'interfaccia;
l'agent process conserva normalmente runtime, modelli, autenticazione, strumenti e configurazione.
ACP usa JSON-RPC 2.0. Copre inizializzazione e autenticazione, creazione/ripristino delle sessioni,
prompt e cancellazione, streaming di testo e pensieri, piani e tool call, comandi, richieste di
permesso e — se entrambe le parti lo supportano — operazioni su filesystem e terminale.
Fonti: [introduzione ACP](https://agentclientprotocol.com/overview/introduction),
[flusso e metodi del protocollo](https://agentclientprotocol.com/protocol/overview),
[External Agents in Zed](https://zed.dev/docs/ai/external-agents).
## Confronto in Zed
| Aspetto | Oh My Pi | Pi |
|---|---|---|
| Tipo di integrazione | Server ACP incorporato: `omp acp` | Adapter comunitario `pi-acp`, installabile dal registry di Zed |
| Collegamento al motore | ACP è una modalità dello stesso motore OMP | L'adapter avvia `pi --mode rpc` e traduce RPC ↔ ACP |
| Output e tool call | Streaming e tool call ACP nativi | Streaming, tool card, posizioni nei file e diff strutturati tradotti dall'adapter |
| File | Può inoltrare `read`/`write` al filesystem del client | Nessuna delega ACP `fs/*`; Pi legge e scrive localmente |
| Terminale | Può creare e seguire terminali del client | Nessuna delega ACP `terminal/*`; i comandi girano localmente |
| Permessi | `edit` e `bash` possono usare `session/request_permission` nell'editor | La UI riceve i tool call, ma non ha la stessa integrazione nativa di file/terminale |
| Sessioni | Implementazione diretta di sessioni, comandi e configurazione ACP | Mappa le sessioni ACP ai file di sessione Pi e supporta `session/load` |
| Skills/comandi | Carica skills, estensioni e slash command OMP | Carica skills e comandi Pi; l'adapter aggiunge comandi per l'uso headless |
| MCP configurati in Zed | OMP contiene il plumbing ACP/MCP nel proprio server | Accettati nei parametri ACP ma non inoltrati a Pi dall'adapter corrente |
| Maturità dichiarata | Funzionalità first-class del progetto | L'adapter si definisce “MVP-style” e centrato soprattutto su Zed |
L'integrazione OMP non è solo una dichiarazione nel README: il comando ACP è parte del sorgente e
il `ClientBridge` instrada `read`, `write`, `bash`, `edit` e richieste di permesso verso il client
quando Zed annuncia le capacità corrispondenti. Fonti:
[README OMP](https://github.com/can1357/oh-my-pi/blob/daf07999c2fee9b22edc7bf8fea1fb6272e0df5e/README.md),
[comando ACP](https://github.com/can1357/oh-my-pi/blob/daf07999c2fee9b22edc7bf8fea1fb6272e0df5e/packages/coding-agent/src/commands/acp.ts),
[ACP ClientBridge](https://github.com/can1357/oh-my-pi/blob/daf07999c2fee9b22edc7bf8fea1fb6272e0df5e/packages/coding-agent/src/modes/acp/acp-client-bridge.ts).
Pi è comunque supportato esplicitamente da Zed: si installa `pi ACP` dal registry. L'integrazione
è però un progetto separato e non una modalità presente nel core Pi. L'adapter corrente conserva
molto dell'esperienza utile — streaming, tool call, diff, resume, comandi e skills — ma dichiara
esplicitamente di non delegare filesystem o terminale a Zed e di non collegare a Pi gli MCP server
ricevuti dal client. Fonti:
[scheda Pi nel registry Zed](https://zed.dev/acp/agent/pi),
[manifest del registry](https://github.com/agentclientprotocol/registry/blob/9ec416a76f69c9ff8a8316931e38f4c74ad41fa8/pi-acp/agent.json),
[README e limiti di `pi-acp`](https://github.com/svkozak/pi-acp/blob/d1cffc047ab37a096ee70ca39cfc1de463db8d12/README.md),
[RPC di Pi](https://github.com/earendil-works/pi/blob/9211da172325117dc59e6f9f25f248dc628b3f81/packages/coding-agent/docs/rpc.md).
## Giudizio
**OMP si trova meglio con ACP perché ACP è una sua interfaccia nativa e il suo livello strumenti è
stato progettato per delegare operazioni all'editor.** Zed non è soltanto una finestra per la chat:
partecipa a file, terminale e autorizzazioni.
**Pi si trova comunque bene con Zed per l'uso quotidiano**, specialmente per conversazione,
streaming, modifiche, diff e ripresa delle sessioni. Oggi, però, l'integrazione è meno profonda:
Zed controlla un adapter che controlla Pi via RPC, mentre file e shell rimangono dal lato Pi.
Per Pi+Amos, i subagenti tmux non diventano automaticamente thread/subagenti nativi di Zed: ACP
espone la sessione Pi principale, mentre l'orchestrazione Amos continua nel proprio runtime e nei
pane tmux. È quindi una combinazione possibile, ma per osservare e pilotare direttamente quei pane
l'esperienza terminale/tmux resta più fedele. Questa conclusione è un'inferenza dall'architettura
dell'adapter (`pi-acp` avvia una singola sessione Pi RPC per sessione ACP) e dai limiti dichiarati,
non una garanzia esplicita del progetto Amos.
@@ -0,0 +1,79 @@
# Qwen 3.6: session tool-call failure and correction
Date: 2026-09-21. Verified runtime: Pi coding agent and Pi AI 0.80.3.
## Cause and correction
Interactive sessions returned text resembling a bash call and stopped before the
first review widget. The Evidence-only extension `tht-evidence-json-mode.ts` lived
under `.pi/extensions`, so Pi automatically loaded it into interactive sessions.
Its `before_provider_request` hook imposed `response_format: {type: "json_object"}`
and temperature zero on every request.
Replaying the captured startup request, with all original hooks preserved, isolated
the cause. With JSON response format, Qwen returned the command as text and finish
reason `stop`. Removing only that field produced a native `bash` call and finish
reason `tool_calls`. Both requests contained the same 15 tools and used High thinking.
The extension now lives in `.pi/evidence-extensions` and is loaded explicitly by
`PiEvidenceRestructurer`. Evidence retains JSON output. Interactive sessions retain
native tool calls. No changes to the remote Qwen server were required.
Earlier SDK probes replaced `session.agent.onPayload`, inadvertently bypassing the
extension hooks. Their success did not reproduce the application path and did not
establish a model or server fault.
## Model configuration
The installation catalog now accepts optional `session.compatibility.thinkingFormat`
values `qwen` and `qwen-chat-template`, requiring `reasoning: true`. Go validation,
the backend schema, and generated catalog/Pi projections preserve this setting.
It controls thinking; it was not the cause or correction of the tool-call failure.
For the verified endpoint, `qwen-chat-template` sends `enable_thinking` and
`preserve_thinking` inside `chat_template_kwargs`. Selecting Off explicitly disables
thinking. Declaring `reasoning: false` alone does not disable thinking on the server.
See the [operator configuration](../general/pi-configuration.md#qwen-36-sessions-and-thinking-controls)
and [Pi 0.80.3 model documentation](https://github.com/earendil-works/pi/blob/v0.80.3/packages/coding-agent/docs/models.md#openai-compatibility).
## Validation and local delivery
- Catalog and projection regression tests failed before the compatibility change,
then passed. Go config/modelprojection/CLI tests, 84 targeted backend tests,
TypeScript checking, and the strict documentation build passed.
- The extension-isolation regression failed before relocation. All 46 Evidence
authoring/restructurer tests and Ruff checks on changed Python files passed.
- The rebuilt local core image has digest
`sha256:9cd593d7362dcbefe177f1b9eb4b9ffdf3010ced7bd7efc8fc3fdcd288f24579`.
Core/frontend were recreated, healthy, and returned HTTP 200.
- A real `pi --mode rpc --no-session` probe with Qwen and High thinking executed
`tht session show` successfully and reached the first clarification widget.
The probe allowed only that read and `reviewer_select`; it stopped without
submitting a human response or recording decisions. Its temporary configuration
referenced the mounted credential because the original runtime lease had expired.
- The user subsequently confirmed that the application now works.
This verifies recovery from the startup failure. It does not certify every workflow
phase or the separate LiteLLM metadata-generation path.
## Public manual publication
- Source: `84084bba37d4985658174db04f8e9195c0435d72`, pushed to Gitea `main`.
- Actions generated `pages` commit `1fe88f99228e06d10e8ecd92556c1ef0d52526e8`
for that exact source revision.
- Live release: `20260921T142437Z-84084bba`; previous release retained for rollback:
`20260916-css-b1c510a0`.
- Built from a clean checkout with the locked strict MkDocs build and all four
authentication documentation verification scripts passing. The public boundary
contains exactly 20 pages. The committed Go tests, Evidence tests, targeted
backend tests, and TypeScript check also passed from that checkout.
- Recreated only the `thothii-docs` Compose service. It is healthy, its mounted
`current` release is correct, and `nginx -t` passed.
- Anonymous requests to home, search, both installation manuals, and the model
configuration page returned 200 with bytes matching the clean build. Styles,
scripts, and referenced fonts returned 200 with the expected content types.
The retired/internal overview, memory-plan, and Compose-reference paths returned 404.
- Browser checks of both installation manuals and the Qwen section confirmed
loaded stylesheets and styled, readable layouts.
Published page: [Qwen 3.6 configuration](https://git.tylconsulting.it/thothii-docs/general/pi-configuration/#qwen-36-sessions-and-thinking-controls).
-33
View File
@@ -1,33 +0,0 @@
# Prodotti text-to-SQL e natural-language-to-SQL
Ricognizione di prodotti e siti ufficiali che consentono di interrogare dati strutturati
in linguaggio naturale e/o di generare SQL. Sono escluse pubblicazioni accademiche,
benchmark e articoli di terze parti. Le descrizioni riportano solo capacità dichiarate
nelle fonti ufficiali collegate.
Data della ricerca: 2026-09-09.
Nota di verifica: al 2026-09-09 SQLPilot non è stato verificato come raggiungibile. Il fetch
del sito ufficiale `https://sqlpilot.ai/` restituisce `502 Bad Gateway`; anche
`https://www.sqlpilot.ai/`, `http://sqlpilot.ai/`, `/download` e `/signup` non risultano
raggiungibili dal controllo diretto (risoluzione DNS fallita), senza redirect osservabili.
La pagina ufficiale indicizzata in precedenza resta una fonte storica, non una conferma di
disponibilità odierna. Non è stato individuato un URL ufficiale alternativo funzionante.
| Prodotto | URL ufficiale | Descrizione verificabile | Modello |
|---|---|---|---|
| [Vanna AI](https://vanna.ai/) | [Sito](https://vanna.ai/) · [Documentazione](https://vanna.ai/docs/index.html) | Framework/agente SQL che permette agli utenti di porre domande in linguaggio naturale su un database. La documentazione descrive un flusso RAG: si addestra il modello con SQL, DDL e documentazione, poi `ask` genera SQL che può essere eseguito sul database. | Open source; disponibile anche come servizio/soluzione hosted ed enterprise. |
| [Wren AI](https://github.com/Canner/WrenAI) | [Repository ufficiale](https://github.com/Canner/WrenAI) · [Documentazione CLI](https://github.com/Canner/WrenAI/blob/main/docs/core/reference/cli.md) | Layer semantico open source per agenti e applicazioni GenBI. Il progetto dichiara supporto a text-to-SQL su oltre 20 sorgenti dati; la CLI conserva coppie natural-language-to-SQL e usa il contesto semantico/MDL per scrivere ed eseguire query. | Open source (licenza Apache-2.0 per il core dichiarato nel repository). |
| [Dataherald](https://github.com/Dataherald/dataherald) | [Repository ufficiale](https://github.com/Dataherald/dataherald) | Engine natural-language-to-SQL per domande su dati relazionali. Il repository descrive un’API che espone un database in modo interrogabile in linguaggio naturale e include componenti per engine, API enterprise, console amministrativa e Slackbot. | Open source (Apache-2.0); include componenti enterprise self-hosted. |
| [DB-GPT](https://github.com/eosphoros-ai/DB-GPT) | [Repository ufficiale](https://github.com/eosphoros-ai/DB-GPT) · [DB-GPT-Hub](https://github.com/eosphoros-ai/DB-GPT-Hub) | Framework open source per applicazioni data-driven e agenti, con un workflow Text-to-SQL documentato e un progetto Hub per dataset, modelli e fine-tuning dedicati alla conversione testo-SQL. | Open source. |
| [Snowflake Cortex Analyst](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-analyst) | [Documentazione ufficiale](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-analyst) | Servizio gestito di Snowflake con cui utenti business pongono domande in linguaggio naturale e ricevono risposte senza scrivere SQL. La documentazione lo descrive esplicitamente come sistema agentico che genera risposte text-to-SQL, usando semantic model/views per il contesto. | SaaS/enterprise gestito nella piattaforma Snowflake. |
| [Databricks Genie / Genie Agents](https://docs.databricks.com/gcp/en/genie-agents/concepts) | [Documentazione ufficiale](https://docs.databricks.com/gcp/en/genie-agents/concepts) · [API](https://docs.databricks.com/gcp/en/genie-agents/conversation-api) | Funzionalità Databricks per interagire con dati tramite linguaggio naturale. La documentazione dichiara che Genie converte i prompt in SQL, restituisce quando possibile la query generata e i risultati, e può porre domande di chiarimento. | SaaS/enterprise, integrato in Databricks. |
| [ThoughtSpot Spotter](https://www.thoughtspot.com/product/spotter-semantics) | [Pagina prodotto ufficiale](https://www.thoughtspot.com/product/spotter-semantics) · [SpotGuide](https://tsa-guide.thoughtspot.com/) | Interfaccia di analytics conversazionale: l’utente descrive ciò che vuole in linguaggio naturale e il motore genera query SQL deterministiche tramite il layer semantico. La guida ufficiale presenta Spotter come esperienza “no SQL” per cercare e interrogare i dati. | SaaS/enterprise analytics. |
| [Seek AI](https://www.seek.ai/ai-data-analyst) | [AI Data Analyst](https://www.seek.ai/ai-data-analyst) · [Product Overview](https://www.seek.ai/product-overview) | Piattaforma/agent per dati strutturati con un Dialogue Agent che interpreta domande in linguaggio naturale e un Semantic Parsing Agent che genera query; l’offerta include anche un’interfaccia embedded per prodotti SaaS e un’app nativa Snowflake. | SaaS/enterprise; disponibile anche come componente embedded e Snowflake Native App. |
| ~~SQLPilot~~ | [Sito ufficiale](https://sqlpilot.ai/) | **Non verificato al 2026-09-09**: il dominio ufficiale non è risultato raggiungibile (502/DNS) e non è stato trovato un redirect o un URL ufficiale alternativo funzionante. Una precedente indicizzazione ufficiale descriveva un editor SQL assistito da AI, ma non costituisce conferma di disponibilità odierna. | Non verificato. |
## Note di perimetro
- “Open source” indica un progetto il cui repository ufficiale dichiara una licenza o un core open source; non implica che eventuali servizi hosted siano gratuiti.
- “SaaS/enterprise” indica un prodotto gestito o venduto come piattaforma aziendale; le fonti ufficiali non sempre pubblicano prezzi o dettagli contrattuali.
- Le capacità possono dipendere dal database collegato, dal modello semantico configurato e dai permessi dell’installazione.
+3 -2
View File
@@ -62,8 +62,9 @@ to the reviewer. A correction can reopen the CTE plan without discarding decisio
## F8: Memory promotion ## F8: Memory promotion
At the end of the session, the system proposes reusable clarifications. The reviewer decides which At the end of the session, the system proposes reusable Memory cards, including the approved
ones to promote to the global registry, and the session is then finalized. solved question. The reviewer selects and edits the additions or updates to retain in the
workspace's Memory archive, or declines them all. The session is then finalized.
## Available gates ## Available gates
@@ -60,8 +60,8 @@ Il comportamento da verificare deriva da:
- stato corrente del progetto in `PROJECT_STATE.md`; - stato corrente del progetto in `PROJECT_STATE.md`;
- [contratto Catalog Schema Snapshot](../contracts/catalog-schema-snapshot.md); - [contratto Catalog Schema Snapshot](../contracts/catalog-schema-snapshot.md);
- [piano del Metadata Catalog](../plans/2026-08-26-metadata-catalog-from-thothai.md); - [piano del Metadata Catalog](../reports/knowledge-archives-release.md);
- [specifica della generazione descrizioni](../plans/2026-08-28-ai-catalog-description-generation-spec.md); - [specifica della generazione descrizioni](../reports/knowledge-archives-release.md);
- [ADR 0007: sincronizzazione autorevole durevole](../adr/0007-durable-authoritative-schema-synchronization.md); - [ADR 0007: sincronizzazione autorevole durevole](../adr/0007-durable-authoritative-schema-synchronization.md);
- [ADR 0009: un solo run sequenziale di generazione](../adr/0009-use-one-sequential-description-generation-run.md); - [ADR 0009: un solo run sequenziale di generazione](../adr/0009-use-one-sequential-description-generation-run.md);
- [ADR 0010: campioni reali limitati](../adr/0010-allow-bounded-real-source-samples-for-description-generation.md); - [ADR 0010: campioni reali limitati](../adr/0010-allow-bounded-real-source-samples-for-description-generation.md);
+50
View File
@@ -0,0 +1,50 @@
# Use and administer Memory
Memory stores reusable knowledge for a workspace. It is separate from Evidence sources
and from the saved documents of an individual session.
| Card family | Purpose |
| --- | --- |
| Domain clarification | Define a term or interpretation, with its scope. |
| SQL rule | Explain how to construct SQL and why. |
| Solved question | Retain an approved question and SQL as a consultative example. |
| Explained error | Record a correction and its rationale. |
## Browse and edit
Open **Administration → Memory management** and select the workspace. Administration
requires the appropriate permissions. You can search, filter, open a card's complete
content, edit it, manage its dependencies and links, or explicitly confirm deletion.
Browsing and editing require the PostgreSQL archive but do not require an active session
or a working DWH or search index. Cards retain their identity and origin when edited;
there is no editorial revision history. Deleting a card also removes its links and
dependencies, not the other cards or the workspace's Evidence.
Links connect cards in the same workspace. Record scope and physical dependencies
carefully so knowledge is not applied to unrelated databases, tables or columns.
## Saved content and search availability
Saving a card and updating its search index are different outcomes. **Saved, index update
incomplete** means the content is already in the archive but is not ready for recall.
Use **Pending index updates** to retry. Do not create a duplicate card to work around an
indexing failure. A restart does not discard the pending operation.
Qdrant is a rebuildable search projection, not the authoritative archive. Rebuilding it
does not recover deleted cards from old sessions or vector payloads. Back up the
authoritative installation data before maintenance.
After a successful physical schema synchronization, cards dependent on removed database
objects can be deleted. Workspace-wide cards and unrelated dependencies remain. Review
the synchronization result and any pending cleanup before starting another synchronization.
## Memory in a reviewed session
The workflow proposes relevant clarifications, rules and examples. A search result is
not approval: the reviewer decides whether it applies. At the final Memory review,
select the additions or updates worth retaining, edit their content and scope, or decline
them all. Finalizing a session does not silently approve every proposed card.
Approved SQL is read-only at that final review. To change the solution, return to SQL
review. See the [workflow guide](../skills.md) and [user guide](../guida-utente.md).
+38
View File
@@ -80,6 +80,44 @@ async function navigation(page: Page) {
if (await trigger.isVisible()) await trigger.click(); if (await trigger.isVisible()) await trigger.click();
} }
for (const mode of ["full", "embedded"] as const) {
for (const viewport of [{ width: 1280, height: 720 }, { width: 390, height: 844 }]) {
test(`catalog configuration scrolls to all fields and actions in ${mode} at ${viewport.width}px`, async ({ page }, testInfo) => {
await page.setViewportSize(viewport);
const writes = await fixtures(page, mode);
await page.route("**/api/catalog/databases", route => route.fulfill({ json: [{
workspaceId: workspace, workspaceName: title, workspaceAvailable: true,
workspaceRevision: revision, workspaceEvidence: { sourceType: null, state: "not_declared" },
runtimeBinding: null, configured: false, engine: "postgres", databaseName: "warehouse",
schema: "public", version: 0, createdAt: "", updatedAt: "",
binding: { transport: "postgres_direct", port: 5432 }, connectionStatus: "untested",
secrets: { password: false, apiKey: false, sshPrivateKey: false,
sshPrivateKeyPassphrase: false, sshKnownHosts: false, tlsCa: false },
}] }));
await page.goto("/?thoth_route=administration/database");
await page.getByRole("button", { name: `Configure catalog for ${title}`, exact: true }).click();
const form = page.getByRole("region", { name: "Configure catalog form", exact: true });
await expect(form).toBeVisible();
const scrollArea = page.locator(".thot-fleet-ledger__workspace");
const box = await scrollArea.boundingBox();
expect(box).not.toBeNull();
await page.mouse.move(box!.x + box!.width / 2, box!.y + box!.height - 30);
await page.mouse.wheel(0, 3000);
await expect(form.getByRole("button", { name: "Save catalog configuration", exact: true })).toBeInViewport();
await expect(form.getByLabel("Password", { exact: true })).toBeInViewport();
expect(await scrollArea.evaluate(el => el.scrollTop)).toBeGreaterThan(0);
expect((await inspect(page)).overflow).toBe(0);
await page.screenshot({ path: testInfo.outputPath("catalog-configuration-bottom.png") });
await page.mouse.wheel(0, -3000);
await expect.poll(() => scrollArea.evaluate(el => el.scrollTop)).toBe(0);
await form.getByRole("button", { name: "Back to list", exact: true }).scrollIntoViewIfNeeded();
await form.getByRole("button", { name: "Back to list", exact: true }).click();
await expect(page.getByRole("button", { name: `Configure catalog for ${title}`, exact: true })).toBeVisible();
expect(writes).toEqual([]);
});
}
}
async function admin(page: Page, name: string) { async function admin(page: Page, name: string) {
await navigation(page); await navigation(page);
const toggle = page.getByRole("button", { name: "Administration", exact: true }); const toggle = page.getByRole("button", { name: "Administration", exact: true });
+1
View File
@@ -31,6 +31,7 @@ export interface SchemaTable {
export interface WidgetDescriptor { export interface WidgetDescriptor {
id: string; id: string;
interaction_language?: string;
schema_version?: number; schema_version?: number;
session_id?: string; session_id?: string;
phase?: string; phase?: string;
+12 -2
View File
@@ -1,4 +1,4 @@
import { useCallback, useSyncExternalStore } from "react"; import { createContext, useCallback, useContext, useSyncExternalStore, type ReactNode } from "react";
import { itCore } from "./locales/it-core"; import { itCore } from "./locales/it-core";
import { itAdmin } from "./locales/it-admin"; import { itAdmin } from "./locales/it-admin";
import { itWorkflow } from "./locales/it-workflow"; import { itWorkflow } from "./locales/it-workflow";
@@ -63,8 +63,18 @@ export function translate(message: string, params?: TranslationParams): string {
return translateIn(locale, message, params); return translateIn(locale, message, params);
} }
const InteractionLocale = createContext<string | undefined>(undefined);
/** Scope HITL controls to the session language without changing application navigation. */
export function InteractionLanguage({ language, children }: { language?: string; children: ReactNode }) {
return <InteractionLocale.Provider value={language ? resolveLocale(language) : undefined}>
{children}
</InteractionLocale.Provider>;
}
export function useI18n() { export function useI18n() {
const current = useSyncExternalStore(subscribe, getLocale, () => "en"); const uiLocale = useSyncExternalStore(subscribe, getLocale, () => "en");
const current = useContext(InteractionLocale) ?? uiLocale;
const t = useCallback( const t = useCallback(
(message: string, params?: TranslationParams) => translateIn(current, message, params), (message: string, params?: TranslationParams) => translateIn(current, message, params),
[current], [current],
@@ -1213,7 +1213,39 @@ test("closing and reopening Model activity preserves the complete activity log",
expect(useSessionStore.getState().activityLog).toEqual(beforeClose); expect(useSessionStore.getState().activityLog).toEqual(beforeClose);
}); });
test("session finalization shows the completion banner and returns to landing", async () => { test.each(["stop", "session_exit"])("%s restores the empty landing with model activity closed", async (exit) => {
const close = vi.fn(() => new HttpResponse(null, { status: 204 }));
server.use(
http.post("/api/sessions/:id/resume", () => resumeResult("s1")),
http.post("/api/sessions/:id/close", close),
);
wrap(adminUser);
await userEvent.click(await screen.findByText("Attiva uno"));
await userEvent.click(await screen.findByRole("button", { name: "Resume" }));
await userEvent.click(screen.getByRole("button", { name: "Show model activity" }));
act(() => useSessionStore.getState().setLastUserEntry({ kind: "input", text: "Previous question" }));
expect(screen.getByLabelText("Model activity timeline")).toHaveTextContent("Previous question");
const source = FakeEventSource.instances.at(-1)!;
if (exit === "stop") {
await userEvent.click(screen.getByRole("button", { name: /stop and save session/i }));
await userEvent.click(screen.getAllByRole("button", { name: "Stop & save" })[0]);
} else {
act(() => source.emit({ type: "system_event", event: "session_exit" }));
}
expect(await screen.findByText(/type your question/i)).toBeInTheDocument();
expect(screen.queryByRole("heading", { name: "Model activity" })).not.toBeInTheDocument();
expect(screen.getByTestId("app-shell")).toHaveAttribute("data-activity-layout", "closed");
expect(screen.queryByRole("complementary", { name: "Session summary" })).not.toBeInTheDocument();
expect(screen.getByRole("complementary", { name: "Session navigation" })).toBeVisible();
expect(screen.getByRole("textbox", { name: /new question/i })).toHaveValue("");
expect(useSessionStore.getState().activityLog).toEqual([]);
expect(source.closed).toBe(true);
expect(close).toHaveBeenCalledTimes(exit === "stop" ? 1 : 0);
});
test("a new question after finalization restores the landing and administration navigation", async () => {
useSessionStore.getState().resetSession(); useSessionStore.getState().resetSession();
let finalized = false; let finalized = false;
server.use( server.use(
@@ -1221,7 +1253,7 @@ test("session finalization shows the completion banner and returns to landing",
http.get("/api/sessions", () => http.get("/api/sessions", () =>
HttpResponse.json(finalized ? [{ ...LIST[0], status: "finalized" }] : LIST)), HttpResponse.json(finalized ? [{ ...LIST[0], status: "finalized" }] : LIST)),
); );
wrap(); wrap(adminUser);
await userEvent.click(await screen.findByText("Attiva uno")); await userEvent.click(await screen.findByText("Attiva uno"));
await userEvent.click(await screen.findByRole("button", { name: /resume/i })); await userEvent.click(await screen.findByRole("button", { name: /resume/i }));
// The final turn ends: the session is now finalized on disk and agent_end // The final turn ends: the session is now finalized on disk and agent_end
@@ -1233,9 +1265,18 @@ test("session finalization shows the completion banner and returns to landing",
}); });
const cta = await screen.findByRole("button", { name: /start a new question/i }); const cta = await screen.findByRole("button", { name: /start a new question/i });
expect(screen.getByText(/session completed and finalized/i)).toBeInTheDocument(); expect(screen.getByText(/session completed and finalized/i)).toBeInTheDocument();
await userEvent.click(screen.getByRole("button", { name: /show model activity/i }));
expect(screen.getByRole("heading", { name: "Model activity" })).toBeVisible();
expect(screen.queryByRole("complementary", { name: "Session navigation" })).not.toBeInTheDocument();
await userEvent.click(cta); await userEvent.click(cta);
// Landing state: the composer invites a brand-new question. // Landing state: the composer invites a brand-new question.
expect(await screen.findByText(/type your question/i)).toBeInTheDocument(); expect(await screen.findByText(/type your question/i)).toBeInTheDocument();
expect(screen.queryByRole("heading", { name: "Model activity" })).not.toBeInTheDocument();
expect(screen.queryByText(/session completed and finalized/i)).not.toBeInTheDocument();
const navigation = screen.getByRole("complementary", { name: "Session navigation" });
expect(within(navigation).getByRole("button", { name: "Administration" })).toBeVisible();
expect(screen.getByRole("textbox", { name: /new question/i })).toHaveValue("");
expect(useSessionStore.getState().activityLog).toEqual([]);
}); });
test("renaming a group reassigns its members via setSessionGroup", async () => { test("renaming a group reassigns its members via setSessionGroup", async () => {
+17 -15
View File
@@ -694,6 +694,20 @@ export function AppShell({ canLogout }: AppShellProps) {
authGeneration, authGeneration,
); );
function resetSessionView() {
invalidateResumeIntent();
newSessionOperationRef.current = null;
resetSession();
selectActiveSession(null);
// Panel visibility belongs to the shell, outside the session store.
// Clear it too so leaving a session restores the initial landing layout.
setPanelSession(null);
setShowActivity(false);
setCreatingSession(false);
setAwaitingQuestion(false);
setStopConfirm(false);
}
// A backend "session_exit" system event (e.g. the replay server emitting it // A backend "session_exit" system event (e.g. the replay server emitting it
// when the reviewer picks "Esci") asks us to leave the live session view and // when the reviewer picks "Esci") asks us to leave the live session view and
// return to the landing state. We deliberately do NOT also POST /close here — // return to the landing state. We deliberately do NOT also POST /close here —
@@ -705,10 +719,7 @@ export function AppShell({ canLogout }: AppShellProps) {
if (ev === "session_exit") { if (ev === "session_exit") {
// Never let a streamed event terminate the managed Pi child. Only the // Never let a streamed event terminate the managed Pi child. Only the
// explicit “Stop & save” action is allowed to call /close. // explicit “Stop & save” action is allowed to call /close.
invalidateResumeIntent(); resetSessionView();
resetSession();
selectActiveSession(null);
setAwaitingQuestion(false);
} }
// The final workflow turn ends with the session already finalized on disk: // The final workflow turn ends with the session already finalized on disk:
// refetch now instead of waiting for the 10s poll, so the completed state // refetch now instead of waiting for the 10s poll, so the completed state
@@ -725,15 +736,8 @@ export function AppShell({ canLogout }: AppShellProps) {
} }
if (!canLeaveDatabaseManagement()) return; if (!canLeaveDatabaseManagement()) return;
navigate({ surface: "core" }, true); navigate({ surface: "core" }, true);
invalidateResumeIntent(); resetSessionView();
newSessionOperationRef.current = null;
resetSession();
// Starting a new question closes any open session detail panel: the reader is
// moving away from that session, so its left-hand box must not linger.
setPanelSession(null);
setAwaitingQuestion(true); setAwaitingQuestion(true);
setCreatingSession(false);
selectActiveSession(null);
// Best effort only: session creation keeps the authoritative readiness gate. // Best effort only: session creation keeps the authoritative readiness gate.
// Composer focus is deliberately independent of this network request. // Composer focus is deliberately independent of this network request.
void prewarmRuntime().catch(() => undefined); void prewarmRuntime().catch(() => undefined);
@@ -781,9 +785,7 @@ export function AppShell({ canLogout }: AppShellProps) {
} finally { } finally {
if (!isAuthOperationCurrent(guard, { sessionId: id, disposalEpoch: operationEpochRef.current }) if (!isAuthOperationCurrent(guard, { sessionId: id, disposalEpoch: operationEpochRef.current })
|| activeSessionIdRef.current !== id) return; || activeSessionIdRef.current !== id) return;
resetSession(); resetSessionView();
selectActiveSession(null);
setAwaitingQuestion(false);
} }
} }
@@ -23,3 +23,23 @@ test("gate chrome follows UI locale while model questions and choices remain in
expect(screen.getByRole("button", { name: "Salva e procedi" })).toBeInTheDocument(); expect(screen.getByRole("button", { name: "Salva e procedi" })).toBeInTheDocument();
expect(useSessionStore.getState().pendingWidget?.options?.[0].label).toBe("Salva e procedi"); expect(useSessionStore.getState().pendingWidget?.options?.[0].label).toBe("Salva e procedi");
}); });
test.each([
["en", "it", "Save and proceed", "Other — specify"],
["it", "en", "Salva e procedi", "Altro: specifica"],
])("HITL controls follow question language %s even when the UI locale changes", (language, uiLocale, save, other) => {
setLocale(uiLocale);
useSessionStore.setState({ pendingWidget: {
id: "language-gate", widget: "select", interaction_language: language,
title: "Which admission date should be used?",
options: [{ id: "approve", label: "Save and proceed", label_i18n: "Save and proceed" }],
reserved: ["back", "exit", "other"],
} });
render(<WidgetHost sessionId="s1" />);
expect(screen.getByRole("button", { name: save })).toBeVisible();
expect(screen.getByRole("button", { name: other })).toBeVisible();
act(() => setLocale(language));
act(() => setLocale(uiLocale));
expect(screen.getByRole("button", { name: other })).toBeVisible();
expect(screen.getByText("Which admission date should be used?")).toBeVisible();
});
+8 -1
View File
@@ -1,4 +1,4 @@
import { useI18n } from "../i18n"; import { InteractionLanguage, useI18n } from "../i18n";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useSessionStore } from "../store/sessionStore"; import { useSessionStore } from "../store/sessionStore";
import { resolve } from "../widgets"; import { resolve } from "../widgets";
@@ -9,6 +9,13 @@ import { captureAuthOperation, isAuthOperationCurrent } from "../auth/authOperat
import { localizeWidget } from "../widgets/localize"; import { localizeWidget } from "../widgets/localize";
export function WidgetHost({ sessionId }: { sessionId: string | null }) { export function WidgetHost({ sessionId }: { sessionId: string | null }) {
const language = useSessionStore((s) => s.pendingWidget?.interaction_language);
return <InteractionLanguage language={language}>
<SessionWidgetHost sessionId={sessionId} />
</InteractionLanguage>;
}
function SessionWidgetHost({ sessionId }: { sessionId: string | null }) {
const { t: translate } = useI18n(); const { t: translate } = useI18n();
const source = useSessionStore((s) => s.pendingWidget); const source = useSessionStore((s) => s.pendingWidget);
const pending = source ? localizeWidget(source, translate) : null; const pending = source ? localizeWidget(source, translate) : null;
@@ -22,7 +22,9 @@
flex-direction: column; flex-direction: column;
min-width: 0; min-width: 0;
min-height: 0; min-height: 0;
overflow: hidden; overflow-x: hidden;
overflow-y: auto;
overscroll-behavior: contain;
background-color: oklch(var(--sidebar)); background-color: oklch(var(--sidebar));
container-name: fleet-workspace; container-name: fleet-workspace;
container-type: inline-size; container-type: inline-size;
@@ -1,5 +1,8 @@
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
// Loaded explicitly by PiEvidenceRestructurer. Keep outside .pi/extensions:
// JSON-only output prevents interactive sessions from emitting native tool calls.
export default function (pi: ExtensionAPI) { export default function (pi: ExtensionAPI) {
pi.on("before_provider_request", (event) => { pi.on("before_provider_request", (event) => {
if (typeof event.payload !== "object" || event.payload === null) { if (typeof event.payload !== "object" || event.payload === null) {
@@ -43,6 +43,8 @@ for (const [language, workspaceLanguage, entry, title, yes, no] of [
assert.ok(injected.systemPrompt.includes(`interaction_language=${language}`)); assert.ok(injected.systemPrompt.includes(`interaction_language=${language}`));
assert.ok(injected.systemPrompt.includes(`workspace_language=${workspaceLanguage}`)); assert.ok(injected.systemPrompt.includes(`workspace_language=${workspaceLanguage}`));
assert.match(injected.systemPrompt, /reviewer questions, explanations, and choices/); assert.match(injected.systemPrompt, /reviewer questions, explanations, and choices/);
const name = new Intl.DisplayNames(["en"], { type: "language" }).of(language);
assert.ok(injected.systemPrompt.includes(`artifact explanation in ${name}`));
} }
let widget; let widget;
ctx.ui.input = async title => { ctx.ui.input = async title => {
@@ -51,10 +53,19 @@ for (const [language, workspaceLanguage, entry, title, yes, no] of [
}; };
await tools.get("reviewer_datamart").def.execute("call", { session: "s1" }, null, null, ctx); await tools.get("reviewer_datamart").def.execute("call", { session: "s1" }, null, null, ctx);
assert.equal(widget.title, title); assert.equal(widget.title, title);
assert.equal(widget.interaction_language, language);
assert.equal(widget.title_i18n, "Generate a datamart?"); assert.equal(widget.title_i18n, "Generate a datamart?");
assert.deepEqual(widget.options.map(o => [o.id, o.label]), [ assert.deepEqual(widget.options.map(o => [o.id, o.label]), [
["generate", yes], ["skip", no], ["generate", yes], ["skip", no],
]); ]);
// Pi may supply a new context object for a tool; language must come from
// the manifest even for a model-authored select, not just generated chrome.
await tools.get("reviewer_select").def.execute("select", {
session: "s1", title: "Choose the admission date",
options: [{ id: "skip", label: "First admission" }],
}, null, null, { ...ctx });
assert.equal(widget.interaction_language, language);
assert.equal(widget.title, "Choose the admission date");
} finally { } finally {
cp.execFileSync = previous.exec; cp.execFileSync = previous.exec;
for (const [key, value] of [["THT_SESSION", previous.session], for (const [key, value] of [["THT_SESSION", previous.session],
@@ -40,6 +40,8 @@ const CATALOG = {
let _handler = null; let _handler = null;
const _origExecFileSync = cp.execFileSync; const _origExecFileSync = cp.execFileSync;
cp.execFileSync = function delegatingStub(file, args, opts) { cp.execFileSync = function delegatingStub(file, args, opts) {
if (args[0] === "session" && args[1] === "ensure-interaction-language")
return JSON.stringify({ interaction_language: "en", workspace_language: "it" });
if (_handler) return _handler(file, args, opts); if (_handler) return _handler(file, args, opts);
return _origExecFileSync.call(cp, file, args, opts); return _origExecFileSync.call(cp, file, args, opts);
}; };
+14 -8
View File
@@ -293,10 +293,14 @@ function sessionLanguage(ctx, sessionId) {
} }
function languageContext(manifest) { function languageContext(manifest) {
const name = new Intl.DisplayNames(["en"], { type: "language" }).of(manifest.interaction_language);
return "\n\n<session-language>\n" + return "\n\n<session-language>\n" +
`interaction_language=${manifest.interaction_language}\n` + `interaction_language=${manifest.interaction_language}\n` +
`workspace_language=${manifest.workspace_language}\n` + `workspace_language=${manifest.workspace_language}\n` +
"Use interaction_language for all newly generated reviewer questions, explanations, and choices. " + "Use interaction_language for all newly generated reviewer questions, explanations, and choices. " +
`Write every reviewer-visible title, intro, option label, rationale and artifact explanation in ${name}. ` +
"This language was selected from the original question. Before calling a reviewer tool, " +
"check each prose field and rewrite any field in another language into the interaction language. " +
"This persisted session setting is authoritative on every turn, including resume and steering. " + "This persisted session setting is authoritative on every turn, including resume and steering. " +
"Preserve workspace documents, quoted sources, prior decisions, SQL, identifiers and literal values. " + "Preserve workspace documents, quoted sources, prior decisions, SQL, identifiers and literal values. " +
"Interpret domain terms in workspace_language. Instruction/example language does not change these settings.\n" + "Interpret domain terms in workspace_language. Instruction/example language does not change these settings.\n" +
@@ -590,9 +594,11 @@ export function isJoinReviewApproval(resp, optionIds) {
[...chosen].every((id) => expected.has(id)); [...chosen].every((id) => expected.has(id));
} }
export async function emitAndWait(ctx, descriptor) { export async function emitAndWait(ctx, descriptor, sessionId) {
const language = sessionId ? sessionLanguage(ctx, sessionId).interaction_language : contextLanguages.get(ctx);
const localized = language ? { ...descriptor, interaction_language: language } : descriptor;
for (;;) { for (;;) {
const value = await ctx.ui.input(JSON.stringify(descriptor), ""); const value = await ctx.ui.input(JSON.stringify(localized), "");
if (value === undefined || value === null) { if (value === undefined || value === null) {
await reLoop(ctx); await reLoop(ctx);
continue; continue;
@@ -826,7 +832,7 @@ export default function (pi) {
return { return {
message: { message: {
role: "user", role: "user",
content: [{ type: "text", text: "Esegui ora il workflow richiesto. Non scrivere analisi, spiegazioni o un elenco: usa il tool bash per `tht session show` e, se la sessione e' in Fase 1 senza decisioni, invoca immediatamente reviewer_select. La tua prossima risposta visibile deve essere una tool call." }], content: [{ type: "text", text: "Run the requested workflow now: use bash for `tht session show` and, if the session is in Phase 1 without decisions, immediately call reviewer_select. Your next visible response must be a tool call. Write every reviewer-facing field in the interaction_language specified in <session-language>, including titles, introductions and option labels." }],
}, },
systemPrompt: systemPrompt:
`${event.systemPrompt}\n\n` + `${event.systemPrompt}\n\n` +
@@ -922,7 +928,7 @@ export default function (pi) {
ctx, params, `u${Date.now()}`, ctx, params, `u${Date.now()}`,
); );
if (prepared.result) return prepared.result; if (prepared.result) return prepared.result;
const response = await emitAndWait(ctx, prepared.widget); const response = await emitAndWait(ctx, prepared.widget, prepared.session);
const outcome = disambiguationGate.resolveClarification(prepared, response); const outcome = disambiguationGate.resolveClarification(prepared, response);
if (outcome.decision) { if (outcome.decision) {
const err = relayIfThtFails( const err = relayIfThtFails(
@@ -1000,7 +1006,7 @@ export default function (pi) {
id: option.id, label: option.label, label_i18n: option.label_i18n, id: option.id, label: option.label, label_i18n: option.label_i18n,
})), })),
}); });
const resp = await emitAndWait(ctx, widget); const resp = await emitAndWait(ctx, widget, session);
const outcome = resolveSelectOutcome(options, resp); const outcome = resolveSelectOutcome(options, resp);
if (outcome.kind === "freetext") if (outcome.kind === "freetext")
return textResult(`Altro (reviewer): ${outcome.text}`); return textResult(`Altro (reviewer): ${outcome.text}`);
@@ -1102,7 +1108,7 @@ export default function (pi) {
}); });
let resp; let resp;
for (;;) { for (;;) {
resp = await emitAndWait(ctx, widget); resp = await emitAndWait(ctx, widget, session);
if (resp.control === "freetext" || resp.control === "back" || resp.control === "exit") if (resp.control === "freetext" || resp.control === "back" || resp.control === "exit")
break; break;
if (!joinOnly || isJoinReviewApproval(resp, meritOptions.map((option) => option.id))) if (!joinOnly || isJoinReviewApproval(resp, meritOptions.map((option) => option.id)))
@@ -1243,7 +1249,7 @@ export default function (pi) {
title, title,
tables: enriched, tables: enriched,
}); });
const resp = await emitAndWait(ctx, widget); const resp = await emitAndWait(ctx, widget, session);
if (resp.control === "back") return textResult("Il reviewer vuole tornare indietro."); if (resp.control === "back") return textResult("Il reviewer vuole tornare indietro.");
if (resp.control === "exit") return textResult("Il reviewer vuole uscire."); if (resp.control === "exit") return textResult("Il reviewer vuole uscire.");
if (resp.control === "freetext") if (resp.control === "freetext")
@@ -1494,7 +1500,7 @@ export default function (pi) {
}); });
let outcome; let outcome;
for (;;) { for (;;) {
const resp = await emitAndWait(ctx, widget); const resp = await emitAndWait(ctx, widget, session);
outcome = resolveConfirmOutcome(resp); outcome = resolveConfirmOutcome(resp);
if (outcome.kind !== "unknown") break; if (outcome.kind !== "unknown") break;
await ctx.ui.notify(gateText(locale, "chooseApproval"), "warning"); await ctx.ui.notify(gateText(locale, "chooseApproval"), "warning");
+2 -1
View File
@@ -6,4 +6,5 @@ Nuova domanda ThothII: "$@"
Use the session manifest's interaction_language for reviewer dialogue. Workspace Use the session manifest's interaction_language for reviewer dialogue. Workspace
content and SQL retain their original language and values. A managed session already content and SQL retain their original language and values. A managed session already
has its language pinned; standalone `tht session new` defaults to workspace language. has its language pinned from the question; standalone `tht session new` also detects
the question's language, using workspace language only for ambiguous input.
+9 -3
View File
@@ -17,19 +17,25 @@ confirmation and is persisted directly — an option without a payload only asks
Continue records the complete join set), `reviewer_confirm` (gate on an artifact / phase transition). Free text Continue records the complete join set), `reviewer_confirm` (gate on an artifact / phase transition). Free text
arrives via the "Altro/Other" option or by prefixing `!` in chat. arrives via the "Altro/Other" option or by prefixing `!` in chat.
**Language contract:** the session manifest's `interaction_language` controls all **Language contract:** session creation detects the original question's language and
pins it as `interaction_language` (the UI/CLI preference is only a fallback for
short or ambiguous input). The session manifest's `interaction_language` controls all
new reviewer questions, explanations, option labels and rationales, including prose new reviewer questions, explanations, option labels and rationales, including prose
you generate inside review artifacts. It remains authoritative throughout the session, you generate inside review artifacts. It remains authoritative throughout the session,
including resume and steering from a browser using a different UI locale. The gate including resume and steering from a browser using a different UI locale. The gate
injects this persisted language into each model turn; the language of these instructions injects this persisted language into each model turn; the language of these instructions
and examples does not select the output language. and examples does not select the output language. Before each reviewer tool call,
check that every generated title, intro, question, option label, rationale and
artifact explanation is in that language. Rewrite mismatched prose before calling
the tool; retain identifiers and quoted source values verbatim.
`workspace.language` controls workspace-owned documents, catalog descriptions, Evidence `workspace.language` controls workspace-owned documents, catalog descriptions, Evidence
and interpretation of domain terms. Preserve quoted source content and prior decisions and interpretation of domain terms. Preserve quoted source content and prior decisions
verbatim. Keep SQL, identifiers, literal values and workspace artifacts unchanged by verbatim. Keep SQL, identifiers, literal values and workspace artifacts unchanged by
the interaction preference. Ask about ambiguous domain terms in the interaction language. the interaction preference. Ask about ambiguous domain terms in the interaction language.
For a legacy manifest without the field, run `tht session ensure-interaction-language For a legacy manifest without the field, run `tht session ensure-interaction-language
<id> --json` before interacting: it pins workspace language once and accepts no override. <id> --json` before interacting: it detects the original question's language with
workspace language as fallback, pins it once and accepts no override.
## Phase map (advance cheat-sheet) ## Phase map (advance cheat-sheet)

Some files were not shown because too many files have changed in this diff Show More