docs: consolidate historical records and verify public manual publication
Publish documentation / publish (push) Successful in 29s
@@ -10,6 +10,8 @@ on:
|
||||
- "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"
|
||||
- ".gitea/workflows/publish-docs.yml"
|
||||
workflow_dispatch:
|
||||
@@ -45,6 +47,13 @@ jobs:
|
||||
- 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
|
||||
run: |
|
||||
mkdocs build --strict
|
||||
|
||||
@@ -1,719 +1,95 @@
|
||||
# ThothII — Project State
|
||||
|
||||
Last updated: 2026-09-15.
|
||||
|
||||
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/`,
|
||||
`docs/contracts/`, `docs/adr/`, and `docs/evidence.md`. Superseded plans and reports are
|
||||
available from Git history rather than duplicated in the working tree.
|
||||
|
||||
The guarded server migration from a legacy checkout to the schema-v2 installation, Gitea source,
|
||||
Authentik, internal catalog/embedding services, and the PSD workspace repository is documented in
|
||||
`docs/operations/server-upgrade-gitea-workspace-v2.md`. Treat its operator gates and rollback
|
||||
requirements as mandatory; do not replace the running server stack in place.
|
||||
|
||||
## Public documentation boundary — 2026-09-15
|
||||
|
||||
MkDocs now publishes only its explicit public-manual allowlist: 20 product, installation,
|
||||
usage and administration pages, plus approved assets. Internal developer/project documents
|
||||
remain in Git but are excluded from generated pages and search. The build and publication
|
||||
workflow enforce this boundary with `scripts/verify-public-docs.py` and negative fixtures.
|
||||
The critical review and proposed (not executed) source-file retirements are in
|
||||
`docs/maintenance/2026-09-15-documentation-cleanup.md`. It also records pre-existing stale
|
||||
paths later in this snapshot and obsolete authentication/DWH documentation verifiers;
|
||||
those require a separate reference/test cleanup, not restoration of superseded guides.
|
||||
Commit `043ffdfa` passed Gitea publication run 122 and the generated `pages` search index
|
||||
contains only the 20 public pages. The served `/thothii-docs/` URL is still a stale static
|
||||
copy dated 2026-08-26 with internal pages: locating/updating its actual server publication
|
||||
path remains blocked on verified administrative access. Do not claim the live site is clean.
|
||||
|
||||
## Session review layout deployed — 2026-09-14
|
||||
|
||||
Session-only dialogs now use the visible app bounds: artifact/column review grows
|
||||
up to 80rem wide and the available height; short confirmations grow to 40rem.
|
||||
Existing primary actions appear above and below session forms and review content,
|
||||
with shared handlers, validation and pending state. Stop/delete focus Cancel;
|
||||
rename focuses the name field. Administration surfaces are unchanged. The activity
|
||||
shortcut is relabelled To Administration / Vai all’Amministrazione, retaining its
|
||||
workspace-management destination. 778 frontend tests, five responsive browser
|
||||
scenarios, typecheck, translations and the production build passed. The owner
|
||||
authorized deployment and the server frontend was recreated at 18:09 CEST with tag `b1723c34-session-dialogs-20260914`. Frontend is healthy,
|
||||
Omics serves the new assets, and doctor passes 13/13 checks. Core and Omics web
|
||||
were not restarted. See DESIGN.md for the layout contract and
|
||||
`docs/reports/2026-09-14-session-dialogs-release.md` for provenance and rollback.
|
||||
|
||||
## Session composer and empty Memory fix deployed — 2026-09-14
|
||||
|
||||
The embedded shell now caps its height at the portal mount height, keeping steering
|
||||
and Stop & save visible. Empty authoritative Memory archives return zero results
|
||||
without requiring embedding/BM25; SQL-rule embedding is lazy and shared. The live
|
||||
empty PSD archive reproduces 503 with the current image and succeeds with the
|
||||
candidate. 54 Memory tests, 90 frontend tests, five browser scenarios and both image
|
||||
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
|
||||
as `d6cdffea`. Both are healthy; production Memory search returns `[]`, Omics serves
|
||||
the corrected CSS, native doctor passes 13/13 checks, and admissions are reopened.
|
||||
Session inventory is preserved. Backups and rollback images are retained. Native
|
||||
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
|
||||
the remaining interactive browser acceptance.
|
||||
|
||||
## Full/embedded shell and bilingual interface
|
||||
|
||||
Full/embedded shell, EN/IT UI, immutable session interaction language, dark theme,
|
||||
fullscreen and full-mode logout are implemented. The local Mac descriptor explicitly
|
||||
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
|
||||
identity verification and a replaceable presentation-only PortalAdapter; this does
|
||||
not mean this branch was verified on the production server. Its changes are
|
||||
in Omics commit `95154e1`; production deployment remains pending.
|
||||
See `docs/operations/shell-and-localization.md` for integration and installation
|
||||
instructions and `docs/reports/2026-09-13-full-shell-implementation.md` for tests,
|
||||
independent reviews, local browser checks and rollback details.
|
||||
|
||||
### Documentation handoff before branch closure — 2026-09-13
|
||||
|
||||
Current rendering architecture is in `docs/architecture/application-shell.md`;
|
||||
the exact portal identity/proxy contract is in `docs/install/authentication-upstream.md`.
|
||||
`docs/operations/server-codex-handoff.md` is the current server delivery/deploy
|
||||
runbook, including Omics source integration from GitHub, configuration, tests and
|
||||
rollback. It supersedes the earlier Omics repository-relay instructions. The acceptance matrix is
|
||||
`docs/testing/authentication-manual-acceptance.md`. README, documentation navigation,
|
||||
user/installation/authentication guides and descriptor examples point to these
|
||||
paths. Local examples explicitly use full/en; the projected server example is
|
||||
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
|
||||
promotion to main; the actual merge is recorded in Git. PSD acceptance remains pending.
|
||||
|
||||
### Navigation readiness and session accordions — 2026-09-13
|
||||
|
||||
The Workspace navigation button now carries an accessible green/red readiness
|
||||
dot instead of a separate text row. Green requires a selected workspace and a
|
||||
successful, current `ready` response; checking, unavailable and other states are
|
||||
red, with the state exposed through the tooltip and accessible description.
|
||||
The backend readiness gate is unchanged.
|
||||
|
||||
Active sessions (non-archived) and Archive both start collapsed. Their adjacent
|
||||
headers are the only visible content below the scope tabs: no Sessions heading
|
||||
or external selection toolbar. Each nonempty panel owns its Select all and
|
||||
bulk-delete controls, scoped to that list and preserving the other's selection.
|
||||
As of 2026-09-14, only one section can be open at a time, and either can be
|
||||
collapsed. Empty lists show only "No sessions yet." The open section uses the
|
||||
remaining sidebar height, with scrolling content capped at `min(18rem, 35dvh)`. The mobile
|
||||
navigation dialog also provides a bounded height. Keyboard controls and labels
|
||||
are retained. See `DESIGN.md` and `docs/guida-utente.md` for the UI contract.
|
||||
|
||||
Verification: 768 frontend unit tests, 20 browser scenarios (including 80 mocked
|
||||
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`.
|
||||
Core, catalog, Qdrant and embedding containers were not changed. The prior
|
||||
frontend image is retained as
|
||||
`thothii-frontend:before-single-session-accordion-20260914` for rollback.
|
||||
|
||||
### Current Omics delivery and server handoff — 2026-09-14
|
||||
|
||||
The owner corrected the delivery requirement: Omics is obtained from GitHub,
|
||||
not relayed to another repository as part of this deployment. Any optional
|
||||
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.
|
||||
# Project state
|
||||
|
||||
Updated: 2026-09-15. This is a current snapshot, not a release diary. Stable commands
|
||||
and invariants are in [AGENTS.md](AGENTS.md); prior snapshots remain in Git.
|
||||
|
||||
## Current contracts
|
||||
|
||||
- React supports full/embedded rendering independently of local/OIDC/upstream auth,
|
||||
with EN/IT UI and immutable session interaction language. See
|
||||
[application shell](docs/architecture/application-shell.md) and
|
||||
[localization](docs/operations/shell-and-localization.md).
|
||||
- PostgreSQL Metadata Catalog owns database identity, binding, schema, descriptions,
|
||||
sensitivity and relationships for all core consumers. Workspace schema v4 contains
|
||||
identity and optional Evidence only. Installation schema v2 is the authored model
|
||||
catalog source. See [overview](docs/architecture/overview.md) and
|
||||
[model configuration](docs/general/pi-configuration.md).
|
||||
- The harness owns workflow persistence; chat is not the durable session record.
|
||||
Memory uses PostgreSQL authority and derived Qdrant dense/BM25 search. Editable
|
||||
Evidence has local archive authority and manual consolidation. File save, search
|
||||
activation and Git publication have distinct outcomes. See
|
||||
[Memory](docs/gestione-memory.md), [Evidence](docs/contracts/curated-evidence-v4.md)
|
||||
and [consolidated release evidence](docs/reports/knowledge-archives-release.md).
|
||||
- Reference preprocessing must not clear Memory. Use the installation-scoped
|
||||
`tht --installation /absolute/path/thothii-installation.yaml workspace preprocess run`
|
||||
and its [contract](docs/contracts/workspace-preprocessing-cli.md).
|
||||
- DWH sessions are read-only. SSH tunnels support database-management diagnostics
|
||||
and metadata synchronization, not NL→SQL session creation; use direct or REST
|
||||
transport for sessions.
|
||||
|
||||
## Installation and workspace boundaries
|
||||
|
||||
Fresh standalone installations follow the manual terminal procedures in
|
||||
[Italian](docs/install/standalone-manual-it.md) or
|
||||
[English](docs/install/standalone-manual-en.md), without an installer or launcher.
|
||||
The [Compose reference](docs/operations/compose-reference.md) is for maintainers,
|
||||
not another quick start. Catalog and Memory migrations are explicit.
|
||||
|
||||
Descriptors, authentication, provider credentials, certificates and runtime bindings
|
||||
stay in protected installation-local paths. Do not copy secrets into examples or
|
||||
workspace Git. Legacy runtime snapshots may use absolute paths and protected
|
||||
`harness/.env`; do not silently relocate them.
|
||||
|
||||
PSD authoring is separate at `/Users/mp/projects/tht-workspace-psd`. Its GitHub
|
||||
repository was copied to private Gitea
|
||||
[workspace_psd](https://git.tylconsulting.it/mptyl/workspace_psd), preserving both
|
||||
branches. It is a copy, not automatic synchronization. Running installations were
|
||||
not repointed to a different workspace remote.
|
||||
|
||||
## Recorded deployments and server authority
|
||||
|
||||
Use the ordered [server handoff](docs/operations/server-codex-handoff.md) for
|
||||
coordinated ThothII/Omics upgrades. Omics integration uses GitHub
|
||||
`Dallavilla-Tiziano/omics_portal`, with no Gitea relay prerequisite. Omics uses
|
||||
embedded/upstream identity, not another ThothII OIDC login. Read
|
||||
[upstream authentication](docs/install/authentication-upstream.md) before changes.
|
||||
|
||||
Last recorded application deliveries (not a fresh runtime attestation):
|
||||
|
||||
- [Session dialogs](docs/reports/2026-09-14-session-dialogs-release.md):
|
||||
`b1723c34-session-dialogs-20260914`, frontend-only.
|
||||
- [Session layout/Memory fix](docs/reports/2026-09-14-session-layout-memory-fix.md):
|
||||
`49333a2d-session-memory-fix`, core/frontend.
|
||||
|
||||
Keep those reports and rollback instructions while operator gates remain open.
|
||||
A later deployment does not prove every earlier acceptance item passed.
|
||||
|
||||
## Remaining acceptance and design gates
|
||||
|
||||
- Fresh-machine Mac, Windows/WSL2 and Linux installation acceptance, including real
|
||||
DWH/model endpoints, remains a separate operator exercise.
|
||||
- Real IdP/portal login, logout, embedded interaction and PSD semantic acceptance
|
||||
follow the [manual matrix](docs/testing/authentication-manual-acceptance.md) and
|
||||
delivery reports; synthetic tests do not close them.
|
||||
- [Security hardening](docs/plans/2026-09-08-security-hardening-prd.md) is a draft.
|
||||
Revalidate SEC01–12 and obtain design approval before implementation or real
|
||||
server/IdP/DWH mutation.
|
||||
- Optional NER remains opt-in; labeled Italian quality, benchmark and licensing
|
||||
acceptance are not implied by document cleanup.
|
||||
- Semantic aliases, value descriptions, synonyms/concepts, dialect and multi-schema
|
||||
extensions remain explicit design work. Current sensitivity delivery follows the
|
||||
Catalog contract; additional policies require their own acceptance.
|
||||
- Legacy database UI fallback (`?db-ui=legacy`, dev/staging) and prototype removal
|
||||
remain subject to owner acceptance.
|
||||
|
||||
## Documentation maintenance
|
||||
|
||||
MkDocs publishes only 20 product/operator pages and five approved assets.
|
||||
Architecture, contracts, ADRs, plans, research, tests and release evidence are
|
||||
excluded from HTML and search. The repository itself is public: editorial exclusion
|
||||
is not confidentiality.
|
||||
|
||||
The [cleanup record](docs/maintenance/2026-09-15-documentation-cleanup.md) records
|
||||
retired sources and retained gates. Main contains source; Actions generates the
|
||||
`pages` branch. The live site requires the separate explicit deployment described
|
||||
in [public manual publication](docs/operations/public-docs-publication.md).
|
||||
|
||||
@@ -30,71 +30,25 @@ installation, use and administration. Developer architecture, contracts, ADRs, t
|
||||
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 proposed consolidation; no historical source documents have been deleted.
|
||||
for the executed consolidation and the inventory of historical sources retained in Git.
|
||||
|
||||
## Docker Compose: local startup
|
||||
## Docker Compose and installation
|
||||
|
||||
Requirements: Docker Engine with Compose v2. The mandatory stack is `frontend`, `core`,
|
||||
`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.
|
||||
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.
|
||||
|
||||
From a fresh clone, run these commands from the repository root:
|
||||
The mandatory stack includes frontend, core, PostgreSQL catalog, Qdrant, Ollama
|
||||
and the embedding initializer. DWH and LLM endpoints remain external dependencies.
|
||||
Pi is included in the core image. Credentials and certificates belong in protected
|
||||
installation-local files, never in the workspace repository.
|
||||
|
||||
```sh
|
||||
cp deploy/env/local.env.example deploy/env/local.env
|
||||
# Copy docs/install/examples/thothii-installation.local.yaml to a protected operator path,
|
||||
# replace its placeholders, chmod it 600, and set that exact THT_INSTALLATION_CONFIG_SOURCE.
|
||||
# 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,
|
||||
then runs the base+local stack in the foreground. Migrations never run implicitly in backend
|
||||
startup. The core image contains its Pi runtime; no host `pi` executable is used. For a server
|
||||
installation, build the image, start the catalog, and run the same migration service before the
|
||||
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.
|
||||
For developer topology, overlays and lifecycle details, see the internal
|
||||
[Compose reference](docs/operations/compose-reference.md). Ordinary stop/down keeps
|
||||
persistent data; removing volumes is destructive and is not an upgrade step.
|
||||
Process health is distinct from external dependency checks performed by doctor.
|
||||
|
||||
## Git-backed workspace repository
|
||||
|
||||
|
||||
@@ -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**.
|
||||
|
||||
## Curated and immutable Evidence
|
||||
## Evidence sources, local authority and search projections
|
||||
|
||||
The workspace repository is the publication boundary. The curator prepares `evidence/source/`,
|
||||
reviews units in `evidence/curated/`, validates them, and merges them. With `evidence.schema_version: 2`,
|
||||
the runtime materializes the full `evidence/` tree from the exact Git commit, but the renderer passes
|
||||
only `curated/**/*.md` from the immutable revision root to preprocessing. Sources, manifests, and
|
||||
evaluation data remain available for traceability. The runtime never modifies, stages, commits, or
|
||||
publishes the authoring repository.
|
||||
The workspace repository publishes revision-pinned workspace identities and Evidence sources.
|
||||
An initialized editable Evidence archive is maintained locally through external editors and
|
||||
explicit consolidation; normal preprocessing or source refresh must not overwrite its manual
|
||||
corrections. File save, active search generation and a later human Git commit/push are separate
|
||||
outcomes. See [the current Evidence contract](../contracts/curated-evidence-v4.md) and
|
||||
[source/publication boundaries](../contracts/workspace-evidence-v3.md).
|
||||
|
||||
Before indexing, the curated corpus from the pinned revision is validated. Each workspace has two
|
||||
physical Qdrant collections with different lifecycles. `reference` contains Schema, relationships,
|
||||
and Evidence and may be replaced or cleared by preprocessing; `memory` contains `memory` and
|
||||
`solved_question` records and is never preprocessing output. Only the `reference` collection has the
|
||||
sparse `bm25` vector with `idf`, and only the Evidence stage writes sparse values.
|
||||
Each workspace has separate Qdrant collections. `reference` contains Schema, relationships and
|
||||
Evidence and may be replaced or cleared by preprocessing. Memory uses its own dense/BM25
|
||||
projection, rebuilt from authoritative PostgreSQL cards rather than old vector payloads or
|
||||
session artifacts. Both collections can contain sparse vectors; their ownership and cleanup
|
||||
lifecycles remain separate. See [Memory](../gestione-memory.md).
|
||||
|
||||
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
|
||||
@@ -115,6 +115,13 @@ the core can admit a new session.
|
||||
|
||||
## 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
|
||||
`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.
|
||||
|
||||
|
Before Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 189 KiB |
|
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"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 183 KiB |
|
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"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
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.
|
||||
See the [E1 validation report](../plans/2026-09-09-evidence-e1-validation.md) and
|
||||
[E2 validation report](../plans/2026-09-09-evidence-e2-validation.md).
|
||||
See the [E1 validation report](../reports/knowledge-archives-release.md) and
|
||||
[E2 validation report](../reports/knowledge-archives-release.md).
|
||||
|
||||
@@ -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
|
||||
[ADR 0018](adr/0018-use-postgres-for-memory-and-qdrant-for-retrieval.md) for the
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
@@ -1,9 +1,78 @@
|
||||
# Revisione e proposta di bonifica della documentazione
|
||||
# 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. Questa è una proposta di manutenzione interna,
|
||||
non una pagina del manuale pubblico e non un'autorizzazione a cancellare i file elencati.
|
||||
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
|
||||
|
||||
@@ -297,7 +366,7 @@ al sito. «Consolidare» e «ritirare» non indicano cancellazioni eseguite.
|
||||
| `testing/evidence-lifecycle-test-plan.md` | Developer/QA | Conservare criteri ripetibili e gate; separare risultati di singola release. |
|
||||
<!-- inventory:end -->
|
||||
|
||||
## Verifica della modifica
|
||||
## 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.
|
||||
@@ -309,7 +378,7 @@ al sito. «Consolidare» e «ritirare» non indicano cancellazioni eseguite.
|
||||
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.
|
||||
|
||||
### Esito remoto del 15 settembre 2026
|
||||
### 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
|
||||
@@ -326,4 +395,21 @@ 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.
|
||||
La bonifica dei sorgenti e della build è completata; quella della copia servita resta aperta.
|
||||
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.
|
||||
|
||||
@@ -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.
|
||||
@@ -0,0 +1,90 @@
|
||||
# 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.
|
||||
Theme-generated assets are separate from those five source assets.
|
||||
|
||||
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.
|
||||
- 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.
|
||||
@@ -722,6 +722,6 @@ la nuova installazione è ferma ma ispezionabile.
|
||||
- [OIDC generico](../install/authentication-oidc.md)
|
||||
- [Operazioni workspace](workspaces.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)
|
||||
- [Analisi locale della sensibilità](sensitivity-analysis.md)
|
||||
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
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
|
||||
[E1 — validazione](2026-09-09-evidence-e1-validation.md) e
|
||||
[E2 — validazione](2026-09-09-evidence-e2-validation.md) e
|
||||
[E3 — validazione](2026-09-09-evidence-e3-validation.md).
|
||||
[E1 — validazione](../reports/knowledge-archives-release.md) e
|
||||
[E2 — validazione](../reports/knowledge-archives-release.md) e
|
||||
[E3 — validazione](../reports/knowledge-archives-release.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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
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
|
||||
[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)
|
||||
riesamina tutte le decisioni Q1–Q15 alla luce degli ultimi chiarimenti del
|
||||
|
||||
@@ -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
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
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
|
||||
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)
|
||||
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
|
||||
|
||||
## 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.
|
||||
**Data:** 8 settembre 2026.
|
||||
**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,
|
||||
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.
|
||||
@@ -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 |
|
||||
| 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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -60,8 +60,8 @@ Il comportamento da verificare deriva da:
|
||||
|
||||
- stato corrente del progetto in `PROJECT_STATE.md`;
|
||||
- [contratto Catalog Schema Snapshot](../contracts/catalog-schema-snapshot.md);
|
||||
- [piano del Metadata Catalog](../plans/2026-08-26-metadata-catalog-from-thothai.md);
|
||||
- [specifica della generazione descrizioni](../plans/2026-08-28-ai-catalog-description-generation-spec.md);
|
||||
- [piano del Metadata Catalog](../reports/knowledge-archives-release.md);
|
||||
- [specifica della generazione descrizioni](../reports/knowledge-archives-release.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 0010: campioni reali limitati](../adr/0010-allow-bounded-real-source-samples-for-description-generation.md);
|
||||
|
||||
@@ -1,207 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Current public guides and internal authentication contracts; no retired runbooks.
|
||||
set -euo pipefail
|
||||
|
||||
script_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
root=$script_root
|
||||
if [[ $# -gt 0 ]]; then
|
||||
[[ $# -eq 2 && $1 == "--root" && -d $2 ]] || {
|
||||
echo "usage: auth-docs-smoke.sh [--root DIRECTORY]" >&2
|
||||
exit 2
|
||||
}
|
||||
root=$(cd "$2" && pwd -P)
|
||||
fi
|
||||
docs=(
|
||||
"$root/docs/architecture/authentication.md"
|
||||
"$root/docs/install/authentication-local.md"
|
||||
"$root/docs/install/authentication-oidc.md"
|
||||
"$root/docs/install/authentik.md"
|
||||
"$root/docs/testing/authentication-manual-acceptance.md"
|
||||
"$root/docs/architecture/overview.md"
|
||||
"$root/docs/install/local.md"
|
||||
"$root/docs/install/server.md"
|
||||
"$root/docs/install/psd-workspace-setup.md"
|
||||
"$root/docs/install/reverse-proxy-caddy.md"
|
||||
"$root/docs/install/reverse-proxy-nginx.md"
|
||||
"$root/docs/contracts/tht-pi.md"
|
||||
"$root/docs/contracts/workspace-preprocessing-cli.md"
|
||||
"$root/docs/guida-utente.md"
|
||||
"$root/docs/index.md"
|
||||
"$root/README.md"
|
||||
"$root/PROJECT_STATE.md"
|
||||
"$root/mkdocs.yml"
|
||||
)
|
||||
|
||||
for path in "${docs[@]}"; do
|
||||
[[ -f "$path" ]] || { echo "auth docs smoke: missing $path" >&2; exit 1; }
|
||||
done
|
||||
|
||||
corpus=$(mktemp)
|
||||
trap 'rm -f "$corpus"' EXIT
|
||||
cat "${docs[@]}" >"$corpus"
|
||||
|
||||
required=(
|
||||
"tht auth"
|
||||
"groups"
|
||||
"TOT Admin"
|
||||
"THT_OIDC_CLIENT_SECRET"
|
||||
"THT_AUTHENTIK_API_TOKEN"
|
||||
"Remember me"
|
||||
"oidc_mapped_group_missing"
|
||||
"oidc_callback_failed"
|
||||
"session.read_all"
|
||||
"workspace.secrets.manage"
|
||||
"auth.diagnostics.read"
|
||||
)
|
||||
for term in "${required[@]}"; do
|
||||
rg -Fq "$term" "$corpus" || { echo "auth docs smoke: missing required term: $term" >&2; exit 1; }
|
||||
done
|
||||
|
||||
canonical_compose='docker compose --env-file deploy/env/local.env -f compose.yaml -f deploy/compose.local.yaml up --build -d'
|
||||
rg -Fxq "$canonical_compose" "$root/docs/install/local.md" || {
|
||||
echo "auth docs smoke: missing canonical local Compose command" >&2
|
||||
exit 1
|
||||
}
|
||||
if rg -n -F 'docker compose --env-file deploy/env/local.env +' "$corpus"; then
|
||||
echo "auth docs smoke: noncanonical local Compose command" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
nav_count=$(awk 'index($0, "architecture/authentication.md") { count++ } END { print count + 0 }' "$root/mkdocs.yml")
|
||||
[[ $nav_count == 1 ]] || {
|
||||
echo "auth docs smoke: authentication navigation must appear exactly once" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
python3 - "$root" <<'PY'
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
architecture = (root / "docs/architecture/authentication.md").read_text()
|
||||
|
||||
user_row = "| `user` | `session.use` |"
|
||||
admin_row = (
|
||||
"| `admin` | `session.use`, `session.read_all`, `session.manage_all`, `settings.manage`, "
|
||||
"`workspace.manage`, `workspace.secrets.manage`, `database.manage`, `pi.manage`, `auth.diagnostics.read` |"
|
||||
)
|
||||
if user_row not in architecture or admin_row not in architecture:
|
||||
raise SystemExit("auth docs smoke: role-to-permission map is not exact")
|
||||
|
||||
diagnostic_heading = "## Diagnostics and ordering"
|
||||
diagnostic_start = architecture.find(diagnostic_heading)
|
||||
diagnostic_end = architecture.find("\n## ", diagnostic_start + len(diagnostic_heading))
|
||||
diagnostic_section = architecture[diagnostic_start:diagnostic_end if diagnostic_end >= 0 else None]
|
||||
match = re.search(r"```text\n([\s\S]*?)```", diagnostic_section)
|
||||
expected_codes = [
|
||||
"auth_ready",
|
||||
"auth_config_incomplete",
|
||||
"auth_config_invalid",
|
||||
"auth_session_store_invalid",
|
||||
"local_user_registry_invalid",
|
||||
"local_admin_missing",
|
||||
"oidc_secret_missing",
|
||||
"oidc_discovery_unreachable",
|
||||
"oidc_issuer_mismatch",
|
||||
"oidc_jwks_unreachable",
|
||||
"oidc_group_catalog_unreachable",
|
||||
"oidc_group_catalog_unauthorized",
|
||||
"oidc_mapped_group_missing",
|
||||
"oidc_mapped_group_ambiguous",
|
||||
"oidc_groups_claim_invalid",
|
||||
"oidc_device_flow_unavailable",
|
||||
]
|
||||
actual_codes = [] if match is None else [line for line in match.group(1).splitlines() if line]
|
||||
if actual_codes != expected_codes:
|
||||
raise SystemExit("auth docs smoke: diagnostic code union is not exact")
|
||||
|
||||
for relative, language, forbidden, required in [
|
||||
("docs/install/reverse-proxy-caddy.md", "caddyfile", "forward_auth", "forward_auth"),
|
||||
("docs/install/reverse-proxy-nginx.md", "nginx", "auth_request", "auth_request"),
|
||||
]:
|
||||
source = (root / relative).read_text()
|
||||
direct_start = source.find("## Direct ThothII-managed OIDC")
|
||||
deprecated_start = source.find("## Deprecated upstream migration mode")
|
||||
if direct_start < 0 or deprecated_start <= direct_start:
|
||||
raise SystemExit(f"auth docs smoke: {relative} does not split direct and deprecated modes")
|
||||
direct = source[direct_start:deprecated_start]
|
||||
deprecated_end = source.find("\n## ", deprecated_start + 4)
|
||||
deprecated = source[deprecated_start:deprecated_end if deprecated_end >= 0 else None]
|
||||
blocks = re.findall(rf"```{language}\n([\s\S]*?)```", direct)
|
||||
direct_code = "\n".join(blocks)
|
||||
if "/api/auth/oidc/login" not in direct or "/api/auth/oidc/callback" not in direct:
|
||||
raise SystemExit(f"auth docs smoke: {relative} omits unchanged public OIDC paths")
|
||||
if re.search(rf"(?m)^\s*{forbidden}\b", direct_code):
|
||||
raise SystemExit(f"auth docs smoke: {relative} applies external auth in direct OIDC mode")
|
||||
deprecated_code = "\n".join(
|
||||
re.findall(rf"```{language}\n([\s\S]*?)```", deprecated)
|
||||
)
|
||||
if not re.search(rf"(?m)^\s*{required}\b", deprecated_code):
|
||||
raise SystemExit(f"auth docs smoke: {relative} omits scoped deprecated upstream auth")
|
||||
PY
|
||||
|
||||
if rg -n -i --pcre2 '\bthothii-admin\b|\bthothctl\b' "$corpus"; then
|
||||
echo "auth docs smoke: forbidden obsolete host CLI wording" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rg -n -i --pcre2 -- '--password(?!-file)\b(?:[[:space:]]+|=)\S+' "$corpus"; then
|
||||
echo "auth docs smoke: plaintext password option" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rg -n -i --pcre2 '(?:^|[,{[:space:]])password[[:space:]]*:[[:space:]]*\S+|"password"[[:space:]]*:[[:space:]]*(?:"[^"]+"|[^,}[:space:]]+)' "$corpus"; then
|
||||
echo "auth docs smoke: plaintext password field" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rg -n -i --pcre2 '(?:unmapped|additional|extra)[^.\r\n]{0,160}groups?[^.\r\n]{0,160}(?:generate|produce|emit|cause|trigger|raise|create|result)[^.\r\n]{0,160}(?:warnings?|alerts?|advisory|advisories|notices?|notifications?|noise)|(?:warnings?|alerts?|advisory|advisories|notices?|notifications?|noise)[^.\r\n]{0,160}(?:generate|produce|emit|cause|trigger|raise|create|result)[^.\r\n]{0,160}(?:unmapped|additional|extra)[^.\r\n]{0,160}groups?' "$corpus"; then
|
||||
echo "auth docs smoke: misleading noise claim for unmapped groups" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Projected server authentication documentation contract.
|
||||
projection_docs=(
|
||||
"$root/docs/install/server.md"
|
||||
"$root/docs/install/authentication-local.md"
|
||||
"$root/docs/testing/authentication-manual-acceptance.md"
|
||||
"$root/docs/testing/psd-server-project-a-manual.md"
|
||||
"$root/docs/plans/2026-08-20-psd-server-project-a-standalone.md"
|
||||
"$root/PROJECT_STATE.md"
|
||||
)
|
||||
projection_corpus=$(mktemp)
|
||||
trap 'rm -f "$corpus" "$projection_corpus"' EXIT
|
||||
cat "${projection_docs[@]}" >"$projection_corpus"
|
||||
projection_required=(
|
||||
"canonical authentication root" "runtime projection" "CURRENT" "generations"
|
||||
"root:root 0700/0600" "10001:10001 0700/0600"
|
||||
"THT_AUTH_RUNTIME_ROOT"
|
||||
"auth status --json" "auth publish"
|
||||
"candidate or recovery" "Mac, Windows, and local direct-file authentication"
|
||||
"explicit authorization"
|
||||
)
|
||||
for term in "${projection_required[@]}"; do
|
||||
rg -Fqi "$term" "$projection_corpus" || {
|
||||
echo "auth docs smoke: missing runtime-projection term: $term" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
if rg -n -i --pcre2 "(?:useradd|groupadd)[^\n]{0,100}10001|10001[^\n]{0,100}(?:useradd|groupadd)" "$projection_corpus"; then
|
||||
echo "auth docs smoke: runtime projection creates a host 10001 identity" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rg -n -i --pcre2 '(?:^|[.!?]\s+)(?:(?:mount|bind)\s+(?:the\s+)?(?:canonical authentication root|auth-canonical)\b[^\n]{0,120}(?:core|/run/thothii-auth)|(?:core|the core service)\s+(?:mounts?|binds?)\s+(?:the\s+)?(?:canonical authentication root|auth-canonical)\b)|auth-canonical[^\n]{0,120}:/run/thothii-auth' "$projection_corpus"; then
|
||||
echo "auth docs smoke: canonical authentication is mounted into core" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rg -n -i --pcre2 '(?:(?:operators?\s+)?(?:may|can|should|must)\s+(?!not\b|never\b)(?:edit|write|modify|prune)|^\s*(?:sudo\s+)?(?:vi|vim|nano|sed|cp|mv|rm|tee|printf|echo)\b)[^\n]*(?:CURRENT|generations)' "$projection_corpus"; then
|
||||
echo "auth docs smoke: runtime projection permits direct selector or generation edits" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rg -n -i --pcre2 '(?:^\s*(?:sudo\s+nginx\s+-T|(?:sudo\s+)?(?:printenv|env)(?:\s|$)|(?:sudo\s+)?(?:diff|cat|less|more|head|tail|yq)\s+[^\n]*(?:secret|auth\.yaml|users\.yaml))|(?:may|can|should|must)\s+(?!not\b|never\b)(?:dump|print|capture|attach|include|run)\b[^\n]*(?:password|auth\.yaml|users\.yaml|YAML|raw environment|nginx\s+-T|secret-bearing diff))' "$projection_corpus"; then
|
||||
echo "auth docs smoke: runtime projection documents unsafe evidence collection" >&2
|
||||
exit 1
|
||||
fi
|
||||
if rg -n -i --pcre2 'Project A (?:has been|was|is) started|legacy[- ]stack (?:has been|was|is) changed' "$projection_corpus"; then
|
||||
echo "auth docs smoke: runtime projection claims live Project A or legacy mutation" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "auth docs smoke: required terms and forbidden wording checks passed"
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
exec python3 "$root/scripts/verify-auth-docs.py" auth "$@"
|
||||
|
||||
@@ -1,96 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
tmp=$(mktemp -d "${TMPDIR:-/tmp}/thoth-auth-docs.XXXXXX")
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
|
||||
files=(
|
||||
docs/architecture/authentication.md
|
||||
docs/install/authentication-local.md
|
||||
docs/install/authentication-oidc.md
|
||||
docs/install/authentik.md
|
||||
docs/testing/authentication-manual-acceptance.md
|
||||
docs/architecture/overview.md
|
||||
docs/install/local.md
|
||||
docs/install/server.md
|
||||
docs/install/psd-workspace-setup.md
|
||||
docs/install/reverse-proxy-caddy.md
|
||||
docs/install/reverse-proxy-nginx.md
|
||||
docs/contracts/tht-pi.md
|
||||
docs/contracts/workspace-preprocessing-cli.md
|
||||
docs/guida-utente.md
|
||||
docs/index.md
|
||||
README.md
|
||||
PROJECT_STATE.md
|
||||
mkdocs.yml
|
||||
)
|
||||
|
||||
make_fixture() {
|
||||
local name=${1:?fixture name required}
|
||||
local fixture="$tmp/$name"
|
||||
mkdir -p "$fixture"
|
||||
for relative in "${files[@]}"; do
|
||||
mkdir -p "$fixture/$(dirname "$relative")"
|
||||
cp "$root/$relative" "$fixture/$relative"
|
||||
done
|
||||
printf '%s\n' "$fixture"
|
||||
}
|
||||
|
||||
expect_rejected() {
|
||||
local name=${1:?fixture name required}
|
||||
local fixture_text=${2:?fixture text required}
|
||||
local expected=${3:?expected error required}
|
||||
local fixture output
|
||||
fixture=$(make_fixture "$name")
|
||||
printf '%s\n' "$fixture_text" >>"$fixture/README.md"
|
||||
output="$tmp/$name.output"
|
||||
if "$root/scripts/auth-docs-smoke.sh" --root "$fixture" >"$output" 2>&1; then
|
||||
echo "auth docs fixture unexpectedly passed: $name" >&2
|
||||
exit 1
|
||||
fi
|
||||
rg -Fq "$expected" "$output" || {
|
||||
echo "auth docs fixture failed for the wrong reason: $name" >&2
|
||||
sed -n '1,20p' "$output" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "auth docs negative fixture rejected: $name"
|
||||
}
|
||||
|
||||
positive=$(make_fixture positive)
|
||||
printf '%s\n' \
|
||||
'Use --password-file <file>; never pass a password value.' \
|
||||
'Additional unmapped groups are silently ignored without warnings, alerts, or advisories.' \
|
||||
>>"$positive/README.md"
|
||||
"$root/scripts/auth-docs-smoke.sh" --root "$positive" >/dev/null
|
||||
echo "auth docs positive fixture passed"
|
||||
|
||||
expect_rejected thothctl-intervening \
|
||||
'Run thothctl --installation <descriptor> --json auth check.' \
|
||||
'forbidden obsolete host CLI wording'
|
||||
expect_rejected alternate-admin \
|
||||
'Run thothii-admin users list.' \
|
||||
'forbidden obsolete host CLI wording'
|
||||
expect_rejected password-option \
|
||||
'Run tht auth user add demo --password example-value.' \
|
||||
'plaintext password option'
|
||||
expect_rejected yaml-password \
|
||||
'password: example-value' \
|
||||
'plaintext password field'
|
||||
expect_rejected json-password \
|
||||
'{"password": "example-value"}' \
|
||||
'plaintext password field'
|
||||
expect_rejected unmapped-warning \
|
||||
'Unmapped OIDC groups generate warnings.' \
|
||||
'misleading noise claim for unmapped groups'
|
||||
expect_rejected unmapped-alert \
|
||||
'Unmapped provider groups trigger operator alerts.' \
|
||||
'misleading noise claim for unmapped groups'
|
||||
expect_rejected unmapped-advisory \
|
||||
'An advisory is emitted for every unmapped group.' \
|
||||
'misleading noise claim for unmapped groups'
|
||||
expect_rejected compose-plus \
|
||||
'docker compose --env-file deploy/env/local.env + -f compose.yaml -f deploy/compose.local.yaml up --build -d' \
|
||||
'noncanonical local Compose command'
|
||||
|
||||
echo "auth docs smoke fixture suite passed"
|
||||
exec python3 "$root/scripts/test-verify-auth-docs.py" auth
|
||||
|
||||
@@ -110,18 +110,13 @@ if (JSON.stringify(config).includes("fixture-model-api-key")) {
|
||||
NODE
|
||||
done
|
||||
|
||||
for manual in "$root/README.md" "$root/docs/installazione-docker-4-contesti.md"; do
|
||||
grep -Fq -- './scripts/run-stack.sh' "$manual" || {
|
||||
echo "${manual#"$root/"} lacks the canonical local launcher" >&2
|
||||
exit 1
|
||||
}
|
||||
grep -Fq -- 'docker compose --env-file deploy/env/server.env' "$manual" \
|
||||
&& grep -Fq -- '-f compose.yaml -f deploy/compose.server.yaml' "$manual" || {
|
||||
echo "${manual#"$root/"} lacks the canonical server base+profile command" >&2
|
||||
for manual in "$root/docs/install/standalone-manual-it.md" "$root/docs/install/standalone-manual-en.md"; do
|
||||
grep -Fq -- '--configure-only' "$manual" && grep -Fq -- 'catalog-migrate' "$manual" || {
|
||||
echo "${manual#"$root/"} omits explicit configuration or migration" >&2
|
||||
exit 1
|
||||
}
|
||||
if rg -q 'local-compose\.workspace-registry|server-compose\.workspace-registry' "$manual"; then
|
||||
echo "${manual#"$root/"} still references a superseded standalone Compose example" >&2
|
||||
echo "${manual#"$root/"} references a retired Compose example" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -13,7 +13,7 @@ targets=(
|
||||
deploy/secrets/README.md
|
||||
docs/install
|
||||
docs/index.md
|
||||
docs/installazione-docker-4-contesti.md
|
||||
docs/operations/compose-reference.md
|
||||
scripts/build-local.sh
|
||||
scripts/build-local.ps1
|
||||
scripts/docker-smoke.sh
|
||||
@@ -43,7 +43,7 @@ case "$rg_status" in
|
||||
;;
|
||||
esac
|
||||
|
||||
for document in README.md docs/installazione-docker-4-contesti.md; do
|
||||
for document in docs/operations/compose-reference.md; do
|
||||
grep -Fq -- '-f deploy/compose.session-server.yaml.example' "$document" || {
|
||||
echo "$document omits the required public-server session override" >&2
|
||||
exit 1
|
||||
|
||||
@@ -40,7 +40,7 @@ if [[ -d docker ]]; then
|
||||
done < <(find docker -type f -print0)
|
||||
fi
|
||||
|
||||
for file in README.md .env.example docs/installazione-docker-4-contesti.md; do
|
||||
for file in README.md .env.example docs/operations/compose-reference.md; do
|
||||
add_file install_files "$file"
|
||||
done
|
||||
if [[ -d docs/install ]]; then
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Mutation fixtures for current auth/DWH documentation; never contacts services."""
|
||||
import importlib.util
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
SCRIPT = ROOT / "scripts/verify-auth-docs.py"
|
||||
spec = importlib.util.spec_from_file_location("authdocs", SCRIPT)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
mode = sys.argv[1] if len(sys.argv) > 1 else "auth"
|
||||
assert mode in ("auth", "dwh")
|
||||
relative = next(iter(module.AUTH if mode == "auth" else module.DWH))
|
||||
|
||||
with tempfile.TemporaryDirectory(prefix="thoth-auth-docs-") as tmp:
|
||||
fixture = Path(tmp)
|
||||
shutil.copytree(ROOT / "docs", fixture / "docs")
|
||||
for item in ROOT.glob("*.md"):
|
||||
shutil.copy2(item, fixture / item.name)
|
||||
target = fixture / relative
|
||||
original = target.read_text()
|
||||
|
||||
def check(expected=None):
|
||||
result = subprocess.run([sys.executable, str(SCRIPT), mode, "--root", tmp], capture_output=True, text=True)
|
||||
if expected is None:
|
||||
assert result.returncode == 0, result.stderr
|
||||
else:
|
||||
assert result.returncode != 0 and expected in result.stderr, result.stderr
|
||||
|
||||
check()
|
||||
mutations = [
|
||||
("curl -k https://example.invalid", "TLS bypass"),
|
||||
("curl --insecure https://example.invalid", "TLS bypass"),
|
||||
("verify_tls=false", "TLS bypass"),
|
||||
("DWH_API_KEY=synthetic", "raw environment secret"),
|
||||
('curl -H "X-API-Key: synthetic" https://example.invalid', "raw key header"),
|
||||
("tht auth user add demo --password synthetic", "secret argument"),
|
||||
("password: synthetic", "plaintext password"),
|
||||
("chmod 644 /protected/demo.key", "world-readable secret"),
|
||||
("sudo nginx -T", "raw nginx capture"),
|
||||
("git diff /protected/secret", "raw diff capture"),
|
||||
("docker compose up dwh-auth", "Compose coupling"),
|
||||
("thtdwh_v1." + "a" * 16 + "." + "b" * 43, "credential literal"),
|
||||
("secret_sha256: " + "a" * 64, "credential digest"),
|
||||
("auth-canonical:/run/thothii-auth:ro", "canonical auth root"),
|
||||
("useradd --uid 10001 core", "host runtime identity"),
|
||||
("nano /protected/generations/one/auth.yaml", "direct auth projection edit"),
|
||||
]
|
||||
for snippet, error in mutations:
|
||||
target.write_text(original + "\n```sh\n" + snippet + "\n```\n")
|
||||
check(error)
|
||||
target.write_text(original + '\nDo not use `curl -k`.\n```sh\nDWH_API_KEY_FILE=/protected/demo.key\n```\n')
|
||||
check()
|
||||
if mode == "auth":
|
||||
target.write_text(original.replace("`memory.manage`, ", ""))
|
||||
check("role-to-permission")
|
||||
target.write_text(original.replace("auth_ready\n", "auth_unknown\n"))
|
||||
check("diagnostic code")
|
||||
target.write_text(original + "\n[broken](missing-file.md)\n")
|
||||
check("broken local link")
|
||||
target.unlink()
|
||||
check("missing")
|
||||
print(f"{mode} documentation mutation fixtures passed")
|
||||
@@ -1,319 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
verify="$root/scripts/verify-dwh-auth-docs.sh"
|
||||
temp_root=
|
||||
|
||||
report_pass() {
|
||||
printf 'case=%s status=PASS\n' "$1"
|
||||
}
|
||||
|
||||
report_fail() {
|
||||
printf 'case=%s status=FAIL\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if [[ "$temp_root" == /tmp/thothii-dwh-auth-docs.* && -d "$temp_root" ]]; then
|
||||
rm -rf -- "$temp_root"
|
||||
fi
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
[[ -x "$verify" ]] || report_fail verifier_missing
|
||||
|
||||
temp_root=$(mktemp -d /tmp/thothii-dwh-auth-docs.XXXXXXXX) || report_fail fixture_root
|
||||
fixture_root="$temp_root/fixture"
|
||||
mkdir -p "$fixture_root/docs/install" "$fixture_root/docs/operations" \
|
||||
"$fixture_root/docs/testing/evidence" "$fixture_root/scripts" "$fixture_root/deploy/psd"
|
||||
|
||||
for relative in \
|
||||
docs/install/dwh-auth-server.md \
|
||||
docs/install/dwh-auth-client-enrollment.md \
|
||||
docs/install/dwh-auth-tls.md \
|
||||
docs/operations/psd-dwh-auth-rollout.md \
|
||||
docs/testing/dwh-auth-manual-acceptance.md \
|
||||
docs/testing/evidence/psd-dwh-auth-rollout-report-template.md \
|
||||
docs/install/local-workspace-registry.md \
|
||||
docs/install/server-workspace-registry.md \
|
||||
docs/install/psd-workspace-setup.md \
|
||||
docs/guida-utente.md \
|
||||
docs/index.md \
|
||||
mkdocs.yml; do
|
||||
mkdir -p "$fixture_root/$(dirname "$relative")"
|
||||
cp "$root/$relative" "$fixture_root/$relative"
|
||||
done
|
||||
cp -a "$root/docs/." "$fixture_root/docs/"
|
||||
cp "$root/deploy/psd/workspace-bindings.env.example" "$fixture_root/deploy/psd/workspace-bindings.env.example"
|
||||
|
||||
"$verify" --root "$fixture_root" || report_fail positive_source
|
||||
report_pass positive_source
|
||||
|
||||
expect_rejected() {
|
||||
local name=$1 target=$2 addition=$3
|
||||
local case_root="$temp_root/$name"
|
||||
cp -a "$fixture_root" "$case_root"
|
||||
printf '\n%s\n' "$addition" >>"$case_root/$target"
|
||||
if "$verify" --root "$case_root" >/dev/null 2>&1; then
|
||||
report_fail "$name"
|
||||
fi
|
||||
report_pass "$name"
|
||||
}
|
||||
|
||||
exercise_header_file_curl() {
|
||||
local v1_header="$temp_root/synthetic-v1.header"
|
||||
local legacy_header="$temp_root/synthetic-legacy.header"
|
||||
command -v curl >/dev/null 2>&1 || report_fail header_file_curl_command
|
||||
printf '%s\n' 'X-API-Key: synthetic-v1' >"$v1_header"
|
||||
printf '%s\n' 'X-API-Key: legacy opaque value /:[]' >"$legacy_header"
|
||||
chmod 0600 "$v1_header" "$legacy_header"
|
||||
if ! python3 - "$v1_header" "$legacy_header" <<'PY'
|
||||
import http.server
|
||||
import pathlib
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
|
||||
accepted = {"synthetic-v1", "legacy opaque value /:[]"}
|
||||
|
||||
class Handler(http.server.BaseHTTPRequestHandler):
|
||||
def log_message(self, _format, *_args):
|
||||
pass
|
||||
|
||||
def do_GET(self):
|
||||
self.send_response(204 if self.headers.get("X-API-Key") in accepted else 401)
|
||||
self.end_headers()
|
||||
|
||||
server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), Handler)
|
||||
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
||||
thread.start()
|
||||
try:
|
||||
for header_file in sys.argv[1:]:
|
||||
if stat.S_IMODE(pathlib.Path(header_file).stat().st_mode) != 0o600:
|
||||
raise SystemExit(1)
|
||||
result = subprocess.run(
|
||||
["curl", "--silent", "--show-error", "--output", "/dev/null", "--write-out", "%{http_code}", "--header", f"@{header_file}", f"http://127.0.0.1:{server.server_port}/rpc/ping"],
|
||||
check=False,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0 or result.stdout != "204":
|
||||
raise SystemExit(1)
|
||||
finally:
|
||||
server.shutdown()
|
||||
thread.join()
|
||||
PY
|
||||
then
|
||||
report_fail header_file_curl_synthetic
|
||||
fi
|
||||
report_pass header_file_curl_synthetic
|
||||
}
|
||||
|
||||
# Build synthetic only-in-fixture text at runtime: it is never a provisioned credential.
|
||||
fake_key="thtdwh_v1.$(printf 'A%.0s' {1..16}).$(printf 'A%.0s' {1..43})"
|
||||
fake_digest="$(printf 'A%.0s' {1..43})"
|
||||
|
||||
expect_rejected credential_literal docs/install/dwh-auth-client-enrollment.md "$fake_key"
|
||||
expect_rejected opaque_legacy_header_literal docs/install/dwh-auth-client-enrollment.md "curl --header 'X-API-Key: opaque-legacy-fixture' https://example.invalid/dwh/rpc/ping"
|
||||
expect_rejected credential_digest_literal docs/testing/evidence/psd-dwh-auth-rollout-report-template.md "\"secret_sha256\": \"$fake_digest\""
|
||||
expect_rejected curl_insecure docs/install/dwh-auth-tls.md 'curl -k https://example.invalid/dwh/rpc/ping'
|
||||
expect_rejected tls_disabled docs/install/dwh-auth-tls.md 'verify_tls=false'
|
||||
expect_rejected secret_in_environment docs/install/dwh-auth-client-enrollment.md "export THT_WS_PSD_CLINICAL_DWH_API_KEY=$fake_key"
|
||||
expect_rejected secret_in_argv docs/install/dwh-auth-client-enrollment.md "curl -H 'X-API-Key: $fake_key' https://example.invalid/dwh/rpc/ping"
|
||||
expect_rejected curl_config_artifact docs/install/dwh-auth-server.md 'curl --config /root/dwh-auth-provision/request.conf https://example.invalid/dwh/rpc/ping'
|
||||
expect_rejected world_readable_secret docs/install/dwh-auth-server.md 'chmod 0644 /root/dwh-auth-provision/client.key'
|
||||
expect_rejected sudo_raw_nginx_capture docs/operations/psd-dwh-auth-rollout.md 'sudo nginx -T > /tmp/nginx-full.conf'
|
||||
expect_rejected raw_diff_capture docs/operations/psd-dwh-auth-rollout.md 'sudo diff -u /etc/nginx/sites-available/policlinicosandonato /root/backup.conf'
|
||||
expect_rejected git_raw_diff_capture docs/operations/psd-dwh-auth-rollout.md 'git diff --no-index /root/old.conf /root/new.conf'
|
||||
expect_rejected raw_nginx_capture docs/operations/psd-dwh-auth-rollout.md 'nginx -T > /tmp/nginx-full.conf'
|
||||
expect_rejected compose_coupling docs/install/dwh-auth-server.md 'docker compose up dwh-auth'
|
||||
|
||||
expect_replacement_rejected() {
|
||||
local name=$1 target=$2 needle=$3 replacement=$4
|
||||
local case_root="$temp_root/$name"
|
||||
cp -a "$fixture_root" "$case_root"
|
||||
[[ $(grep -Foc -- "$needle" "$case_root/$target") -eq 1 ]] || report_fail "${name}_fixture"
|
||||
sed -i "s~$needle~$replacement~" "$case_root/$target"
|
||||
if "$verify" --root "$case_root" >/dev/null 2>&1; then report_fail "$name"; fi
|
||||
report_pass "$name"
|
||||
}
|
||||
|
||||
|
||||
expect_global_replacement_rejected() {
|
||||
local name=$1 target=$2 needle=$3 replacement=$4
|
||||
local case_root="$temp_root/$name"
|
||||
cp -a "$fixture_root" "$case_root"
|
||||
[[ $(grep -Foc -- "$needle" "$case_root/$target") -gt 0 ]] || report_fail "${name}_fixture"
|
||||
sed -i "s~$needle~$replacement~g" "$case_root/$target"
|
||||
if "$verify" --root "$case_root" >/dev/null 2>&1; then report_fail "$name"; fi
|
||||
report_pass "$name"
|
||||
}
|
||||
|
||||
exercise_header_file_curl
|
||||
|
||||
exercise_journal_scanner() {
|
||||
local journal_source="$fixture_root/docs/install/dwh-auth-server.md"
|
||||
local journal_code
|
||||
journal_code=$(python3 - "$journal_source" <<'PY2'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
source = pathlib.Path(sys.argv[1]).read_text(encoding="utf-8")
|
||||
start = source.index("if sudo python3 -c '") + len("if sudo python3 -c '")
|
||||
end = source.index("' \"$since\" \"$v1_key_file\" \"$legacy_key_file\"; then", start)
|
||||
print(source[start:end])
|
||||
PY2
|
||||
) || report_fail journal_extract
|
||||
[[ "$journal_code" == *"process.stdout.read1("* ]] || report_fail journal_chunk_reader
|
||||
[[ "$journal_code" != *"enumerate(process.stdout"* ]] || report_fail journal_unbounded_reader
|
||||
|
||||
local fake_bin="$temp_root/fake-bin"
|
||||
local fake_journalctl="$fake_bin/journalctl"
|
||||
local data_file="$temp_root/journal.data"
|
||||
local pid_file="$temp_root/journal.pid"
|
||||
local stdout_file="$temp_root/journal.stdout"
|
||||
local stderr_file="$temp_root/journal.stderr"
|
||||
mkdir -p "$fake_bin"
|
||||
python3 - "$fake_journalctl" <<'PY2'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
path = pathlib.Path(sys.argv[1])
|
||||
path.write_text("""#!/usr/bin/env python3
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import time
|
||||
|
||||
pathlib.Path(os.environ["JOURNAL_PID_FILE"]).write_text(str(os.getpid()))
|
||||
data = pathlib.Path(os.environ["JOURNAL_DATA_FILE"]).read_bytes()
|
||||
for offset in range(0, len(data), 997):
|
||||
os.write(sys.stdout.fileno(), data[offset:offset + 997])
|
||||
if os.environ.get("JOURNAL_HOLD") == "1":
|
||||
time.sleep(30)
|
||||
raise SystemExit(int(os.environ.get("JOURNAL_EXIT", "0")))
|
||||
""")
|
||||
path.chmod(0o755)
|
||||
PY2
|
||||
|
||||
local key_file="$temp_root/synthetic-real.key"
|
||||
local legacy_file="$temp_root/synthetic-legacy.key"
|
||||
printf '%s' 'REAL_SECRET_SYNTHETIC' >"$key_file"
|
||||
printf '%s' 'LEGACY_SECRET_SYNTHETIC' >"$legacy_file"
|
||||
chmod 0600 "$key_file" "$legacy_file"
|
||||
|
||||
run_journal_case() {
|
||||
local name=$1 expected=$2 data=$3 exit_code=$4 hold=$5
|
||||
printf '%s' "$data" >"$data_file"
|
||||
: >"$pid_file" "$stdout_file" "$stderr_file"
|
||||
if PATH="$fake_bin:$PATH" JOURNAL_DATA_FILE="$data_file" JOURNAL_PID_FILE="$pid_file" JOURNAL_EXIT="$exit_code" JOURNAL_HOLD="$hold" \
|
||||
python3 -c "$journal_code" synthetic-since "$key_file" "$legacy_file" >"$stdout_file" 2>"$stderr_file"; then
|
||||
actual=0
|
||||
else
|
||||
actual=$?
|
||||
fi
|
||||
[[ "$actual" -eq "$expected" ]] || report_fail "journal_${name}_status"
|
||||
[[ ! -s "$stdout_file" && ! -s "$stderr_file" ]] || report_fail "journal_${name}_output"
|
||||
if [[ -s "$pid_file" ]]; then
|
||||
pid=$(cat "$pid_file")
|
||||
[[ ! -e "/proc/$pid" ]] || report_fail "journal_${name}_reaped"
|
||||
fi
|
||||
report_pass "journal_${name}"
|
||||
}
|
||||
|
||||
local python_bin
|
||||
python_bin=$(command -v python3) || report_fail python_missing
|
||||
|
||||
run_journal_case clean 0 $'INFO clean\n' 0 0
|
||||
run_journal_case oversized_line 2 "$(printf 'A%.0s' {1..1048577})" 0 0
|
||||
run_journal_case too_many_lines 2 "$(printf 'x\n%.0s' {1..10001})" 0 0
|
||||
run_journal_case child_failure 2 $'INFO child failure\n' 7 0
|
||||
run_journal_case actual_key_across_chunk 1 "$(printf 'A%.0s' {1..65530})REAL_SECRET_SYNTHETIC" 0 1
|
||||
|
||||
local empty_path="$temp_root/empty-path"
|
||||
mkdir -p "$empty_path"
|
||||
printf '%s' 'INFO spawn failure\n' >"$data_file"
|
||||
: >"$pid_file" "$stdout_file" "$stderr_file"
|
||||
if PATH="$empty_path" JOURNAL_DATA_FILE="$data_file" JOURNAL_PID_FILE="$pid_file" JOURNAL_EXIT=0 JOURNAL_HOLD=0 \
|
||||
"$python_bin" -c "$journal_code" synthetic-since "$key_file" "$legacy_file" >"$stdout_file" 2>"$stderr_file"; then
|
||||
actual=0
|
||||
else
|
||||
actual=$?
|
||||
fi
|
||||
[[ "$actual" -eq 2 ]] || report_fail journal_spawn_failure_status
|
||||
[[ ! -s "$stdout_file" && ! -s "$stderr_file" ]] || report_fail journal_spawn_failure_output
|
||||
[[ ! -s "$pid_file" ]] || report_fail journal_spawn_failure_child
|
||||
report_pass journal_spawn_failure
|
||||
|
||||
chmod 000 "$key_file"
|
||||
: >"$pid_file" "$stdout_file" "$stderr_file"
|
||||
if PATH="$fake_bin:$PATH" JOURNAL_DATA_FILE="$data_file" JOURNAL_PID_FILE="$pid_file" JOURNAL_EXIT=0 JOURNAL_HOLD=0 \
|
||||
"$python_bin" -c "$journal_code" synthetic-since "$key_file" "$legacy_file" >"$stdout_file" 2>"$stderr_file"; then
|
||||
actual=0
|
||||
else
|
||||
actual=$?
|
||||
fi
|
||||
chmod 0600 "$key_file"
|
||||
[[ "$actual" -eq 2 ]] || report_fail journal_key_unreadable_status
|
||||
[[ ! -s "$stdout_file" && ! -s "$stderr_file" ]] || report_fail journal_key_unreadable_output
|
||||
[[ ! -s "$pid_file" ]] || report_fail journal_key_unreadable_child
|
||||
report_pass journal_key_unreadable
|
||||
|
||||
python3 - "$key_file" <<'PY2'
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
pathlib.Path(sys.argv[1]).write_bytes(b"K" * 65_537)
|
||||
PY2
|
||||
run_journal_case long_key_across_chunks 1 "$(printf 'A%.0s' {1..65535})$(printf 'K%.0s' {1..65537})" 0 1
|
||||
}
|
||||
|
||||
exercise_journal_scanner
|
||||
|
||||
expect_replacement_rejected missing_exact_gui_label docs/install/dwh-auth-client-enrollment.md "Validate workspace source" "Validate workspace"
|
||||
expect_replacement_rejected server_transport_contradiction docs/operations/psd-dwh-auth-rollout.md 'server PSD: `postgres_direct` read-only' 'server PSD: `rest_api` read-only'
|
||||
expect_replacement_rejected missing_mac_local_marker deploy/psd/workspace-bindings.env.example "Mac/local/remota" "server PSD"
|
||||
expect_replacement_rejected missing_private_ca docs/install/psd-workspace-setup.md "TLS_CA_FILE" "TLS_CA_REMOVED"
|
||||
expect_global_replacement_rejected missing_socket_path docs/install/dwh-auth-server.md "/run/dwh-auth/verify.sock" "/run/dwh-auth/other.sock"
|
||||
expect_replacement_rejected rest_transport_flag deploy/psd/workspace-bindings.env.example "THT_WS_PSD_CLINICAL_DWH_TRANSPORT=rest_api" "THT_WS_PSD_CLINICAL_DWH_TRANSPORT=postgres_direct"
|
||||
|
||||
expect_replacement_rejected wrong_key_output_path docs/install/dwh-auth-server.md "key_output=/root/dwh-auth-provision/psd-mac-primary.key" "key_output=/root/dwh-auth-provision/psd-mac-primary-primary.key"
|
||||
expect_replacement_rejected wrong_legacy_key_id docs/install/dwh-auth-server.md "legacy_key_id=legacy-shared" "legacy_key_id=legacy-public-key-id"
|
||||
expect_global_replacement_rejected missing_header_file_transport docs/install/dwh-auth-server.md '--header "@' '--header "X-API-Key: '
|
||||
expect_global_replacement_rejected missing_legacy_header_file docs/install/dwh-auth-server.md "legacy_header_file" "retired_auth_header"
|
||||
expect_global_replacement_rejected missing_safe_restore docs/install/dwh-auth-server.md "registry_staging" "registry_overlay"
|
||||
expect_replacement_rejected unsafe_restore_overlay docs/install/dwh-auth-server.md "sudo tar --acls --xattrs -C \"\$registry_staging\" -xf \"\$registry_backup\"" "sudo tar --acls --xattrs -C /var/lib -xf \"\$registry_backup\""
|
||||
expect_replacement_rejected missing_guarded_registry_publish docs/install/dwh-auth-server.md "if ! sudo mv -T -- \"\$registry_root\" \"\$registry_previous\"; then" "sudo mv -T -- \"\$registry_root\" \"\$registry_previous\""
|
||||
expect_replacement_rejected missing_candidate_check_guard docs/install/dwh-auth-server.md "if ! sudo /usr/local/sbin/dwh-auth --registry-root \"\$registry_candidate\" check; then" "sudo /usr/local/sbin/dwh-auth --registry-root \"\$registry_candidate\" check"
|
||||
expect_replacement_rejected unsafe_registry_publish_target docs/install/dwh-auth-server.md "if ! sudo mv -T -- \"\$registry_root\" \"\$registry_previous\"; then" "if ! sudo mv \"\$registry_root\" \"\$registry_previous\"; then"
|
||||
expect_replacement_rejected missing_key_file_byte_guard docs/install/dwh-auth-server.md "key_file_bytes=PASS" "key_file_bytes=SKIPPED"
|
||||
expect_replacement_rejected missing_failed_registry_publish_target docs/install/dwh-auth-server.md "if ! sudo mv -T -- \"\$registry_root\" \"\$registry_staging/failed-dwh-auth\"; then" "if ! sudo mv \"\$registry_root\" \"\$registry_staging/failed-dwh-auth\"; then"
|
||||
expect_global_replacement_rejected missing_rollback_registry_publish_target docs/install/dwh-auth-server.md "if ! sudo mv -T -- \"\$registry_previous\" \"\$registry_root\"; then" "if ! sudo mv \"\$registry_previous\" \"\$registry_root\"; then"
|
||||
expect_global_replacement_rejected missing_exact_header_bytes docs/install/dwh-auth-server.md 'dd if="$1" bs=65536 status=none' 'dd if="$1" bs=1'
|
||||
|
||||
|
||||
expect_replacement_rejected missing_actual_key_journal_scan docs/install/dwh-auth-server.md "journal_actual_key_scan=PASS" "journal_generic_scan=PASS"
|
||||
expect_replacement_rejected missing_journal_actual_key_match docs/install/dwh-auth-server.md "any(needle in searchable for needle in needles)" "False"
|
||||
expect_replacement_rejected missing_journal_returncode_guard docs/install/dwh-auth-server.md "if process.returncode != 0:" "if process.returncode == 0:"
|
||||
journal_contract_source="$fixture_root/docs/install/dwh-auth-server.md"
|
||||
if grep -Fq 'result.stdout.splitlines' "$journal_contract_source" \
|
||||
|| grep -Fq 'enumerate(process.stdout' "$journal_contract_source" \
|
||||
|| ! grep -Fq 'max_journal_bytes = 1_048_576' "$journal_contract_source" \
|
||||
|| ! grep -Fq 'max_journal_lines = 10_000' "$journal_contract_source" \
|
||||
|| ! grep -Fq 'max_chunk_bytes = 65_536' "$journal_contract_source" \
|
||||
|| ! grep -Fq 'process.stdout.read1(' "$journal_contract_source" \
|
||||
|| ! grep -Fq 'remaining = max_journal_bytes - bytes_seen' "$journal_contract_source" \
|
||||
|| ! grep -Fq 'searchable = carry + chunk' "$journal_contract_source" \
|
||||
|| ! grep -Fq 'process.kill()' "$journal_contract_source"; then
|
||||
report_fail journal_streaming_limits
|
||||
fi
|
||||
report_pass journal_streaming_limits
|
||||
expect_replacement_rejected missing_socket_legacy_probe docs/install/dwh-auth-server.md "socket_legacy=PASS" "socket_legacy=SKIPPED"
|
||||
expect_replacement_rejected missing_https_post_revoke_v1 docs/install/dwh-auth-server.md "https_v1_post_revoke=PASS" "https_v1_post_revoke=SKIPPED"
|
||||
expect_replacement_rejected missing_https_post_revoke_legacy docs/install/dwh-auth-server.md "https_legacy_post_revoke=PASS" "https_legacy_post_revoke=SKIPPED"
|
||||
expect_global_replacement_rejected rollout_post_revoke_status docs/operations/psd-dwh-auth-rollout.md "v1=2xx post-revoca" "v1=204 post-revoca"
|
||||
|
||||
report_pass summary
|
||||
exec python3 "$root/scripts/test-verify-auth-docs.py" dwh
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate current authentication documentation, not retired rollout transcripts."""
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
AUTH = {
|
||||
"docs/architecture/authentication.md": ["## Diagnostics and ordering", "HttpOnly", "SameSite=Lax", "authRevision", "401", "403", "direct, non-empty", "ignored silently"],
|
||||
"docs/install/authentication-local.md": ["tht auth", "--password-file"],
|
||||
"docs/install/authentication-oidc.md": ["groups", "THT_OIDC_CLIENT_SECRET", "THT_AUTHENTIK_API_TOKEN", "/api/auth/oidc/callback"],
|
||||
"docs/install/authentik.md": ["groups"],
|
||||
"docs/install/authentication-upstream.md": ["AUTH_MODE=upstream"],
|
||||
"docs/testing/authentication-manual-acceptance.md": ["Remember me", "403", "Header identità inventati", "non sostituiscono"],
|
||||
}
|
||||
DWH = {
|
||||
"docs/install/dwh-auth-server.md": ["/var/lib/dwh-auth/", "/run/dwh-auth/verify.sock", "key create", "key revoke", "encrypted", "401", "503"],
|
||||
"docs/install/dwh-auth-client-enrollment.md": ["rest_api", "postgres_direct", "ssh_tunnel", "API_KEY_FILE", "TLS_CA_FILE", "/rpc/ping", "401", "503"],
|
||||
"docs/install/dwh-auth-tls.md": ["SAN", "TLS_CA_FILE", "openssl x509 -noout -fingerprint -sha256"],
|
||||
}
|
||||
CODES = """auth_ready auth_config_incomplete auth_config_invalid auth_session_store_invalid
|
||||
local_user_registry_invalid local_admin_missing oidc_secret_missing oidc_discovery_unreachable
|
||||
oidc_issuer_mismatch oidc_jwks_unreachable oidc_group_catalog_unreachable
|
||||
oidc_group_catalog_unauthorized oidc_mapped_group_missing oidc_mapped_group_ambiguous
|
||||
oidc_groups_claim_invalid oidc_device_flow_unavailable""".split()
|
||||
ADMIN = """session.use session.read_all session.manage_all settings.manage workspace.manage
|
||||
workspace.secrets.manage database.manage memory.manage evidence.manage pi.manage auth.diagnostics.read""".split()
|
||||
|
||||
|
||||
def verify(root, mode):
|
||||
requirements = AUTH if mode == "auth" else DWH
|
||||
sources = {}
|
||||
for relative, tokens in requirements.items():
|
||||
path = root / relative
|
||||
if not path.is_file():
|
||||
raise ValueError(f"missing {relative}")
|
||||
source = path.read_text()
|
||||
sources[relative] = source
|
||||
for token in tokens:
|
||||
if token.lower() not in source.lower():
|
||||
raise ValueError(f"{relative}: missing topic {token}")
|
||||
for target in re.findall(r"(?<!!)\[[^]]*\]\(([^)#]+)(?:#[^)]+)?\)", source):
|
||||
if "://" in target or target.startswith("mailto:"):
|
||||
continue
|
||||
candidate = (path.parent / target).resolve()
|
||||
if not candidate.is_file() or root.resolve() not in candidate.parents:
|
||||
raise ValueError(f"{relative}: broken local link {target}")
|
||||
|
||||
corpus = "\n".join(sources.values())
|
||||
# Negative prose can explain a forbidden command; only executable examples are
|
||||
# checked for command safety. Credential literals are forbidden everywhere.
|
||||
code = "\n".join(re.findall(r"```[^\n]*\n([\s\S]*?)```", corpus))
|
||||
for pattern, label, source in [
|
||||
(r"\b(?:thothctl|thothii-admin)\b", "obsolete host CLI", corpus),
|
||||
(r"(?im)^\s*(?:[\"']?password[\"']?)\s*:\s*\S+|\"password\"\s*:\s*\"[^\"]+\"", "plaintext password", code),
|
||||
(r"thtdwh_v1\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]{43}", "credential literal", corpus),
|
||||
(r"(?mi)^\s*[\"']?secret_sha256[\"']?\s*[:=]\s*[\"']?[A-Za-z0-9_-]{16,}", "credential digest", corpus),
|
||||
(r"(?mi)^\s*(?:export\s+)?[A-Z][A-Z0-9_]*(?:API_KEY|SECRET|TOKEN|PASSWORD)\s*=\s*[^\s#]+", "raw environment secret", code),
|
||||
(r"(?i)--(?:password|api-key|token)(?!-file)\b(?:\s+|=)\S+", "secret argument", code),
|
||||
(r"(?i)(?:-H|--header)\s+['\"][^'\"]*X-API-Key\s*:", "raw key header", code),
|
||||
(r"(?i)curl[^\n]*(?:\s-k\b|--insecure)|verify_tls\s*[:=]\s*false|insecure_skip_verify\s*[:=]\s*true", "TLS bypass", code),
|
||||
(r"(?i)chmod\s+0?[0-7][0-7][4-7]\s+[^\n]*(?:\.key|secret|provision|auth\.yaml|users\.yaml)", "world-readable secret", code),
|
||||
(r"(?m)^\s*(?:sudo\s+)?nginx\s+-T\b", "raw nginx capture", code),
|
||||
(r"(?m)^\s*(?:sudo\s+)?(?:diff\b|git\s+diff\b)", "raw diff capture", code),
|
||||
(r"(?i)docker\s+compose[^\n]*\bdwh-auth\b", "DWH service Compose coupling", code),
|
||||
(r"auth-canonical[^\n]*:/run/thothii-auth", "canonical auth root mounted into core", code),
|
||||
(r"(?:useradd|groupadd)[^\n]*10001", "host runtime identity creation", code),
|
||||
(r"(?im)^\s*(?:sudo\s+)?(?:vi|vim|nano|sed|cp|mv|rm|tee)\b[^\n]*(?:CURRENT|generations)", "direct auth projection edit", code),
|
||||
]:
|
||||
if re.search(pattern, source):
|
||||
raise ValueError(f"forbidden {label}")
|
||||
if mode == "auth":
|
||||
architecture = sources["docs/architecture/authentication.md"]
|
||||
user_row = "| `user` | `session.use` |"
|
||||
admin_row = "| `admin` | " + ", ".join(f"`{p}`" for p in ADMIN) + " |"
|
||||
if user_row not in architecture or admin_row not in architecture:
|
||||
raise ValueError("role-to-permission map is not exact")
|
||||
section = architecture.split("## Diagnostics and ordering", 1)[1].split("\n## ", 1)[0]
|
||||
block = re.search(r"```text\n([\s\S]*?)```", section)
|
||||
if not block or block.group(1).split() != CODES:
|
||||
raise ValueError("diagnostic code union is not exact")
|
||||
if re.search(r"(?:extra|unmapped|additional) groups[^.\n]*(?:generate|emit|produce) (?:warnings|alerts)", corpus, re.I):
|
||||
raise ValueError("misleading noise claim for unmapped groups")
|
||||
print(f"{mode} documentation contract passed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("mode", choices=["auth", "dwh"])
|
||||
parser.add_argument("--root", type=Path, default=Path(__file__).resolve().parent.parent)
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
verify(args.root, args.mode)
|
||||
except (ValueError, OSError) as exc:
|
||||
parser.exit(1, f"{args.mode} docs: {exc}\n")
|
||||
@@ -1,135 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Current public guides and internal authentication contracts; no retired runbooks.
|
||||
set -euo pipefail
|
||||
|
||||
root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)
|
||||
if [[ $# -gt 0 ]]; then
|
||||
[[ $# -eq 2 && $1 == --root && -d $2 ]] || { echo 'usage: verify-dwh-auth-docs.sh [--root DIRECTORY]' >&2; exit 2; }
|
||||
root=$(cd "$2" && pwd -P)
|
||||
fi
|
||||
|
||||
python3 - "$root" <<'PY'
|
||||
import pathlib, re, sys
|
||||
|
||||
root = pathlib.Path(sys.argv[1])
|
||||
docs = {
|
||||
"server": "docs/install/dwh-auth-server.md",
|
||||
"client": "docs/install/dwh-auth-client-enrollment.md",
|
||||
"tls": "docs/install/dwh-auth-tls.md",
|
||||
"rollout": "docs/operations/psd-dwh-auth-rollout.md",
|
||||
"manual": "docs/testing/dwh-auth-manual-acceptance.md",
|
||||
"evidence": "docs/testing/evidence/psd-dwh-auth-rollout-report-template.md",
|
||||
"local": "docs/install/local-workspace-registry.md",
|
||||
"server_registry": "docs/install/server-workspace-registry.md",
|
||||
"psd": "docs/install/psd-workspace-setup.md",
|
||||
"guide": "docs/guida-utente.md",
|
||||
"index": "docs/index.md",
|
||||
"nav": "mkdocs.yml",
|
||||
"psd_template": "deploy/psd/workspace-bindings.env.example",
|
||||
}
|
||||
text = {}
|
||||
for label, relative in docs.items():
|
||||
path = root / relative
|
||||
if not path.is_file():
|
||||
raise SystemExit(f"dwh-auth docs: missing {relative}")
|
||||
text[label] = path.read_text(encoding="utf-8")
|
||||
|
||||
requirements = {
|
||||
"server": ["key_output=/root/dwh-auth-provision/psd-mac-primary.key", "legacy_key_id=legacy-shared", "v1_header_file", "legacy_header_file", "random_header_file", "--header \"@", "registry_staging", "registry_previous", "registry_restore_rollback", "journal_actual_key_scan=PASS", "sys.argv[2:]", "read_bytes()", "subprocess.Popen", "stderr=subprocess.DEVNULL", "returncode != 0", "max_journal_bytes = 1_048_576", "max_journal_lines = 10_000", "process = None", "except OSError:", "stdout=subprocess.PIPE", "max_chunk_bytes = 65_536", "process.stdout.read1(", "remaining = max_journal_bytes - bytes_seen", "searchable = carry + chunk", "max_needle_length", "process.kill()", "process.wait()", "socket_v1=PASS", "socket_legacy=PASS", "https_v1_post_revoke=PASS", "https_legacy_post_revoke=PASS", "key_file_bytes=PASS", "dd if=\"$1\" bs=65536 status=none", "manifest", "journalctl", "systemctl disable --now", "trap", "/var/lib/dwh-auth", "root:dwh-auth", "2750", ".writer.lock", "0640", "/run/dwh-auth/verify.sock", "0660", "systemd", "key create", "key list", "key status", "key revoke", "check", "backup", "rollback", "disinstallazione", "rest_api", "postgres_direct", "ssh_tunnel"],
|
||||
"client": ["Workspace management", "Validate workspace source", "Test workspace connections", "Save entered secrets", "Forget stored value", "API_KEY_FILE", "THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE", "TLS_CA_FILE", "/rpc/ping", "rest_api", "postgres_direct", "ssh_tunnel", "401", "503", "rotazione", "revoca"],
|
||||
"tls": ["self-issued", ".it", ".com", "SAN", "TLS_CA_FILE", "openssl x509 -noout -fingerprint -sha256", "fuori banda", "rinnovo", "curl -k"],
|
||||
"rollout": ["v1=2xx", "legacy=2xx", "v1=2xx post-revoca", "legacy=401 post-revoca", "file header curl protetti 0600", "PASS/FAIL", "no raw diff", "Task 9", "Task 10", "IN_DISCUSSION", "postgres_direct", "rest_api", "legacy-shared", "nginx -t", "204", "401", "503", "Qdrant", "Ollama", "rollback"],
|
||||
"manual": ["credenziali reali", "sintetici", "/rpc/ping", "204", "401", "503", "TLS", "registry", "postgres_direct", "ssh_tunnel"],
|
||||
"evidence": ["ID pubblici", "owner", "mode", "timestamp", "checksum", "approvazione"],
|
||||
}
|
||||
for label, tokens in requirements.items():
|
||||
lowered = text[label].lower()
|
||||
for token in tokens:
|
||||
if token.lower() not in lowered:
|
||||
raise SystemExit(f"dwh-auth docs: {docs[label]} lacks required topic: {token}")
|
||||
|
||||
for path in [root / docs[k] for k in ("server", "client", "tls", "rollout", "manual", "evidence", "local", "server_registry", "psd", "guide", "index")]:
|
||||
source = path.read_text(encoding="utf-8")
|
||||
for target in re.findall(r"(?<!!)\[[^]]*\]\(([^)#]+)(?:#[^)]+)?\)", source):
|
||||
if "://" in target or target.startswith("mailto:"):
|
||||
continue
|
||||
candidate = (path.parent / target).resolve()
|
||||
if not candidate.is_file() or root.resolve() not in candidate.parents:
|
||||
raise SystemExit(f"dwh-auth docs: broken local link {path.relative_to(root)} -> {target}")
|
||||
|
||||
nav = text["nav"]
|
||||
for relative in (docs["server"], docs["client"], docs["tls"], docs["rollout"], docs["manual"], docs["evidence"]):
|
||||
nav_relative = relative.removeprefix("docs/")
|
||||
if nav.count(nav_relative) != 1:
|
||||
raise SystemExit(f"dwh-auth docs: navigation must include once: {nav_relative}")
|
||||
|
||||
corpus = "\n".join(text.values())
|
||||
for pattern, label in [
|
||||
(r"thtdwh_v1\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]{43}", "credential literal"),
|
||||
(r"(?mi)^\s*[\"']?secret_sha256[\"']?\s*[:=]\s*[\"']?[A-Za-z0-9_-]{16,}", "credential digest literal"),
|
||||
(r"(?mi)^\s*(?:export\s+)?[A-Z][A-Z0-9_]*(?:API_KEY|SECRET|TOKEN|PASSWORD)\s*=\s*(?!/|<)[^\s#]+", "secret in environment"),
|
||||
(r"(?i)(?:curl|dwh-auth)[^\n]{0,240}(?:(?:-H|--header)\s+['\"][^'\"]*X-API-Key\s*:|--(?:api-key|token|password)\b)", "secret in argv"),
|
||||
(r"(?im)^(?!.*(?:non usare|mai usare)).*curl\s+(?:[^\n]*\s)?(?:-k|--insecure)\b|verify_tls\s*=\s*false|insecure_skip_verify", "TLS bypass"),
|
||||
(r"(?i)chmod\s+0?[0-7][0-7][4-7]\s+[^\n]*(?:\.key|secret|provision)", "world-readable secret"),
|
||||
(r"(?m)^\s*(?:sudo\s+)?nginx\s+-T\b", "raw Nginx capture"),
|
||||
(r"(?m)^\s*(?:sudo\s+)?(?:diff\b|git\s+diff\b)", "raw diff capture"),
|
||||
(r"(?m)^\s*(?:sudo\s+)?curl[^\n]*--config\b", "curl config artifact"),
|
||||
(r"(?i)docker\s+compose[^\n]*\bdwh-auth\b", "Compose coupling"),
|
||||
]:
|
||||
if re.search(pattern, corpus):
|
||||
raise SystemExit(f"dwh-auth docs: forbidden {label}")
|
||||
|
||||
server_direct = re.search(r"server PSD[^\n]{0,100}postgres_direct", text["rollout"], re.I)
|
||||
psd_direct = re.search(r"server PSD[^\n]{0,100}postgres_direct", text["psd"], re.I)
|
||||
if not server_direct or not psd_direct:
|
||||
raise SystemExit("dwh-auth docs: PSD server must remain postgres_direct")
|
||||
template = text["psd_template"]
|
||||
if "tht_ws_psd_clinical_dwh_transport=rest_api" not in template.lower() or "mac/local/remota" not in template.lower() or not re.search(r"mai .*server psd", template, re.I):
|
||||
raise SystemExit("dwh-auth docs: PSD REST template must be explicitly Mac/local/remota, never server PSD")
|
||||
if "TLS_CA_FILE" not in text["psd"] or re.search(r"(?i)nessuna CA|HTTPS pubblico", text["psd"]):
|
||||
raise SystemExit("dwh-auth docs: PSD setup contradicts private CA TLS requirement")
|
||||
|
||||
restore_steps = (
|
||||
'registry_staging="/var/lib/.dwh-auth-restore-$run_id"',
|
||||
'registry_candidate="$registry_staging/dwh-auth"',
|
||||
'sudo tar --acls --xattrs -C "$registry_staging" -xf "$registry_backup"',
|
||||
'if ! sudo /usr/local/sbin/dwh-auth --registry-root "$registry_candidate" check; then',
|
||||
'if ! sudo mv -T -- "$registry_root" "$registry_previous"; then',
|
||||
'if ! sudo mv -T -- "$registry_candidate" "$registry_root"; then',
|
||||
'if ! sudo mv -T -- "$registry_previous" "$registry_root"; then',
|
||||
'if ! sudo mv -T -- "$registry_root" "$registry_staging/failed-dwh-auth"; then',
|
||||
)
|
||||
restore_positions = [text["server"].find(step) for step in restore_steps]
|
||||
if any(position < 0 for position in restore_positions) or restore_positions != sorted(restore_positions):
|
||||
raise SystemExit("dwh-auth docs: restore must stage/check then use guarded same-filesystem renames")
|
||||
if text["server"].count('if ! sudo mv -T -- "$registry_previous" "$registry_root"; then') != 2:
|
||||
raise SystemExit("dwh-auth docs: every restore rollback rename must use guarded mv -T --")
|
||||
if re.search(r"sudo tar[^\n]*-C /var/lib[^\n]*(?:-x|--extract)[^\n]*registry_backup", text["server"]):
|
||||
raise SystemExit("dwh-auth docs: restore must not overlay the live registry")
|
||||
|
||||
|
||||
journal_steps = (
|
||||
"subprocess.Popen(",
|
||||
"[\"journalctl\", \"-u\", \"dwh-auth\", \"--since\", sys.argv[1]",
|
||||
"sys.argv[2:]",
|
||||
"stderr=subprocess.DEVNULL",
|
||||
"stdout=subprocess.PIPE",
|
||||
"max_journal_bytes = 1_048_576",
|
||||
"max_journal_lines = 10_000",
|
||||
"process = None",
|
||||
"except OSError:",
|
||||
"max_chunk_bytes = 65_536",
|
||||
"process.stdout.read1(",
|
||||
"remaining = max_journal_bytes - bytes_seen",
|
||||
"searchable = carry + chunk",
|
||||
"max_needle_length",
|
||||
"process.kill()",
|
||||
"if process is not None:",
|
||||
"process.wait()",
|
||||
"if process.returncode != 0:",
|
||||
"any(needle in searchable for needle in needles)",
|
||||
)
|
||||
if any(step not in text["server"] for step in journal_steps):
|
||||
raise SystemExit("dwh-auth docs: journal scan must fail closed and check the actual key bytes")
|
||||
|
||||
print("dwh-auth documentation contract passed")
|
||||
PY
|
||||
exec python3 "$root/scripts/verify-auth-docs.py" dwh "$@"
|
||||
|
||||
@@ -24,6 +24,7 @@ INTERNAL_PAGES = {
|
||||
"disambiguazione-iniziale.md", "gestione-memory.md", "installazione-docker-4-contesti.md",
|
||||
"install/authentication-upstream.md", "operations/docker-refresh.md",
|
||||
"operations/shell-and-localization.md",
|
||||
"operations/compose-reference.md", "operations/public-docs-publication.md",
|
||||
}
|
||||
|
||||
|
||||
|
||||