From 64e6b9664a4a48e7feba680bbb4849e76cf163ca Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 28 Sep 2026 15:35:25 +0200 Subject: [PATCH] feat(cli): prepare and validate workspace documents offline Reuse the runtime catalog and workspace parsers in a standalone helper paired with tht. Add document templates, safe diagnostics, local Evidence checks, native bundle builds, shared CLI fixtures and IT/EN preparation guides. Record the approved document-first specification and ticket breakdown. Refs #43. --- CONTEXT.md | 11 + PROJECT_STATE.md | 9 +- backend/package-lock.json | 206 +++++++++ backend/package.json | 2 + backend/scripts/build-workspace-tools.mjs | 47 +++ backend/src/workspace-documents-cli.ts | 6 + backend/src/workspaces/catalog.ts | 6 +- backend/src/workspaces/documents.ts | 222 ++++++++++ backend/test/workspace-documents-cli.test.ts | 119 ++++++ docs/install/standalone-manual-en.md | 57 +++ docs/install/standalone-manual-it.md | 58 +++ ...26-09-14-manual-standalone-installation.md | 8 + .../2026-09-27-guided-installation-prd.md | 386 +++++++++++++++++ ...26-09-27-guided-installation-resumption.md | 155 +++++++ ...-09-28-document-first-installation-spec.md | 387 +++++++++++++++++ ...026-09-28-installation-ticket-breakdown.md | 394 ++++++++++++++++++ tools/tht/README.md | 41 ++ tools/tht/cmd/tht/main.go | 7 + tools/tht/cmd/tht/workspace_documents.go | 49 +++ tools/tht/cmd/tht/workspace_documents_test.go | 29 ++ 20 files changed, 2195 insertions(+), 4 deletions(-) create mode 100644 backend/scripts/build-workspace-tools.mjs create mode 100644 backend/src/workspace-documents-cli.ts create mode 100644 backend/src/workspaces/documents.ts create mode 100644 backend/test/workspace-documents-cli.test.ts create mode 100644 docs/plans/2026-09-27-guided-installation-prd.md create mode 100644 docs/plans/2026-09-27-guided-installation-resumption.md create mode 100644 docs/plans/2026-09-28-document-first-installation-spec.md create mode 100644 docs/plans/2026-09-28-installation-ticket-breakdown.md create mode 100644 tools/tht/cmd/tht/workspace_documents.go create mode 100644 tools/tht/cmd/tht/workspace_documents_test.go diff --git a/CONTEXT.md b/CONTEXT.md index 21849391..547322d4 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -638,6 +638,17 @@ procedura non implica che DWH o provider LLM siano locali o disponibili offline. installazione manuale: verifica dell'host, generazione della configurazione, predisposizione delle credenziali protette e avvio dei servizi. Non è un installer dell'applicazione. +**Installation preparation** — La predisposizione dei documenti che descrivono i workspace +e i parametri dell'installazione, prima di applicarli. Permette all'operatore di raccogliere +e correggere le informazioni senza avviare l'applicazione. + +**Installation validation** — La verifica ripetibile della completezza e coerenza dei +documenti e delle precondizioni di un'installazione. Distingue ciò che è stato verificato +da ciò che richiede un'applicazione già avviata. + +**Installation execution** — L'applicazione dei documenti verificati per predisporre e +avviare ThothII. Non raccoglie nuovi parametri dall'operatore durante l'esecuzione. + **Platform acceptance** — La verifica che una Manual standalone installation possa essere predisposta e avviata su una specifica combinazione di sistema operativo, architettura e runtime, distinta dalla verifica funzionale del collegamento a DWH e provider LLM. diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index ce318f62..cd7bf512 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -1,10 +1,17 @@ # Project state -Updated: 2026-09-15. This is a current snapshot, not a release diary. Stable commands +Updated: 2026-09-28. 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 +- Document-first installation ticket #43 provides offline `tht workspace prepare` + and `tht workspace validate` through a native two-executable bundle. Build and + test instructions are in [the host CLI guide](tools/tht/README.md). Local validation + reuses runtime workspace/catalog parsers and explicitly defers runtime Evidence, + database binding and readiness checks. The remainder of the installation tickets, + Docker Hub publication and example databases remain pending. + - 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 diff --git a/backend/package-lock.json b/backend/package-lock.json index 0416e8b2..09ae155c 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -23,6 +23,7 @@ "@testcontainers/postgresql": "^12.1.0", "@types/node": "24.13.3", "@types/validator": "13.15.10", + "bun": "1.4.2", "tsx": "^4.19.0", "typescript": "^5.6.0", "vitest": "^2.1.0" @@ -802,6 +803,174 @@ "node": ">=8" } }, + "node_modules/@oven/bun-darwin-aarch64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-aarch64/-/bun-darwin-aarch64-1.4.2.tgz", + "integrity": "sha512-MXdZkP1featqxZ+/VTXWG1BVjM4OGBehVY2Q88EeUj/7L0UMeCGItmyPYTN+wxvlGJ6F66JEtzsw+GvQWewnag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-darwin-x64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-darwin-x64/-/bun-darwin-x64-1.4.2.tgz", + "integrity": "sha512-gZTxZuLjkUhAWjTETu3tw0WhsEdNkJ64daj60ybhPf835a2yollV3yTkK9JozvzKPx4TRFzLSl8C+U525pxVbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@oven/bun-freebsd-aarch64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-freebsd-aarch64/-/bun-freebsd-aarch64-1.4.2.tgz", + "integrity": "sha512-SMNItMw1Z8QeeQVKnw8jA7xQNkeXdP+OPgin4Wi/QTx/B8RHHLnuZfqmFy7NtVeT2NF0kKYppW4WWd2CCYZjhQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oven/bun-freebsd-x64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-freebsd-x64/-/bun-freebsd-x64-1.4.2.tgz", + "integrity": "sha512-THbPKXhO54N0DpFRKZNDZpQ7dpbX0bWASuARckAUS9wRtFIHsiY+uULXJvxJGo2YD1YewvXQ4G8Fj7XT5oBCiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@oven/bun-linux-aarch64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64/-/bun-linux-aarch64-1.4.2.tgz", + "integrity": "sha512-3BBP9ovJ2RGHFH6Ae1CAtxNtG1+YY6GD6rmYbsUosoAk9+OEl6zeDQ/k4fBkc6dYOJCtWnx8hUxzNzQATSmvYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-aarch64-android": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64-android/-/bun-linux-aarch64-android-1.4.2.tgz", + "integrity": "sha512-3mZKO2rhsNgbAUtAHC1UKUlF2zTxFraDZT/Elv8wzyH0fJL9h+Iv3TgB9lO63w89PRn3eFe+NRA1bhVgikKNPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oven/bun-linux-aarch64-musl": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-aarch64-musl/-/bun-linux-aarch64-musl-1.4.2.tgz", + "integrity": "sha512-+Sm6y+lSiSFBOtXmnekp5Q6n1tUKlyv71FCPWBc61Cgb14T5eBs8SN/nh4MUCOKzONkI3O+as3MGUgikS4aCBQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64/-/bun-linux-x64-1.4.2.tgz", + "integrity": "sha512-9/E/UXOTpSo3YsV5g+FhtTd/qTpiWoKuxS12cqtuYA1ssu9fRAoPQnipFgGyck3tWO63iUdxBiygq+kELFawng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-linux-x64-android": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-android/-/bun-linux-x64-android-1.4.2.tgz", + "integrity": "sha512-6HC5tzcC79113n2IHCTJMWv+HsQImv4ZFEK2XpYLxY6HbT8tM4cUM2Zv1bHZBQsS3jv/zYBamDJ1UX7If0d5tw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@oven/bun-linux-x64-musl": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-linux-x64-musl/-/bun-linux-x64-musl-1.4.2.tgz", + "integrity": "sha512-vVTKUg1bnPhRP/Hp73jIVoFh2vPFNYEqYX0ERKfZBOQEEHitNAeukZzzuUDZS0SoDCIpuWUGSpd/CDMbjdR+Uw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@oven/bun-windows-aarch64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-aarch64/-/bun-windows-aarch64-1.4.2.tgz", + "integrity": "sha512-8EJ1ST7339WJE3poPW5nBgVW/lWf9HBz4W27ZUNhburKmcBLOByPyE6DP9fHD8FQGm5c+ilUN2hX1mrW0jxq9Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@oven/bun-windows-x64": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@oven/bun-windows-x64/-/bun-windows-x64-1.4.2.tgz", + "integrity": "sha512-+bN6OuVld/9diT/RLSXSW7JE6CvNE3gL9XsAEjULi1nUsXd6DNO6GuA9jNdNb3r8PdJFnYHr5aypNV1Oj3Rd9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@pinojs/redact": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", @@ -1876,6 +2045,43 @@ "node": ">=10.0.0" } }, + "node_modules/bun": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/bun/-/bun-1.4.2.tgz", + "integrity": "sha512-TrSXo6HJfIEaczpb3kjX82I2pL47vK1QUNmHRCUdz9IzaOwa9lzOXSWwu2l18YHE3sNfGRapVLd4nNm+22vVVA==", + "cpu": [ + "arm64", + "x64" + ], + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "os": [ + "darwin", + "linux", + "android", + "freebsd", + "win32" + ], + "bin": { + "bun": "bin/bun.exe", + "bunx": "bin/bunx.exe" + }, + "optionalDependencies": { + "@oven/bun-darwin-aarch64": "1.4.2", + "@oven/bun-darwin-x64": "1.4.2", + "@oven/bun-freebsd-aarch64": "1.4.2", + "@oven/bun-freebsd-x64": "1.4.2", + "@oven/bun-linux-aarch64": "1.4.2", + "@oven/bun-linux-aarch64-android": "1.4.2", + "@oven/bun-linux-aarch64-musl": "1.4.2", + "@oven/bun-linux-x64": "1.4.2", + "@oven/bun-linux-x64-android": "1.4.2", + "@oven/bun-linux-x64-musl": "1.4.2", + "@oven/bun-windows-aarch64": "1.4.2", + "@oven/bun-windows-x64": "1.4.2" + } + }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index 96030dd4..292cdfcb 100644 --- a/backend/package.json +++ b/backend/package.json @@ -3,6 +3,7 @@ "private": true, "type": "module", "scripts": { + "build:workspace-tools": "node scripts/build-workspace-tools.mjs", "dev": "tsx watch src/server.ts", "prebuild": "node scripts/clean-dist.mjs", "build": "tsc -p tsconfig.json", @@ -31,6 +32,7 @@ "@testcontainers/postgresql": "^12.1.0", "@types/node": "24.13.3", "@types/validator": "13.15.10", + "bun": "1.4.2", "tsx": "^4.19.0", "typescript": "^5.6.0", "vitest": "^2.1.0" diff --git a/backend/scripts/build-workspace-tools.mjs b/backend/scripts/build-workspace-tools.mjs new file mode 100644 index 00000000..ed976720 --- /dev/null +++ b/backend/scripts/build-workspace-tools.mjs @@ -0,0 +1,47 @@ +// Maintainer-only build. The resulting two-binary bundle needs no extra host runtime. +import { spawnSync } from "node:child_process"; +import { createHash } from "node:crypto"; +import { mkdirSync, readFileSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const backend = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const repository = resolve(backend, ".."); +const native = `${process.platform === "win32" ? "windows" : process.platform}-${process.arch === "x64" ? "amd64" : process.arch}`; +const targets = { + "windows-amd64": ["windows", "amd64", "bun-windows-x64"], + "darwin-amd64": ["darwin", "amd64", "bun-darwin-x64"], + "darwin-arm64": ["darwin", "arm64", "bun-darwin-arm64"], + "linux-amd64": ["linux", "amd64", "bun-linux-x64"], + "linux-arm64": ["linux", "arm64", "bun-linux-arm64"], +}; +const requested = process.argv.slice(2); +const selected = requested.length === 1 && requested[0] === "--all" ? Object.keys(targets) : requested.length ? requested : [native]; +if (selected.some((target) => !targets[target])) { + console.error(`Usage: npm run build:workspace-tools -- [${Object.keys(targets).join("|")}|--all]`); + process.exit(2); +} +function run(command, args, cwd = backend, env = process.env) { + const result = spawnSync(command, args, { cwd, env, stdio: "inherit" }); + if (result.error || result.status !== 0) throw new Error(`Build failed: ${command}`); +} +const revision = spawnSync("git", ["rev-parse", "HEAD"], { cwd: repository, encoding: "utf8" }); +if (revision.status !== 0) throw new Error("Cannot read build revision"); +const commit = revision.stdout.trim(); +const buildTime = new Date().toISOString(); +const module = "github.com/aritmolab/thothii/tools/tht/internal/version"; +const bunPackage = JSON.parse(readFileSync(join(backend, "node_modules", "bun", "package.json"), "utf8")); +const bun = join(backend, "node_modules", "bun", bunPackage.bin.bun); +for (const target of selected) { + const [os, arch, bunTarget] = targets[target]; + const output = join(repository, "dist", "workspace-tools", target); + mkdirSync(output, { recursive: true }); + const extension = os === "windows" ? ".exe" : ""; + const names = [`tht${extension}`, `tht-workspace-documents${extension}`]; + run(bun, ["build", "src/workspace-documents-cli.ts", "--compile", `--target=${bunTarget}`, "--outfile", join(output, names[1])]); + run("go", ["build", "-trimpath", "-ldflags", `-s -w -X ${module}.semanticVersion=0.0.0-dev -X ${module}.commit=${commit} -X ${module}.buildTime=${buildTime}`, "-o", join(output, names[0]), "./cmd/tht"], join(repository, "tools", "tht"), { ...process.env, CGO_ENABLED: "0", GOOS: os, GOARCH: arch }); + const hashes = names.map((name) => `${createHash("sha256").update(readFileSync(join(output, name))).digest("hex")} ${name}\n`).join(""); + writeFileSync(join(output, "SHA256SUMS"), hashes); + writeFileSync(join(output, "build.json"), JSON.stringify({ commit, buildTime, target, bun: JSON.parse(readFileSync(join(backend, "package.json"), "utf8")).devDependencies.bun }, null, 2) + "\n"); + console.log(output); +} diff --git a/backend/src/workspace-documents-cli.ts b/backend/src/workspace-documents-cli.ts new file mode 100644 index 00000000..88362155 --- /dev/null +++ b/backend/src/workspace-documents-cli.ts @@ -0,0 +1,6 @@ +/** Compiled with its runtime for the host CLI: no installation, Docker or host Node required. */ +import { runWorkspaceDocuments } from "./workspaces/documents.js"; + +const result = runWorkspaceDocuments(process.argv.slice(2)); +console.log(result.output); +process.exitCode = result.status; diff --git a/backend/src/workspaces/catalog.ts b/backend/src/workspaces/catalog.ts index 95b09cac..fd747f79 100644 --- a/backend/src/workspaces/catalog.ts +++ b/backend/src/workspaces/catalog.ts @@ -44,8 +44,8 @@ const catalogSchema = z.object({ }); }); -function safeCatalogError(): Error { - return new Error("Workspace catalog is invalid"); +function safeCatalogError(cause?: unknown): Error { + return new Error("Workspace catalog is invalid", { cause }); } export function parseWorkspaceCatalogYaml(source: string): WorkspaceCatalog { @@ -57,7 +57,7 @@ export function parseWorkspaceCatalogYaml(source: string): WorkspaceCatalog { return catalogSchema.parse(document.toJSON()) as WorkspaceCatalog; } catch (error) { if (error instanceof Error && error.message === "Workspace catalog is invalid") throw error; - throw safeCatalogError(); + throw safeCatalogError(error); } } diff --git a/backend/src/workspaces/documents.ts b/backend/src/workspaces/documents.ts new file mode 100644 index 00000000..1e4fb92c --- /dev/null +++ b/backend/src/workspaces/documents.ts @@ -0,0 +1,222 @@ +import { closeSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { parseAllDocuments, stringify } from "yaml"; +import { ZodError } from "zod"; +import { CATALOG_PATH, parseWorkspaceCatalogYaml, assertCatalogMatchesDescriptor } from "./catalog.js"; +import { parseWorkspaceYaml, type WorkspaceDescriptor } from "./schema.js"; + +interface Issue { document: string; field: string; code: string; correction: string; line?: number } +interface Report { + schema_version: 1; + scope: "local-documents"; + ok: boolean; + workspaces: { id: string; evidence: "absent" | "local-files" | "remote-deferred" }[]; + issues: Issue[]; + deferred_checks: string[]; +} +const MAX_DOCUMENT_BYTES = 1024 * 1024; +const usage = "tht workspace prepare --directory NEW_PATH --id ID --name NAME [--language en|it] [--json]\ntht workspace validate --directory PATH [--json]"; + +class DocumentError extends Error { + constructor(readonly issue: Issue) { super(issue.correction); } +} +function fail(document: string, field: string, code: string, correction: string): never { + throw new DocumentError({ document, field, code, correction }); +} + +/** Never include parser messages or submitted values: YAML and Zod errors can contain secrets. */ +function decode(source: string, document: string, parser: (text: string) => T): T { + try { + const documents = parseAllDocuments(source, { uniqueKeys: true }); + if (documents.length !== 1) fail(document, "$", "yaml_documents", "Keep exactly one YAML document in this file."); + const problem = [...documents[0].errors, ...documents[0].warnings][0]; + if (problem) { + throw new DocumentError({ document, field: "$", code: "yaml_syntax", line: problem.linePos?.[0].line, + correction: "Correct YAML syntax, remove duplicate keys and unsupported tags at the indicated line." }); + } + return parser(source); + } catch (error) { + if (error instanceof DocumentError) throw error; + const cause = error instanceof Error && error.cause instanceof ZodError ? error.cause : error; + if (cause instanceof ZodError) { + const issue = cause.issues[0]; + // Strict schemas produce paths containing schema-defined keys and array indices only. + fail(document, issue.path.join(".") || "$", "schema_invalid", + issue.code === "unrecognized_keys" ? "Remove fields not defined by the current workspace/catalog contract." + : "Correct this field using workspace schema v4 or catalog schema v1; check type, required value, uniqueness and allowed values."); + } + fail(document, "$", "schema_invalid", "Use an authored workspace v4 descriptor; remove database configuration and keep it in the PostgreSQL Metadata Catalog."); + } +} + +function stat(root: string, document: string, directory: boolean) { + let info; + try { info = lstatSync(join(root, document)); } + catch { fail(document, "$", "missing_reference", "Create the referenced local file or directory and grant read access."); } + if (info.isSymbolicLink() || (directory ? !info.isDirectory() : !info.isFile())) { + fail(document, "$", "unsafe_reference", "Use a regular local file or directory, without symbolic links or special files."); + } + return info; +} +function readDocument(root: string, document: string): string { + if (stat(root, document, false).size > MAX_DOCUMENT_BYTES) { + fail(document, "$", "document_too_large", "Keep YAML documents below the 1 MiB local validation limit."); + } + try { return readFileSync(join(root, document), "utf8"); } + catch { fail(document, "$", "unreadable", "Grant read access to this document and retry validation."); } +} +function directories(root: string, document: string) { + stat(root, document, true); + try { return readdirSync(join(root, document), { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name)); } + catch { fail(document, "$", "unreadable", "Grant read and traversal access to this directory and retry validation."); } +} + +function inspectEvidence(root: string, descriptor: WorkspaceDescriptor): "absent" | "local-files" | "remote-deferred" { + const evidence = descriptor.evidence; + if (!evidence) return "absent"; + if (evidence.source.type !== "filesystem") return "remote-deferred"; + const base = evidence.source.uri; + stat(root, base, true); + if (evidence.schema_version === 2) stat(root, `${base}/curated`, true); + const patterns = evidence.source.patterns ?? ["**/*.md"]; + const literals = patterns.filter((pattern) => !/[?*\[]/.test(pattern)); + for (const pattern of literals) { + const parts = pattern.split("/"); + for (let index = 1; index < parts.length; index++) stat(root, `${base}/${parts.slice(0, index).join("/")}`, true); + stat(root, `${base}/${pattern}`, false); + } + // Inventory without following links; curated-unit interpretation remains a runtime check. + const pending = [base]; + let count = 0; + while (pending.length) { + const current = pending.pop()!; + for (const entry of directories(root, current)) { + if (++count > 100_000) fail(base, "evidence.source", "inventory_limit", "Reduce the Evidence tree below 100,000 entries before local validation."); + const path = `${current}/${entry.name}`; + if (entry.isDirectory()) pending.push(path); + else { + const info = stat(root, path, false); + const relative = path.slice(base.length + 1); + // Only apply content checks to selections whose meaning is unambiguous locally. + // Arbitrary globs are expanded by the canonical Python adapter after startup. + const curated = evidence.schema_version === 2 && relative.startsWith("curated/") && relative.endsWith(".md"); + const selected = curated || literals.includes(relative) || (patterns.includes("**/*.md") && relative.endsWith(".md")); + if (selected && info.size > evidence.source.max_bytes) fail(path, "evidence.source.max_bytes", "evidence_too_large", "Reduce the source file or increase the declared max_bytes limit deliberately."); + try { closeSync(openSync(join(root, path), "r")); } + catch { fail(path, "$", "unreadable", "Grant read access to this Evidence file and retry validation."); } + if (curated) { + const text = readDocument(root, path); + const frontmatter = text.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + if (!frontmatter) fail(path, "$", "evidence_frontmatter", "Add a YAML frontmatter block delimited by --- to the curated Markdown unit; follow the Curated Evidence contract."); + decode(frontmatter[1], path, (source) => parseAllDocuments(source)[0].toJSON()); + } + } + } + } + return "local-files"; +} + +function validate(root: string, report: Report): void { + directories(root, "."); + const catalog = decode(readDocument(root, CATALOG_PATH), CATALOG_PATH, parseWorkspaceCatalogYaml); + const ids = new Set(catalog.workspaces.map((entry) => entry.id)); + for (const entry of directories(root, ".")) { + if (entry.name === ".git") continue; + if (entry.isSymbolicLink()) fail(".", "$", "unsafe_reference", "Replace repository-root symbolic links with regular files or directories."); + if (entry.isDirectory() && entry.name !== "workspace-docs" && !ids.has(entry.name)) { + fail(".", "workspaces", "unlisted_directory", "Every root directory except workspace-docs must match a catalog workspace id; remove or register the extra directory."); + } + if (entry.name === "workspace-docs") { + for (const docs of directories(root, "workspace-docs")) { + if (!ids.has(docs.name)) fail("workspace-docs", "$", "unlisted_documentation", "Keep documentation only for workspace ids listed in the catalog."); + for (const file of directories(root, `workspace-docs/${docs.name}`)) { + const path = `workspace-docs/${docs.name}/${file.name}`; + if (!["README.md", "contract.env.example"].includes(file.name)) fail(`workspace-docs/${docs.name}`, "$", "unsupported_documentation", "Keep only README.md and contract.env.example in workspace-docs/. Place Evidence inside the workspace directory."); + stat(root, path, false); + } + } + } + } + for (const entry of catalog.workspaces) { + const document = `${entry.id}/workspace.yaml`; + try { + stat(root, entry.id, true); + const descriptor = decode(readDocument(root, document), document, parseWorkspaceYaml); + try { assertCatalogMatchesDescriptor(entry, descriptor); } + catch { fail(document, "workspace", "catalog_mismatch", "Make id, name and description identical in the catalog, descriptor and workspace directory name."); } + const evidence = inspectEvidence(root, descriptor); + report.workspaces.push({ id: entry.id, evidence }); + if (evidence !== "absent") report.deferred_checks.push(`${entry.id}:evidence-source-selection`, `${entry.id}:evidence-content-provenance-and-indexing`); + if (evidence === "remote-deferred") report.deferred_checks.push(`${entry.id}:remote-evidence-access`); + } catch (error) { + if (error instanceof DocumentError) report.issues.push(error.issue); + else throw error; + } + } +} + +function prepare(root: string, options: Map): void { + const id = options.get("--id"); + const name = options.get("--name"); + const language = options.get("--language") ?? "en"; + const catalog = stringify({ schema_version: 1, workspaces: [{ id, name }] }); + const descriptor = stringify({ workspace: { schema_version: 4, id, name, language } }); + decode(catalog, CATALOG_PATH, parseWorkspaceCatalogYaml); + decode(descriptor, "workspace.yaml", parseWorkspaceYaml); + try { mkdirSync(root); } + catch (error) { + if ((error as NodeJS.ErrnoException).code === "EEXIST") fail(".", "--directory", "destination_exists", "Choose a new directory; preparation never overwrites an existing directory or its documents."); + fail(".", "--directory", "destination_unavailable", "Create the parent directory and grant write access, then choose a new destination."); + } + try { + mkdirSync(join(root, id!)); + mkdirSync(join(root, "workspace-docs", id!), { recursive: true }); + writeFileSync(join(root, CATALOG_PATH), "# Index of workspace identities. Keep metadata identical to each descriptor.\n" + catalog, { flag: "wx" }); + writeFileSync(join(root, id!, "workspace.yaml"), "# Authored workspace v4: optional Evidence; database binding belongs to the Metadata Catalog.\n" + descriptor, { flag: "wx" }); + writeFileSync(join(root, "workspace-docs", id!, "README.md"), + "# Workspace documents / Documenti workspace\n\n" + + "EN: Edit thoth-workspaces.yaml and /workspace.yaml together. Evidence is optional and absent by default. Add reviewed source material under /evidence only when configured. Database connections and schema belong to the installation Metadata Catalog. No example databases are downloaded.\n\n" + + "IT: Modificare insieme thoth-workspaces.yaml e /workspace.yaml. Le Evidence sono facoltative e inizialmente assenti. Inserire materiale verificato in /evidence solo quando configurato. Connessioni e schema dei database appartengono al Metadata Catalog dell'installazione. Nessun database di esempio viene scaricato.\n\n" + + "Repeat / Ripetere: `tht workspace validate --directory `. Local success does not establish runtime readiness or semantic truth / Il successo locale non certifica readiness o verità semantica.\n", { flag: "wx" }); + } catch { + rmSync(root, { recursive: true, force: true }); + fail(".", "$", "prepare_failed", "Preparation could not write the documents; check disk space and permissions, then retry with a new directory."); + } +} + +export function runWorkspaceDocuments(args: string[]): { status: number; output: string } { + const report: Report = { schema_version: 1, scope: "local-documents", ok: false, workspaces: [], issues: [], deferred_checks: ["catalog-database-binding", "database-connectivity", "runtime-preprocessing"] }; + const json = args.includes("--json"); + let status = 1; + try { + const [command, ...rest] = args; + if ((command === "prepare" || command === "validate") && rest.length === 1 && rest[0] === "--help") return { status: 0, output: usage }; + const allowed = command === "prepare" ? ["--directory", "--id", "--name", "--language"] : command === "validate" ? ["--directory"] : []; + const options = new Map(); + const seen = new Set(); + for (let index = 0; index < rest.length; index++) { + const key = rest[index]; + if (seen.has(key)) fail("CLI", "$", "usage", usage); + seen.add(key); + if (key === "--json") continue; + if (!allowed.includes(key) || !rest[index + 1] || rest[index + 1].startsWith("--")) fail("CLI", "$", "usage", usage); + options.set(key, rest[++index]); + } + if (!allowed.length || !options.has("--directory") || (command === "prepare" && (!options.has("--id") || !options.has("--name")))) fail("CLI", "$", "usage", usage); + const root = resolve(options.get("--directory")!); + if (command === "prepare") prepare(root, options); + validate(root, report); + report.ok = report.issues.length === 0; + status = report.ok ? 0 : 1; + } catch (error) { + report.issues.push(error instanceof DocumentError ? error.issue : { document: ".", field: "$", code: "io_error", correction: "Check local permissions and regular files, then retry; no services were started." }); + if (report.issues[0].code === "usage") status = 2; + } + const output = json ? JSON.stringify(report) : [ + report.ok ? "Workspace documents pass local validation." : "Workspace documents require corrections.", + ...report.issues.map((issue) => `${issue.document}${issue.line ? `:${issue.line}` : ""} [${issue.field}] ${issue.code}: ${issue.correction}`), + ...report.workspaces.map((entry) => `${entry.id}: Evidence ${entry.evidence}`), + `Deferred until runtime: ${report.deferred_checks.join(", ")}.`, + ].join("\n"); + return { status, output }; +} diff --git a/backend/test/workspace-documents-cli.test.ts b/backend/test/workspace-documents-cli.test.ts new file mode 100644 index 00000000..49303018 --- /dev/null +++ b/backend/test/workspace-documents-cli.test.ts @@ -0,0 +1,119 @@ +import { spawnSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync, symlinkSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join, resolve } from "node:path"; +import { afterEach, expect, test } from "vitest"; +import { parseWorkspaceCatalogYaml } from "../src/workspaces/catalog.js"; +import { parseWorkspaceYaml } from "../src/workspaces/schema.js"; + +const roots: string[] = []; +function directory() { + const root = mkdtempSync(join(tmpdir(), "tht-documents-")); + roots.push(root); + return join(root, "workspaces"); +} +afterEach(() => roots.splice(0).forEach((root) => rmSync(root, { recursive: true, force: true }))); + +function cli(...args: string[]) { + const packaged = process.env.THT_WORKSPACE_TEST_CLI; + const result = spawnSync(packaged ?? process.execPath, [...(packaged ? ["workspace"] : ["--import", "tsx", resolve("src/workspace-documents-cli.ts")]), ...args, "--json"], { + encoding: "utf8", env: { ...process.env, PATH: "" }, + }); + return { ...result, report: result.stdout.trim() ? JSON.parse(result.stdout) : null }; +} + +const descriptor = "workspace:\n schema_version: 4\n id: practice\n name: Practice\n language: en\n"; +const catalog = "schema_version: 1\nworkspaces:\n - id: practice\n name: Practice\n"; +const corpus = [ + { name: "minimal", descriptor, catalog, valid: true }, + { name: "optional Evidence", descriptor: descriptor + "evidence:\n source:\n type: http\n uris: [https://example.com/manual.md]\n", catalog, valid: true }, + { name: "duplicate YAML key", descriptor: descriptor + " id: practice\n", catalog, valid: false }, + { name: "ambiguous document", descriptor: descriptor + "---\n" + descriptor, catalog, valid: false }, + { name: "unknown workspace field", descriptor: descriptor + " secret: VERY_SECRET_VALUE\n", catalog, valid: false }, + { name: "database binding in authored descriptor", descriptor: descriptor + "dwh: {password: VERY_SECRET_VALUE}\n", catalog, valid: false }, + { name: "duplicate catalog id", descriptor, catalog: catalog + " - id: practice\n name: Practice\n", valid: false }, + { name: "unknown catalog field", descriptor, catalog: catalog + "secret: VERY_SECRET_VALUE\n", valid: false }, + { name: "invalid catalog YAML", descriptor, catalog: catalog + "schema_version: 1\n", valid: false }, + { name: "unsafe Evidence URI", descriptor: descriptor + "evidence:\n source:\n type: http\n uris: [https://user:VERY_SECRET_VALUE@example.com/file]\n", catalog, valid: false }, +]; + +test.each(corpus)("CLI and runtime agree: $name", (fixture) => { + const root = directory(); + mkdirSync(join(root, "practice"), { recursive: true }); + writeFileSync(join(root, "thoth-workspaces.yaml"), fixture.catalog); + writeFileSync(join(root, "practice/workspace.yaml"), fixture.descriptor); + let accepted = true; + try { parseWorkspaceCatalogYaml(fixture.catalog); parseWorkspaceYaml(fixture.descriptor); } + catch { accepted = false; } + expect(accepted).toBe(fixture.valid); + const checked = cli("validate", "--directory", root); + expect(checked.status).toBe(fixture.valid ? 0 : 1); + expect(checked.report.ok).toBe(accepted); + expect(checked.stdout + checked.stderr).not.toContain("VERY_SECRET_VALUE"); + if (!fixture.valid) { + expect(checked.report.issues[0].document).not.toBe("."); + expect(checked.report.issues[0].correction.length).toBeGreaterThan(10); + } +}); + +test("prepare refuses existing directories and invalid options without changing documents", () => { + const root = directory(); + expect(cli("prepare", "--directory", root, "--id", "practice", "--name", "Practice").status).toBe(0); + const before = readFileSync(join(root, "thoth-workspaces.yaml"), "utf8"); + expect(cli("prepare", "--directory", root, "--id", "other", "--name", "Other").report.issues[0].code).toBe("destination_exists"); + expect(readFileSync(join(root, "thoth-workspaces.yaml"), "utf8")).toBe(before); + expect(cli("validate", "--directory", root, "--typo", "VERY_SECRET_VALUE").status).toBe(2); +}); + +test("validation rejects directory mismatches, missing references and symlinks without following them", () => { + const root = directory(); + cli("prepare", "--directory", root, "--id", "practice", "--name", "Practice"); + mkdirSync(join(root, "unlisted")); + expect(cli("validate", "--directory", root).report.issues.some((i: {code: string}) => i.code === "unlisted_directory")).toBe(true); + rmSync(join(root, "unlisted"), { recursive: true }); + writeFileSync(join(root, "practice/workspace.yaml"), descriptor.replace("name: Practice", "name: Different")); + expect(cli("validate", "--directory", root).report.issues[0].code).toBe("catalog_mismatch"); + writeFileSync(join(root, "practice/workspace.yaml"), descriptor + "evidence:\n source:\n type: filesystem\n uri: practice/evidence\n"); + expect(cli("validate", "--directory", root).report.issues[0].code).toBe("missing_reference"); + symlinkSync(roots[roots.length - 1], join(root, "practice/evidence"), "dir"); + expect(cli("validate", "--directory", root).report.issues[0].code).toBe("unsafe_reference"); +}); + +test("local Evidence checks references and YAML frontmatter, and explicitly defers canonical content validation", () => { + const root = directory(); + cli("prepare", "--directory", root, "--id", "practice", "--name", "Practice"); + writeFileSync(join(root, "practice/workspace.yaml"), descriptor + "evidence:\n schema_version: 2\n source:\n type: filesystem\n uri: practice/evidence\n patterns: ['curated/**/*.md']\n"); + mkdirSync(join(root, "practice/evidence/curated/domain"), { recursive: true }); + const evidence = join(root, "practice/evidence/curated/domain/rule.md"); + writeFileSync(evidence, "---\nschema_version: 4\nid: evidence:rule\nkind: domain\nlanguage: en\npurposes: [sql_generation]\n---\n# Rule\n\n## Rule\nUse the order number.\n"); + const checked = cli("validate", "--directory", root); + expect(checked.status).toBe(0); + expect(checked.report.workspaces).toEqual([{ id: "practice", evidence: "local-files" }]); + expect(checked.report.deferred_checks).toContain("practice:evidence-content-provenance-and-indexing"); + writeFileSync(evidence, "---\nid: first\nid: VERY_SECRET_VALUE\n---\n# Rule\n"); + const invalid = cli("validate", "--directory", root); + expect(invalid.status).toBe(1); + expect(invalid.report.issues[0].document).toBe("practice/evidence/curated/domain/rule.md"); + expect(invalid.stdout).not.toContain("VERY_SECRET_VALUE"); + writeFileSync(join(root, "practice/workspace.yaml"), descriptor + "evidence:\n source:\n type: filesystem\n uri: practice/evidence\n patterns: [missing.md]\n"); + expect(cli("validate", "--directory", root).report.issues[0].code).toBe("missing_reference"); +}); + +test("prepare creates documents accepted by the runtime and validate is repeatable without installation or services", () => { + const root = directory(); + const prepared = cli("prepare", "--directory", root, "--id", "practice", "--name", "Practice"); + expect(prepared.stderr).toBe(""); + expect(prepared.status).toBe(0); + expect(prepared.report.ok).toBe(true); + const catalog = readFileSync(join(root, "thoth-workspaces.yaml"), "utf8"); + const descriptor = readFileSync(join(root, "practice/workspace.yaml"), "utf8"); + expect(parseWorkspaceCatalogYaml(catalog).workspaces[0].id).toBe("practice"); + expect(parseWorkspaceYaml(descriptor).evidence).toBeUndefined(); + for (let index = 0; index < 2; index++) { + const checked = cli("validate", "--directory", root); + expect(checked.status).toBe(0); + expect(checked.report.workspaces).toEqual([{ id: "practice", evidence: "absent" }]); + } + expect(readFileSync(join(root, "thoth-workspaces.yaml"), "utf8")).toBe(catalog); + expect(readFileSync(join(root, "practice/workspace.yaml"), "utf8")).toBe(descriptor); +}); diff --git a/docs/install/standalone-manual-en.md b/docs/install/standalone-manual-en.md index 312bb201..e8f6d412 100644 --- a/docs/install/standalone-manual-en.md +++ b/docs/install/standalone-manual-en.md @@ -7,6 +7,63 @@ question, queries an enterprise database read-only, and guides the user through core, PostgreSQL catalog, Qdrant, embedding service, and Pi run in Docker; Node.js, Python, and Pi are not required on the host. +## Prepare and validate workspace documents before starting the stack + +The first two steps of the new flow work without Docker, Node, Python, Pi or an +installation descriptor. Use the platform bundle with **both** `tht` and +`tht-workspace-documents` in the same directory (`.exe` on Windows). Put that directory +on `PATH`, or invoke the absolute executable path. Older packages containing only +`tht` do not provide this capability. Maintainers can currently build the bundle; +publishing assets and Docker Hub images belongs to a later delivery step. The rest +of this guide still describes the existing installation path. + +1. Choose a new directory outside the application checkout, with an existing parent: + + ```sh + tht workspace prepare --directory ./my-workspaces --id practice --name "Practice" --language en + ``` + + This creates `thoth-workspaces.yaml`, `practice/workspace.yaml` and + `workspace-docs/practice/README.md`. Existing destinations, even empty ones, are + refused. No services start, Git is not initialized and no remote is contacted. + Example databases remain a deferred subproject. For a curator-supplied repository, + use a separate local copy and go straight to step 3; read access to the origin is + sufficient. +2. Edit the catalog (schema v1) and workspace descriptor (schema v4) at your own pace. + Keep `id`, `name` and optional `description` identical in both; the id must match + the directory name. Root directories must match catalog entries, except + `workspace-docs` and the local `.git` directory. Database connections, schema and + credentials belong to the installation Metadata Catalog. Evidence is optional + and initially absent. +3. Validate, correct the reported document/field, and repeat: + + ```sh + tht workspace validate --directory ./my-workspaces + tht workspace validate --directory ./my-workspaces --json + ``` + + PowerShell uses the same arguments, for example + `C:\ThothII\bin\tht.exe workspace validate --directory C:\ThothII\my-workspaces`. + Validation changes no files. It rejects multiple/malformed YAML documents, + duplicate keys/ids, unknown fields, catalog/directory/descriptor mismatches, + missing local references and symbolic links. Fix the first error in each document + and repeat to reveal any subsequent errors. + +Evidence `absent` is valid. Filesystem checks cover directories, accessibility and +literal references; standard Markdown selections also check declared size limits. +Evidence v2 requires `curated/` and syntactically valid YAML frontmatter. Local limits +are 1 MiB per document read and 100,000 entries per Evidence tree. Arbitrary patterns, +the complete curated-unit contract, provenance, HTTP/S3 access and indexing remain +explicit runtime checks. See the [Evidence guide](../evidence.md). + +JSON includes `schema_version`, `scope: local-documents`, `ok`, `workspaces`, `issues` +and `deferred_checks`. Issues identify document, field, code, correction and YAML +line where available, without printing document values. Exit statuses: `0` local +success, `1` documents/access/bundle need correction, `2` invalid arguments. Local +success does not certify semantic truth, connectivity or readiness. The Git revision +activated later must contain the checked documents; this command does not publish +uncommitted files or empty directories. + ## Before you start: the two repositories There are two separate repositories: diff --git a/docs/install/standalone-manual-it.md b/docs/install/standalone-manual-it.md index 244d8b71..c97fc0c3 100644 --- a/docs/install/standalone-manual-it.md +++ b/docs/install/standalone-manual-it.md @@ -7,6 +7,64 @@ naturale, interroga in sola lettura un database aziendale e accompagna l’utent della SQL risultante. Il core, il catalogo PostgreSQL, Qdrant, il servizio di embedding e Pi vengono eseguiti in Docker; sul computer non servono Node.js, Python o Pi. +## Preparazione e verifica dei workspace prima dello stack + +I primi due passi del nuovo percorso funzionano senza Docker, Node, Python, Pi o +un file di installazione. Usare il bundle della propria piattaforma con **entrambi** +gli eseguibili `tht` e `tht-workspace-documents` nella stessa cartella (`.exe` su +Windows). Aggiungere la cartella al `PATH`, oppure usare il percorso completo. +Il vecchio pacchetto con il solo `tht` non contiene questa capacità. Il bundle è +attualmente producibile dal manutentore; pubblicazione degli asset e immagini +Docker Hub appartengono a una fase successiva. Il resto della guida descrive ancora +il percorso di installazione esistente. + +1. Scegliere una cartella nuova, esterna all'applicazione, con padre già esistente: + + ```sh + tht workspace prepare --directory ./miei-workspace --id pratica --name "Pratica" --language it + ``` + + Sono creati `thoth-workspaces.yaml`, `pratica/workspace.yaml` e + `workspace-docs/pratica/README.md`. Una destinazione esistente, anche vuota, viene + rifiutata. Nessun servizio viene avviato; Git non viene inizializzato, nessun remoto + viene contattato. I database di esempio restano un sottoprogetto differito. Per + un repository fornito dal curatore, usare una copia locale separata e passare al + punto 3: è sufficiente accesso in lettura all'origine. +2. Modificare con calma catalogo (schema v1) e descrittore workspace (schema v4). + Mantenere uguali `id`, `name` e l'eventuale `description` nei due file; l'id deve + coincidere con la cartella. Ogni cartella alla radice deve corrispondere a un + workspace elencato, salvo `workspace-docs` e la directory locale `.git`. + Connessioni, schema e credenziali dei database appartengono al Metadata Catalog + dell'installazione. Le Evidence sono facoltative e inizialmente assenti. +3. Verificare, correggere il documento/campo indicato e ripetere: + + ```sh + tht workspace validate --directory ./miei-workspace + tht workspace validate --directory ./miei-workspace --json + ``` + + Su PowerShell usare gli stessi argomenti, ad esempio + `C:\ThothII\bin\tht.exe workspace validate --directory C:\ThothII\miei-workspace`. + Il controllo non modifica file. Rifiuta YAML multipli o malformati, chiavi/id + duplicati, campi sconosciuti, incoerenze tra catalogo, cartelle e descrittori, + riferimenti locali mancanti e link simbolici. Correggere il primo errore del + documento e ripetere per vedere eventuali errori successivi. + +Evidence `absent` è valido. Per filesystem si verificano directory, accessibilità e +riferimenti letterali; per le selezioni Markdown standard anche i limiti dichiarati. +Evidence v2 richiede `curated/` e frontmatter con sintassi YAML valida. I limiti locali +sono 1 MiB per documento letto e 100.000 elementi per albero Evidence. Pattern +arbitrari, contratto completo delle unità curate, provenienza, accesso HTTP/S3 e +indicizzazione restano controlli runtime espliciti. Vedere la [guida Evidence](../evidence.md). + +Il JSON espone `schema_version`, `scope: local-documents`, `ok`, `workspaces`, `issues` +e `deferred_checks`. I problemi riportano documento, campo, codice, correzione e, +quando disponibile, riga YAML, senza stampare i valori del documento. Exit status: +`0` successo locale, `1` documenti/accesso/bundle da correggere, `2` argomenti errati. +Il successo locale non certifica verità semantica, connettività o readiness. La +revisione Git attivata in seguito deve contenere i documenti verificati; il comando +non pubblica file non committati o directory vuote. + ## Prima di iniziare: i due repository Servono due repository distinti: diff --git a/docs/plans/2026-09-14-manual-standalone-installation.md b/docs/plans/2026-09-14-manual-standalone-installation.md index 035fd499..9b1ab480 100644 --- a/docs/plans/2026-09-14-manual-standalone-installation.md +++ b/docs/plans/2026-09-14-manual-standalone-installation.md @@ -3,6 +3,14 @@ **Stato:** design concordato con grill-with-docs il 2026-09-14. Questo worktree definisce e rende verificabile il percorso manuale; non introduce pacchetti nativi. +**Aggiornamento:** il [PRD del 27 settembre](2026-09-27-guided-installation-prd.md) +rende le immagini precompilate su Docker Hub il percorso ordinario e include la +loro pubblicazione nel progetto. Il percorso con build da sorgente documentato qui +resta un'alternativa; il precedente rinvio di Docker Hub è superato. +La revisione del 28 settembre dello stesso PRD sostituisce inoltre il setup +interattivo con preparazione dei documenti, verifiche ripetibili ed esecuzione +senza richiesta di parametri. + ## Obiettivo Permettere di predisporre una copia di THothII su macOS, Windows e Linux partendo dal clone del diff --git a/docs/plans/2026-09-27-guided-installation-prd.md b/docs/plans/2026-09-27-guided-installation-prd.md new file mode 100644 index 00000000..3eae7d46 --- /dev/null +++ b/docs/plans/2026-09-27-guided-installation-prd.md @@ -0,0 +1,386 @@ +# PRD — Installazione di ThothII da documenti verificati + +Creato: 2026-09-27. Revisione: 2026-09-28. Stato: requisiti e chiarimenti R1/R2 +approvati; `grill-with-docs` e `/to-spec` conclusi. Specifica pubblicata su Gitea +con etichetta `ready-for-agent`; implementazione non iniziata. +Branch: `codex/guided-standalone-install`. + +La [specifica derivata](2026-09-28-document-first-installation-spec.md) raccoglie +user story, decisioni implementative e collaudi. Il piano di test è stato confermato +dall'utente e la specifica è pubblicata nell'issue +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). +La [scomposizione approvata](2026-09-28-installation-ticket-breakdown.md) è pubblicata +nelle issue #43–#54 con dipendenze native. Il prossimo passaggio è `/implement` +sui ticket senza blocchi, inizialmente validazione workspace e rilascio Docker Hub. + +Questo documento consolida le decisioni della +[ripresa del progetto](2026-09-27-guided-installation-resumption.md) e aggiorna il +[piano standalone del 14 settembre](2026-09-14-manual-standalone-installation.md) +per l'esperienza guidata. Conserva architettura, protezione dei segreti e contratti +del prodotto; modifica il percorso operativo e l'ordine dei collaudi. La successiva +precisazione dell'utente rende la distribuzione di immagini precompilate su Docker Hub +parte della prima versione del percorso ordinario, superando il precedente rinvio. +La revisione del 28 settembre sostituisce la raccolta interattiva dei parametri: +si preparano prima i documenti, li si verifica anche più volte, poi si esegue il setup. +Prevale sulle precedenti decisioni I1/I2 dove consentivano configurazioni obbligatorie +rinviate o richieste durante l'installazione. + +## Obiettivo e destinatario + +Una persona capace di installare Docker, clonare un repository e fornire le proprie +credenziali deve poter predisporre con calma i documenti necessari e rendere +utilizzabile almeno un workspace, senza conoscere l'architettura interna. +Template commentati, esempi compilati e documentazione passo per passo spiegano +cosa inserire nei file YAML e nei file protetti `.env` o equivalenti. +DWH e provider LLM possono essere esterni; non si promette un funzionamento offline. + +La CLI offre preparazione dei template, verifiche ripetibili e applicazione dei +documenti già verificati. Il setup non raccoglie parametri, non apre questionari +e non completa silenziosamente documenti incompleti. L'utente corregge i documenti +prima di eseguirlo. Le pagine amministrative rimangono disponibili per l'uso e la +manutenzione successivi, senza diventare una scorciatoia per rinviare parametri +obbligatori dell'installazione. + +Il percorso predefinito scarica da Docker Hub le immagini applicative già compilate: +il computer dell'utente svolge configurazione, inizializzazione dei servizi e dei +database, avvio e verifiche. L'installazione da sorgente resta disponibile come +scelta esplicita alternativa. Nessuna compilazione dell'applicazione, neppure +all'interno di un container locale, è richiesta dal percorso ordinario. + +## Decisioni approvate + +| Area | Comportamento richiesto | +| --- | --- | +| Distribuzione predefinita | Creare, collaudare e pubblicare su Docker Hub le immagini applicative precompilate; il setup le scarica e le avvia senza build locale. | +| Alternativa da sorgente | Conservare un percorso esplicito di build dai sorgenti, documentato e verificato, con configurazione e funzionalità equivalenti. | +| Due traguardi | Mostrare separatamente piattaforma installata e workspace pronto. | +| Preparazione anticipata | Repository workspace e documenti applicativi predisposti prima dell'esecuzione; template, esempi e guida alla compilazione. | +| Setup senza domande | Consuma documenti già verificati, mostra avanzamento ed errori e non chiede valori mancanti. | +| Modelli | Provider, modelli, usi, endpoint e riferimenti alle credenziali descritti nei documenti prima del setup; configurazioni di esempio supportate e commentate. | +| Embedding | Configurazione locale precompilata come percorso ordinario. | +| Ripresa | Correggere i documenti, ripetere le verifiche e riprendere l'esecuzione senza questionari né perdita del lavoro completato. | +| Workspace | Preparare prima repository e descriptor conformi; dichiarare separatamente i parametri di collegamento ai database secondo i contratti ThothII. | +| Contenuti | Riutilizzare descrizioni/Evidence curate; nessuna generazione AI implicita per completare un documento. Eventuali attività di curation restano esplicite. | +| Evidence | Opzionali nel contratto; se configurate devono essere valide e utilizzabili. | +| Collaudi | Prima Windows, poi Omarchy su PC Intel, infine macOS, in tre tappe distinte. | + +## Rilascio e distribuzione delle immagini + +La creazione e pubblicazione delle immagini appartengono al processo di rilascio +del progetto. Il sottoprogetto installazione comprende quindi anche una procedura +riproducibile per costruire, verificare e pubblicare queste immagini su Docker Hub; +non basta aggiungere un'opzione di pull senza fornire immagini utilizzabili. + +**Stato iniziale dichiarato dall'utente il 28 settembre:** le immagini applicative +ThothII su Docker Hub non sono disponibili. Prima di collaudare l'installazione +precompilata su un PC Windows o Linux occorre produrle, pubblicarle e verificarne +il download. Questa dipendenza è bloccante per quel collaudo, non viene aggirata +usando immagini costruite soltanto nella cache della macchina di test. + +Il progetto deve fornire un comando di produzione e pubblicazione, mantenuto nel +repository e documentato per il manutentore. Il comando riceve revisione/versione, +namespace Docker Hub e architetture, costruisce e verifica le immagini proprietarie, +le pubblica e produce il manifest di rilascio con digest e artefatti compatibili. +Il nome e la sintassi saranno fissati nella specifica; il comando non è ancora +implementato. Le credenziali di pubblicazione appartengono al manutentore e non +entrano nei documenti dell'utente che installerà ThothII. + +La sequenza di rilascio è: produzione e controlli, pubblicazione su Docker Hub, +verifica del pull del rilascio pubblicato, quindi collaudo della procedura sui +sistemi destinatari. Questa fase del manutentore precede i sei passi dell'utente. + +Il pacchetto distribuito deve comprendere tutto ciò che serve all'installazione: +immagini applicative, manifest Compose/configurazione di avvio, migrazioni e risorse +di inizializzazione, oltre al comando operatore o al suo bootstrap. Il numero delle +immagini segue i servizi dell'architettura corrente: non è richiesto accorpare tutto +in un singolo container. I servizi di terze parti mantengono immagini compatibili +con lo stack, senza ricostruirli sul computer dell'utente. + +Nell'architettura corrente le immagini proprietarie da pubblicare sono `core` e +`frontend`; PostgreSQL, Qdrant e Ollama usano immagini upstream. Le attività +`catalog-migrate` e `workspace-maintenance` riusano la stessa immagine release di +`core`. Il pacchetto di installazione include anche le risorse oggi montate dal +checkout, fra cui `docker/catalog-db-init.sql` e `docker/embedding-model-init.sh`. +Il download del modello embedding, la creazione dei volumi/database e le migrazioni +restano attività di inizializzazione locale, distinte dalla compilazione. + +Ogni rilascio identifica una versione coerente di immagini, CLI e configurazione; +il manifest registra riferimenti verificabili, inclusi i digest delle immagini. +Il setup riporta la versione installata e non combina automaticamente componenti +incompatibili tramite tag mobili. Nomi e namespace Docker Hub saranno definiti +nella specifica di pubblicazione; non sono presunti già esistenti. + +Il percorso ordinario deve poter partire dal pacchetto di rilascio senza richiedere +il checkout dei sorgenti applicativi o strumenti di compilazione. Anche l'eventuale +CLI nativa deve essere distribuita già compilata per gli host supportati; nascondere +una compilazione di `tht` nel bootstrap non soddisfa il requisito. + +L'installazione pubblica non richiede credenziali di pubblicazione Docker Hub. +Le immagini non includono credenziali dell'installazione, dati personali, workspace +dell'operatore o i database di esempio preinstallati. Configurazione e dati +persistenti vengono creati localmente nei percorsi e volumi dell'installazione. + +Il supporto iniziale Windows/WSL2 e Omarchy richiede immagini Linux amd64; per la +tappa macOS Apple Silicon servono immagini Linux arm64 e un bootstrap host adeguato. +La pubblicazione dichiara solo le architetture effettivamente verificate, rispettando +l'ordine dei collaudi concordato. + +La modalità sorgente costruisce gli stessi componenti a partire da una revisione +esplicita, documenta i prerequisiti aggiuntivi e usa gli stessi contratti di +configurazione, persistenza e migrazione. Un errore di download da Docker Hub non +deve attivarla automaticamente: l'utente può correggere il problema e riprovare, +oppure scegliere consapevolmente l'alternativa da sorgente. + +## Percorso dell'utente + +Prima dei sei passi sono disponibili guida, template e strumenti di verifica già +compilati. Git e gli strumenti minimi necessari per acquisire i documenti sono +esplicitati nella guida; la verifica completa dell'host rimane al passo 3. I primi +controlli documentali non devono richiedere l'avvio di ThothII o dei suoi container. + +### 1. Preparazione del repository dei workspace + +L'utente prepara una copia del repository predefinito con Financial, European +Football e F1, oppure un repository ad hoc a partire da un template documentato. +Il repository predefinito contiene definizioni, documentazione, Evidence e riferimenti +ai pacchetti dati; il clone non equivale ad aver già creato i database PostgreSQL. +La disponibilità effettiva del percorso predefinito dipende dal sottoprogetto esempi. + +Si preservano le scelte già approvate sulla copia autonoma o sull'accesso in sola +lettura all'originale; workspace ed Evidence locali restano modificabili. La +preparazione non richiede diritti di push al repository pubblico e non sostituisce +un repository già configurato senza una scelta esplicita. + +### 2. Verifica dei documenti dei workspace + +Un comando dedicato verifica sintassi YAML, versione/schema ThothII, campi richiesti, +tipi, identificatori, unicità, corrispondenza fra catalogo e directory, descriptor +referenziati, percorsi e file Evidence dove configurati. La verifica è ripetibile +sui file locali prima che Docker o ThothII siano in esecuzione. + +La verifica sostanziale copre ciò che è dimostrabile dai documenti: coerenza dei +riferimenti, esistenza e leggibilità dei contenuti, conformità delle Evidence e +assenza di contraddizioni rilevabili. Non pretende di certificare automaticamente +la verità delle regole di dominio o interrogare database non ancora creati. + +Ogni errore identifica documento, campo e, quando disponibile, riga, con indicazione +della correzione. L'utente modifica i documenti e ripete il controllo. Le Evidence +restano opzionali: assenza dichiarata ed Evidence configurate ma invalide sono +condizioni diverse. I controlli incrociati che richiedono i parametri applicativi +vengono completati al passo 5. + +### 3. Verifica delle precondizioni + +Verificare sistema/architettura, Docker e Compose, accesso al daemon, risorse e spazio +richiesti, percorsi e permessi, porte previste, accesso ai servizi esterni e al registry +per quanto valutabile. Su Windows verificare Ubuntu WSL2 e integrazione Docker +Desktop. I requisiti dipendenti da valori scelti al passo 4 sono ricontrollati al 5. + +Distinguere componenti necessari sull'host da componenti inclusi nelle immagini: +Pi appartiene a `core`, non è un prerequisito da installare separatamente sul PC. +Presenza e versione di Pi sono verificate nel rilascio; il funzionamento effettivo +nel container è verificato al passo 6. Lo stesso principio vale per le dipendenze +applicative già incluse. Go, Python, Node e compilatori non sono prerequisiti host +del percorso precompilato. + +### 4. Preparazione dei parametri applicativi + +L'utente compila i documenti locali usando template commentati ed esempi: descriptor +di installazione, configurazione dei modelli e riferimenti ai file protetti `.env` +o equivalenti. Sono espliciti campi obbligatori, opzionali, default e condizioni in +cui un parametro serve. La preparazione può svolgersi in più sessioni senza avviare +il setup o i servizi applicativi. + +I documenti definiscono versione del rilascio, percorsi/volumi, profilo e accesso, +repository/workspace selezionati, provider/modelli per i rispettivi usi, endpoint, +embedding, parametri di collegamento ai database e riferimenti ai segreti. La +configurazione dei modelli deriva dall'Installation Model Catalog, senza un secondo +catalogo del setup. L'embedding locale ha un esempio precompilato. + +Il descriptor workspace v4 continua a contenere identità ed Evidence: non vi si +inseriscono campi database o modelli estranei al contratto. I binding database sono +predisposti in un input locale separato, da specificare, e applicati al Metadata +Catalog durante il setup mediante i suoi servizi; non diventano una seconda autorità +runtime. La configurazione server/Omics rimane fuori dal perimetro. + +I segreti non entrano nel repository pubblico, nei log o nei rapporti di verifica. +Per le credenziali tecniche interne un comando preparatorio può generare file +protetti prima delle verifiche, senza questionario né richiesta durante il setup. +Le selezioni degli esempi e le eventuali operazioni facoltative sono dichiarate +prima dell'esecuzione. Le pagine amministrative restano disponibili dopo l'avvio +per modifiche e curation, non per raccogliere valori obbligatori dimenticati. + +### 5. Verifica dei parametri applicativi + +Un comando ripetibile controlla completezza e correttezza dei documenti, sintassi +e compatibilità dei valori, riferimenti ai segreti, modelli/usi/default, collegamenti +workspace/database, configurazione Compose, disponibilità del rilascio nel registry +e compatibilità delle architetture. Completa i controlli incrociati dei passi 2 e 3. + +Quando fattibile senza creare lo stack, verifica raggiungibilità, autenticazione +e compatibilità delle dipendenze esterne con operazioni circoscritte; la documentazione +spiega le prove effettuate, inclusi eventuali accessi a provider a consumo. +Non cambia dati applicativi né esegue migrazioni o importazioni. + +Il rapporto distingue `superato`, `errore`, `avviso` e `non ancora verificabile`, +senza trasformare assenza di verifica in successo. I controlli che richiedono lo +stack sono elencati prima e obbligatori al passo 6, secondo R2 approvata. +Un errore formale o una configurazione obbligatoria mancante blocca l'esecuzione. + +Il risultato identifica i documenti e la revisione del repository esaminati. Una +modifica successiva invalida i controlli dipendenti: il setup non deve applicare +file diversi da quelli verificati senza ricontrollarli. Segreti e loro valori +non vengono copiati nel rapporto. + +### 6. Setup esecutivo + +Il setup ricontrolla l'ammissibilità del piano verificato, scarica le immagini del +rilascio da Docker Hub, crea reti/volumi/container e inizializza i database applicativi. +Esegue migrazioni, applicazione della configurazione e dei binding, sincronizzazione +e preparazione necessaria dei workspace secondo i contratti esistenti. Quando +disponibili e selezionati, crea e carica i database di esempio dal relativo pacchetto. + +Non pone domande su provider, modelli, percorsi, credenziali o altri parametri. +Se trova un valore mancante o incoerente, si ferma con una diagnosi e rimanda alla +correzione dei documenti e alla loro verifica; non apre un wizard di riparazione. +Le conferme dei contratti di dominio non vengono aggirate: la specifica deve +distinguere operazioni predisponibili nel piano da attività umane residue senza +reinserire la raccolta dei parametri durante l'installazione. + +Esegue i controlli disponibili solo a runtime: salute dei servizi, Pi, modello +embedding effettivamente caricato, collegamenti dalla rete dei container, Catalog +e preparazione dei workspace. Mostra separatamente piattaforma avviata e workspace +pronto, più eventuali verifiche funzionali ancora da svolgere. Il collaudo completo +include una domanda reale con revisione umana, senza SQL target di benchmark. + +## Documentazione di accompagnamento + +Le guide italiana e inglese seguono esattamente i sei passi. Per ciascuno indicano +input, file da predisporre, template ed esempio compilato, comando di verifica, +esito atteso, errori comuni e passaggio successivo. Un elenco dei documenti e dei +segreti necessari consente di raccogliere le informazioni in anticipo. + +Le istruzioni distinguono manutentore del rilascio e utente installatore, host e +container, controlli locali e runtime. Il percorso sorgente è esplicitamente +alternativo; un download fallito non ne provoca l'attivazione automatica. + +## Avanzamento, errori e ripresa + +La procedura conserva l'installazione di riferimento, gli input verificati, i +passaggi completati, l'ultimo errore e il prossimo passo, senza conservare segreti +nello stato di avanzamento. Alla ripresa verifica lo stato reale: una vecchia spunta +non prova che servizio, credenziale o indice siano ancora validi. + +Correggere un endpoint o una credenziale non impone di rifare tutto il setup. La +specifica deve definire quali verifiche dipendenti vanno ripetute, preservando +configurazioni, workspace, sessioni e contenuti non coinvolti nella modifica. +Le modifiche manuali vengono riconosciute e spiegate, non cancellate implicitamente. + +La ripresa riguarda le tappe della procedura: non promette una ripresa interna +di operazioni che non la supportano, come il preprocessing corrente. In questi +casi il passaggio viene rieseguito in modo coerente con il suo contratto. + +Un fallimento riporta fase, causa comprensibile, correzione consigliata e modalità +di ripresa. Distinguere problemi dell'host, dei servizi locali e delle dipendenze +esterne. Un provider o DWH indisponibile non annulla una verifica valida della +piattaforma, ma impedisce di dichiarare il percorso complessivo pronto. + +## Criteri di accettazione + +| Scenario | Esito verificabile | +| --- | --- | +| Rilascio Docker Hub | Immagini costruite e pubblicate con versione, digest e architetture dichiarate; avvio verificato usando quanto effettivamente scaricato dal registry. | +| Installazione precompilata | Su host senza toolchain e senza sorgenti applicativi, il setup scarica le immagini e completa configurazione/avvio senza compilare né invocare build locali. | +| Risorse di avvio | Compose, migrazioni e inizializzazione non dipendono da file presenti soltanto in un checkout dei sorgenti. | +| Download fallito | Errore comprensibile e ripetibile; nessuna build da sorgente avviata implicitamente. | +| Modalità sorgente | Scelta esplicita ancora funzionante, con gli stessi contratti di configurazione e persistenza del rilascio precompilato. | +| Preparazione documentale | Template e guida consentono di predisporre tutti i YAML e file protetti necessari prima dell'esecuzione. | +| Ordine dei passi | Repository, verifica workspace, precondizioni, parametri applicativi, verifica parametri, setup esecutivo. | +| Verifica workspace senza stack | Il passo 2 funziona senza Docker attivo o applicazione installata e senza toolchain host aggiuntive. | +| Controlli ripetibili | Ripetere i controlli non crea container, non migra DB e non modifica i documenti dell'utente. | +| Completezza prima del setup | Un campo obbligatorio mancante blocca l'esecuzione, indicando file/campo e correzione; nessuna domanda interattiva. | +| Input modificati | I controlli dipendenti sono invalidati o ripetuti; nessuna applicazione di input diversi da quelli verificati. | +| Setup non interattivo | Con documenti completi il passo 6 termina senza richiedere input da terminale; un errore non apre un questionario. | +| Verifiche sostanziali | Il rapporto distingue prove eseguite da controlli non ancora possibili; non certifica verità di dominio o servizi non verificati. | +| Modello configurato | Credenziali/endpoint verificati e selezione valida per gli usi richiesti. | +| Credenziale errata | Diagnosi senza esporre il segreto, correzione nel file protetto e nuova verifica prima della ripresa. | +| Interruzione e riavvio | La procedura ricontrolla gli input e riprende dall'avanzamento verificato senza nuove domande sui parametri. | +| Contenuti esistenti | Descrizioni curate ed Evidence locali preservate; nessuna generazione o sovrascrittura silenziosa. | +| Evidence assenti | Nessun blocco dovuto alla sola assenza quando non sono configurate. | +| Workspace pronto | Connessione, schema e preparazione necessaria verificati; domanda reale completata con revisione umana. | +| Riesecuzione | Nessuna duplicazione, azzeramento di dati o perdita delle personalizzazioni. | +| Stop/start | Accesso e workspace utilizzabile persistono; i problemi esterni sono segnalati separatamente. | +| Segreti | Assenti da log, riepiloghi, file pubblici e stato di avanzamento. | + +Il dettaglio dei controlli e i test automatici devono rispettare le API e i contratti +attuali; i test di portabilità e il collaudo con servizi reali restano distinti. +La prima tappa usa Windows x64/WSL2, la seconda Linux Omarchy x64, la terza macOS +Apple Silicon, coerentemente con le architetture già previste dal progetto. +Gli esiti di una tappa non valgono come collaudo delle successive. + +## Sottoprogetto degli esempi + +L'implementazione rimane rinviata, come confermato con R1. I requisiti sono conservati sul branch +`codex/benchmark-examples`, commit `08a5db55`. Il setup non offre come disponibili +CLI, database o modalità di copia del repository non ancora implementati. + +Il percorso richiesto ora parte dal repository predefinito oppure da quello ad hoc. +Il primo collaudo utilizza un repository ad hoc; il percorso predefinito completo +arriverà con il sottoprogetto esempi. Quando disponibili e selezionati nei documenti, +creazione e caricamento dei database avvengono nell'installazione +dell'utente a partire dai pacchetti dati verificati, senza compilare l'applicazione +o incorporare quei database nelle immagini. Il percorso ad hoc può essere collaudato +con workspace/database disponibili. La disponibilità di immagini Docker Hub è +invece un prerequisito esplicito di ogni collaudo del percorso precompilato. + +## Confini della specifica successiva + +La verifica del codice ha individuato questi punti di integrazione concreti: + +- `compose.yaml` costruisce oggi `core` e `frontend`; i servizi di manutenzione + usano l'immagine core locale. `setup --complete` esegue una build + (`tools/tht/internal/setup/run.go:77`) e `scripts/install-tht.sh:84` compila la CLI + tramite Docker se non riceve un artefatto già costruito. Il percorso ordinario + deve sostituire entrambi i comportamenti con artefatti del rilascio. La CI + `.github/workflows/container-multiarch.yml` verifica già entrambe le architetture + Linux; occorre aggiungere la pubblicazione Docker Hub e la verifica dei pacchetti + effettivamente distribuiti. +- I parser workspace (`backend/src/workspaces/catalog.ts:52`, `schema.ts:382`) + verificano i documenti senza dipendenze dal runtime, ma non sono oggi un comando + preinstallazione nativo. Devono essere resi disponibili negli strumenti + precompilati preservando gli stessi contratti, senza richiedere Node sul PC. +- La configurazione database è applicata al descriptor runtime dal Catalog + (`backend/src/catalog/runtime-binding.ts:33`). Serve un input locale preparatorio + e un percorso di applicazione al Catalog, senza cambiare il significato del + workspace v4 o creare due autorità persistenti per i binding. +- `config.Load` contiene verifiche riutilizzabili su YAML e file ambiente + (`tools/tht/internal/config/installation.go:100`); il `doctor` corrente combina + controlli documentali e runtime (`tools/tht/internal/doctor/report.go:97`). + La specifica deve separarli per rendere disponibili i gate dei passi 2, 3 e 5. +- L'ammissione della sessione controlla modello, preprocessing e servizi necessari + (`backend/src/routes/sessions.ts:438`); il preprocessing richiede un database + associato e una sincronizzazione corrente. Le descrizioni generate dall'AI + non costituiscono un requisito generale: possono essere già disponibili + descrizioni curate o commenti della sorgente. Riferimento: + [contratto di preprocessing](../contracts/workspace-preprocessing-cli.md). + +La specifica tecnica dovrà definire build/pubblicazione Docker Hub, pacchetto di +rilascio e bootstrap precompilato, selezione esplicita del percorso sorgente, +template e documenti locali, validatori senza runtime, applicazione dei parametri +al Catalog, esecuzione senza questionari, stato/ripresa e verifiche dei due traguardi. +Nomi dei comandi di preparazione/verifica/pubblicazione e formato dello stato sono +dettagli da progettare, non funzionalità esistenti attestate da questo PRD. + +## Chiarimenti approvati del 28 settembre + +| ID | Scelta | Decisione approvata | +| --- | --- | --- | +| R1 | Disponibilità dei tre esempi al primo rilascio | Conservare il rinvio del sottoprogetto e collaudare prima il percorso con repository ad hoc; introdurre il percorso predefinito completo quando gli esempi sono disponibili. | +| R2 | Controlli impossibili prima della creazione dello stack | Bloccare gli errori rilevabili prima; elencare i controlli runtime non ancora eseguibili e renderli obbligatori al passo 6, senza contarli come superati preventivamente. | + +Approvazione: «ok per le tue proposte. dopodichè procedi con to-spec». +Il principio dei sei passi resta invariato. + +Non rientrano in questo lavoro un nuovo installer grafico, la riscrittura delle +superfici amministrative, il supporto multi-repository, la distribuzione dei +database di esempio o modifiche al deployment server/Omics. diff --git a/docs/plans/2026-09-27-guided-installation-resumption.md b/docs/plans/2026-09-27-guided-installation-resumption.md new file mode 100644 index 00000000..769514ed --- /dev/null +++ b/docs/plans/2026-09-27-guided-installation-resumption.md @@ -0,0 +1,155 @@ +# Ripresa dell'installazione guidata + +Stato al 28 settembre: revisione documentale in sei passi richiesta dall'utente; +raccolta interattiva dei parametri durante il setup superata. R1/R2 confermate, +`grill-with-docs` e `/to-spec` conclusi, piano di test confermato dall'utente. +Il riferimento consolidato è il +[PRD dell'installazione guidata](2026-09-27-guided-installation-prd.md). +La [specifica derivata](2026-09-28-document-first-installation-spec.md) è pubblicata +su Gitea come +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42), +con etichetta `ready-for-agent`. `/to-tickets` è completato: la +[scomposizione approvata](2026-09-28-installation-ticket-breakdown.md) collega le +issue #43–#54, con dipendenze native verificate e parent invariata. +Prossimo passo: `/implement` su #43 o #46, inizialmente senza blocchi. +Nessun codice applicativo è stato implementato in questi passaggi. + +## Base di lavoro + +Branch `codex/guided-standalone-install`, commit iniziale `67ee5262`. +Il branch conserva il setup guidato e i controlli workspace incompleti, separati dal +rilascio server. Il piano del 14 settembre descrive il precedente percorso manuale; +le decisioni qui registrate aggiornano l'obiettivo del lavoro ripreso. + +## Decisioni confermate il 27 settembre 2026 + +- Destinatario: una persona capace di installare Docker, clonare un repository e + compilare i valori richiesti, senza conoscere l'architettura di ThothII. +- Risultato: almeno un workspace utilizzabile per una domanda reale, attraverso due + traguardi espliciti: piattaforma installata e workspace pronto. La procedura guida + le decisioni umane necessarie e può essere ripresa senza ricominciare. +- Collaudo in tre tappe distinte e ordinate: prima Windows, poi Linux Omarchy su PC + Intel, infine macOS. Non attribuire a una piattaforma gli esiti ottenuti su un'altra. +- Distribuzione ordinaria tramite immagini applicative precompilate pubblicate su + Docker Hub; build e pubblicazione fanno parte del progetto. L'installazione locale + configura e inizializza lo stack senza compilare; l'alternativa da sorgente resta + esplicitamente disponibile. Anche il comando host deve essere fornito precompilato + nel percorso ordinario. La creazione/caricamento degli esempi resta un'integrazione + prevista, con l'implementazione del relativo sottoprogetto ancora rinviata. + +## Vincoli già documentati + +Il precedente piano prevedeva clone Gitea e build locale; la decisione successiva +li mantiene come alternativa al percorso precompilato Docker Hub. Restano Docker +Compose e, su Windows, Ubuntu WSL2 con integrazione Docker Desktop. +Il workspace descriptor contiene identità ed +Evidence; configurazione database e metadati appartengono al Metadata Catalog. +L'Installation Model Catalog appartiene all'installazione. + +## Revisione del 28 settembre — prevale sul percorso interattivo + +La preparazione e le verifiche precedono l'esecuzione, nell'ordine richiesto: + +1. Preparare il repository workspace predefinito con i tre esempi oppure uno ad hoc. +2. Verificare formalmente e, per quanto possibile, sostanzialmente i documenti workspace. +3. Verificare le precondizioni dell'host e dei componenti previsti. +4. Predisporre i documenti locali YAML/.env con i parametri dell'applicazione. +5. Verificarne completezza, correttezza e coerenza con i workspace. +6. Eseguire il setup dai documenti verificati, scaricando le immagini Docker Hub + e creando lo stack, senza domande sui parametri. + +Template ed esempi commentati e guide IT/EN accompagnano ogni fase. Le verifiche +devono essere ripetibili prima di creare i container. Pi è incluso in `core`, non +deve essere installato sull'host; i suoi controlli runtime avvengono dopo l'avvio. +I binding database restano di competenza del Catalog, con un input locale +preparatorio distinto dai descriptor workspace v4. + +L'utente conferma che le immagini Docker Hub oggi non esistono: occorre un comando +di produzione/pubblicazione per il manutentore, poi verificare il pull degli +artefatti pubblicati prima di collaudare l'installazione precompilata sui PC. +La preparazione del rilascio non è un compito dell'utente installatore. + +R1/R2 confermate: esempi ancora rinviati e primo collaudo con repository ad hoc; +controlli runtime elencati prima ed eseguiti obbligatoriamente dopo la creazione +dello stack. Nessun controllo non eseguito conta come superato. + +## Aspetti da tradurre nella specifica tecnica + +- Quali template e documenti locali preparare, verificare e applicare, senza + raccogliere parametri durante l'esecuzione. +- Come distinguere validazione documentale, controlli preventivi esterni e controlli + runtime, mantenendo i contratti delle superfici amministrative esistenti. +- Criteri dettagliati di verifica, ripresa dopo errori e accettazione per ogni tappa. + +## Sottoprogetto esempi: requisiti definiti, implementazione rinviata + +Su richiesta dell'utente l'implementazione dei database di esempio viene rinviata; +la definizione dei requisiti dell'installazione prosegue indipendentemente. +Branch dedicato: `codex/benchmark-examples`, creato da `67ee5262`, con documentazione +consolidata nel commit `08a5db55`. Il PRD approvato è +`docs/plans/2026-09-27-example-databases-prd.md` su quel branch. + +Il PRD conserva D1–D8: Financial, European Football e F1, tre workspace separati, +dati PostgreSQL e schema commentato, Evidence curate e domande di accompagnamento +senza SQL target, contenuti italiano/inglese. CLI scaricabile dal repository pubblico +degli esempi Gitea gestito da TYL Consulting, collegato dal repository pubblico +ThothII. Selezione di uno, due o tre esempi dopo il setup, oppure come ultimo passo +facoltativo dello stesso setup; pacchetti PostgreSQL già verificati ove redistribuibili. + +La copia del repository potrà essere indipendente, senza storia e collegamenti Git +all'originale, oppure scaricata con accesso al repository pubblico in sola lettura. +Workspace ed Evidence locali restano modificabili. Preparazione e verifiche sono +responsabilità del progetto; all'utente vengono sottoposte solo ambiguità non +risolvibili dalle fonti. Chi installa dovrà trovare gli esempi pronti all'uso. + +Il caricatore, il supporto alla cartella `examples/` e alla copia autonoma sono da +implementare. Finché il sottoprogetto è rinviato, il setup non deve offrirli come +funzionalità disponibili. Il requisito di un workspace utilizzabile resta valido: +per il collaudo si dovrà usare un workspace/database realmente disponibile. + +## Verifiche da completare + +Riesecuzione e fallimenti intermedi del setup; requisiti HTTPS per il repository +workspace; test dedicati dei nuovi comandi; distinzione fra stato della piattaforma +e Workspace Readiness; collaudo reale completo secondo l'ordine concordato. + +## Riscontro sul setup corrente + +- CLI guidata e pagine amministrative esistenti sono già il percorso previsto dal + piano del 14 settembre; non è richiesto un nuovo installer grafico. +- `setup --complete` prepara autenticazione e modelli, build, migrazioni Catalog, + avvio, workspace pull, test Pi e doctor. Non configura binding DB, sincronizzazione + dei metadati e preprocessing (`tools/tht/internal/setup/run.go:60`). Il messaggio + finale corrente «ThothII is ready» deve essere allineato al traguardo verificato. +- I modelli sono oggi preimpostati, senza selettore del provider nel Request + (`tools/tht/internal/setup/files.go:449`). +- La riesecuzione rifiuta file di configurazione esistenti con contenuto diverso; + non equivale ancora a una ripresa guidata delle tappe tecniche e umane + (`tools/tht/internal/setup/files.go:107`). La specifica deve prevedere stato delle + tappe e gestione delle correzioni, senza sovrascrivere personalizzazioni. + +## Round installazione I1–I3 del 27 settembre — storico superato dove indicato + +La revisione del 28 settembre sopra prevale su I1/I2: nessun rinvio di parametri +obbligatori al setup e nessuna raccolta tramite questionario. Il testo seguente +conserva il contesto della decisione precedente e non è il comportamento richiesto +per la nuova procedura. I3 resta valido per riuso dei contenuti e curation esplicita. + +L'utente conferma «tutto come da te suggerito», dopo il chiarimento sulla CLI locale +interattiva: domande condizionate alle risposte, configurazioni precompilate, +credenziali protette, verifica delle connessioni, possibilità di rinviare una +configurazione e ripresa senza ricominciare. La CLI guida alle pagine amministrative +esistenti per il workspace e ne verifica il completamento. + +| ID | Decisione | Scelta approvata | +| --- | --- | --- | +| I1 | Primo avvio senza repository/workspace disponibile | Consentire di completare il solo traguardo «piattaforma installata» e riprendere in seguito la configurazione del workspace. Il percorso complessivo resta incompleto fino al primo workspace utilizzabile e alla domanda reale. Nessuna dipendenza dalla futura disponibilità degli esempi. | +| I2 | Scelta dei modelli durante il setup | Selezione guidata di provider e modello tra configurazioni supportate/precompilate, chiedendo le credenziali necessarie; percorso avanzato per configurazioni personalizzate. Embedding locale preconfigurato come scelta iniziale. | +| I3 | Preparazione di descrizioni ed Evidence | Riutilizzare i contenuti già curati. Proporre la generazione AI delle descrizioni mancanti come scelta esplicita, con revisione umana, invece di avviarla automaticamente. Guidare alle pagine amministrative necessarie e registrare il punto di ripresa. | + +Le Evidence sono opzionali nel contratto del workspace: non introdurre un obbligo +generale di crearle per completare l'installazione. La specifica deve rispettare +i controlli di Workspace Readiness su connessione, schema e indicizzazione, +distinguendo assenza lecita di Evidence da configurazione incompleta o incoerente. +Per le decisioni correnti e i criteri di accettazione fa fede il PRD revisionato +al 28 settembre, senza attestare che siano già implementati. diff --git a/docs/plans/2026-09-28-document-first-installation-spec.md b/docs/plans/2026-09-28-document-first-installation-spec.md new file mode 100644 index 00000000..29205210 --- /dev/null +++ b/docs/plans/2026-09-28-document-first-installation-spec.md @@ -0,0 +1,387 @@ +# Spec: installation from validated documents and Docker Hub releases + +## Problem Statement + +An operator who understands Docker and can edit a documented configuration should +not have to discover ThothII's architecture while answering an installation wizard. +The operator needs time to prepare workspace and application documents, validate +them repeatedly, and correct errors before applying changes to the machine. + +The current setup combines document generation, local builds, startup and runtime +diagnostics. Its success message can precede an actually usable workspace. It does +not provide the complete preinstallation validation and resumable, non-interactive +execution required by this workflow. + +The ordinary installation must consume published, prebuilt application images. +As reported by the project owner on 28 September 2026, ThothII images are not yet +available on Docker Hub. Publishing and verifying a real release is therefore a +prerequisite for testing the consumer installation, rather than a later enhancement. + +## Solution + +Deliver a documented six-step workflow, in this exact order: + +1. Prepare a workspace repository: a project-specific repository initially, or the + default examples repository when that separately deferred project is available. +2. Validate workspace documents formally and substantively to the extent possible + from their contents and available references. +3. Verify host and distribution prerequisites. +4. Prepare application parameters in installation-local YAML and protected + environment/secret documents, using commented templates and complete examples. +5. Validate application parameters and their consistency with the selected workspaces. +6. Execute the validated installation without asking configuration questions: pull + the release images, create and initialize the stack, apply declared configuration, + and run the required runtime checks. + +Before these consumer steps can be tested against Docker Hub, a maintainer command +must build, check and publish the release and verify that its artifacts can be pulled. +Local source builds remain an explicit alternative, with the same configuration and +persistence contracts. A failed pull never silently switches to source compilation. + +Checks that cannot run before container or database creation are enumerated as +deferred obligations and must run after startup. Errors detectable beforehand block +execution. Platform acceptance, Workspace Readiness and functional acceptance are +reported separately. A real question with human review completes functional acceptance. + +## User Stories + +1. As an installer, I want a six-step guide, so that I can understand the whole process before changing my machine. +2. As an installer, I want commented templates and completed examples, so that I can prepare documents without knowing internal component names. +3. As an installer, I want to pause document preparation, so that I can obtain missing information without restarting an installer. +4. As an installer, I want to prepare a project-specific workspace repository, so that I can use my own database before the example databases are delivered. +5. As an installer, I want the future default repository to be clearly distinguished from available features, so that I am not directed to unavailable examples. +6. As an installer, I want read-only access to the source workspace repository, so that consuming a workspace does not require publication rights. +7. As a workspace author, I want local document validation before Docker starts, so that syntax and contract errors are inexpensive to correct. +8. As a workspace author, I want duplicate identifiers, unsupported fields and inconsistent references reported, so that formally valid YAML does not hide an invalid workspace. +9. As a workspace author, I want errors to identify the document and field, so that I know precisely what to edit. +10. As a workspace author, I want optional Evidence distinguished from invalid configured Evidence, so that an intentionally absent corpus does not block installation. +11. As a workspace author, I want semantic validation limits stated honestly, so that successful validation is not mistaken for certification of domain knowledge. +12. As an installer, I want host prerequisites checked explicitly, so that Docker, architecture, permissions and resource problems are detected before setup. +13. As a Windows installer, I want a verified WSL2 and Docker Desktop path, so that I can follow one supported installation procedure. +14. As an installer, I want Pi supplied with the application image, so that I do not have to install an unnecessary host dependency. +15. As an installer, I want application models, endpoints and credentials prepared before execution, so that I can consult colleagues or provider documentation at my own pace. +16. As an installer, I want one authored model catalog, so that provider and embedding configuration do not disagree across components. +17. As an installer, I want database bindings prepared locally and separately from workspace definitions, so that environment-specific details do not leak into shared workspace repositories. +18. As an installer, I want generated internal credentials prepared in protected files, so that I need not invent technical passwords during execution. +19. As an installer, I want my secrets excluded from logs and validation reports, so that diagnostic output is safe to inspect and share. +20. As an installer, I want repeatable application validation, so that I can correct configuration without creating containers or changing databases. +21. As an installer, I want available external connections checked before startup, so that preventable endpoint or authentication errors are found early. +22. As an installer, I want non-executable checks listed explicitly, so that I know what still needs to be proved after startup. +23. As an installer, I want validation tied to the documents and release I selected, so that execution cannot silently apply different inputs. +24. As an installer, I want setup to run with closed standard input, so that it cannot unexpectedly ask me for a parameter. +25. As an installer, I want missing values to stop setup with a useful diagnosis, so that I can correct the document and validate again. +26. As an installer, I want prebuilt images downloaded from Docker Hub, so that I need no application source checkout or compiler. +27. As an installer, I want the operator tool supplied precompiled, so that its bootstrap does not hide a local build. +28. As an installer, I want release components to be compatible and identifiable, so that my installation is reproducible. +29. As an installer, I want registry failures reported without automatic compilation, so that the selected installation mode remains predictable. +30. As an installer, I want configuration, credentials and data kept outside application images, so that my installation remains local and persistent. +31. As an installer, I want migrations and database binding initialization handled explicitly, so that a running container is not mistaken for an initialized application. +32. As an installer, I want existing curated descriptions and Evidence preserved, so that rerunning setup cannot overwrite human work. +33. As an installer, I want runtime checks performed from the actual container environment, so that host connectivity is not confused with application connectivity. +34. As an installer, I want completed and failed execution stages recorded, so that I can resume after interruption without duplicating data. +35. As an installer, I want configuration corrections to invalidate dependent checks, so that resuming does not trust stale results. +36. As an administrator, I want Catalog changes to retain their existing confirmation and concurrency rules, so that installation automation does not bypass domain safeguards. +37. As an installer, I want separate platform and workspace status, so that I know whether I can already ask a real question. +38. As an installer, I want stop/start behavior checked, so that a successful first run is not the only working state. +39. As a maintainer, I want an explicit release build-and-publish command, so that consumer installation can use actual Docker Hub artifacts. +40. As a maintainer, I want versioned images and release metadata, so that published artifacts can be traced to a source revision. +41. As a maintainer, I want publication credentials isolated from consumer configuration, so that installers need no write access to Docker Hub. +42. As a maintainer, I want the published artifacts tested by pulling them, so that an unpublished local image cannot satisfy release acceptance. +43. As a source-build user, I want an explicit supported build path, so that source customization remains possible. +44. As a maintainer, I want Windows, Omarchy and macOS acceptance recorded separately and in order, so that support claims reflect tests actually performed. +45. As an Italian or English reader, I want matching step-by-step guides, so that language choice does not change the installation contract. + +## Implementation Decisions + +### Boundaries and authority + +- Keep the existing standalone architecture and operator CLI. The application runs + through Compose; the operator CLI owns preparation, validation and execution of + the installation, rather than introducing another installer or backend workflow. +- Preserve Workspace schema v4 and the workspace catalog as the authority for + workspace identity and optional Evidence. Formal validation uses the same rules + as runtime loading, including strict YAML interpretation, duplicate detection, + directory/index consistency and prohibited fields. +- Preserve the Installation Model Catalog as the sole authored source for provider, + model eligibility, defaults and embedding facts. Session and embedding configuration + must be complete before ordinary execution. Metadata generation remains optional + when its catalog configuration is omitted consistently. +- Preserve the PostgreSQL Metadata Catalog as the authority for Workspace Database + identity, schema, metadata and active Database Binding. Respect the existing + one-database-per-workspace boundary. Prepared binding documents are bootstrap + inputs, not a second runtime database catalog. +- Preserve installation-local secret storage, reference-based binding credentials, + editable Evidence authority, read-only DWH access and the separation of reference + preprocessing from Memory. Do not rewrite model projections or runtime snapshots + as independent authored configuration. + +### Preparation documents and command surfaces + +- Expose distinct operator operations for template preparation, workspace validation, + host checks, application validation, execution, status and resumption. Their exact + CLI spelling can be finalized with the implementation tickets; they must remain + separately invocable and scriptable. Setup execution is never a parameter wizard. +- Template preparation creates explicitly requested sample documents or protected + credential files without starting application services. It never silently replaces + existing user documents. Placeholders are visibly incomplete and cannot pass the + required-field checks. +- Supply a versioned, installation-local database bootstrap document describing the + workspace identity, engine, physical database/schema, transport and endpoint + configuration, and references to secrets. Validate against existing Catalog + capabilities and selected workspace identities. No credentials belong in the + shared workspace descriptors or public repository. +- Binding imports use authenticated, authorized Catalog services and their version + checks. On first execution they create the declared bindings and install secrets + through the existing store. On rerun, equivalent values are a no-op; conflicting + existing administrative changes stop with a reconciliation report. The bootstrap + input does not continuously overwrite a mutable Catalog. +- Initial local administrative authentication and its protected bootstrap material + are prepared before execution. Execution cannot depend on a person answering a + login wizard. Reuse supported operator authentication boundaries and required + permissions, without introducing a privileged unauthenticated installation API. +- Supply a precompiled validation capability with the operator distribution. The + workspace validation step must work without Docker, ThothII, Node or an application + source checkout. Reuse canonical validation rules; if a new packaging boundary is + necessary, prove equivalence with a shared set of valid and invalid documents. + +### Validation contract + +- Workspace validation covers syntax, strict schema, identity, catalog/descriptor + relationships and locally available Evidence references. It does not claim to + establish the truth of domain rules, database contents or services that do not exist. +- Host checks distinguish host prerequisites from bundled application dependencies. + Pi is checked as a release component and subsequently in the running core image, + never required as a separate host installation. +- Application validation checks required configuration, compatible release and host, + model usages/defaults, protected secret references, database transport capabilities, + workspace links, effective Compose configuration and accessible remote dependencies. + A transport that cannot serve NL-to-SQL sessions cannot pass workspace-readiness + validation merely because it can perform administrative diagnostics. +- Reports expose a stable outcome, check identifier, affected logical input/field, + explanation and next action. The public outcomes are passed, error, warning and + deferred-to-runtime. Human-readable output is accompanied by pristine structured + output for automation; exit status distinguishes success from blocking failure. +- Validation is read-only with respect to user documents, application state and + target databases. Explicit report output is allowed. Remote checks are bounded, + documented and non-mutating; any provider usage incurred by a configured smoke + test is disclosed before invocation, not requested interactively during setup. +- Missing or invalid required configuration, missing release artifacts, unsupported + architecture and failures of available required dependencies block execution. + Unreachable existing external services are errors, not automatically reclassified + as deferred. Only checks intrinsically dependent on the not-yet-created local + stack qualify for the accepted deferred category. +- Maintain an explicit obligation list for deferred checks: container-network + connectivity, Catalog initialization, Pi operation, local embedding availability, + preprocessing and relevant workspace runtime readiness. Each obligation has a + defined runtime check; there is no successful final state while a required + obligation remains unverified or failed. +- Bind the execution plan to normalized non-secret configuration, repository revision + and verified content, selected release digests and validator version. Re-read + protected credentials when checking or executing; do not expose their values or + unkeyed secret-derived fingerprints in reports. Re-run credential checks where + freshness cannot be established safely. +- Revalidate changed dependencies and live prerequisites at execution or resume. + A previously successful report is not blanket authorization to apply changed files + or evidence of current network availability. + +### Release production and distribution + +- Provide a maintainer command accepting source revision, release version, Docker Hub + namespace and target architectures. It performs preflight checks, reproducible + builds, artifact checks, publication and output of a coherent release manifest. + This command is a deliverable of this feature, not an undocumented manual prerequisite. +- Publish the existing core and frontend application images. Catalog migration and + workspace maintenance use the same released core image. Keep PostgreSQL, Qdrant + and Ollama as compatible upstream images; preserve the existing service boundaries. +- Package the operator executable, validation capability, Compose definitions, + initialization resources and migration support required by the release. No runtime + mount may require a resource that exists only in an application source checkout. +- Pin the release identity and resolved image digests. A published release manifest + binds compatible images and operator/configuration versions. Do not overwrite an + already published immutable release version or declare a partially published + image set installable. An interrupted publication can retry without advertising + an incomplete consumer release. +- Keep publishing credentials outside consumer bundles and logs. Public consumers + pull without publishing rights. Application images contain application software, + not installation secrets, user workspace data or prepopulated example databases. +- The ordinary bootstrap downloads a precompiled operator and release artifacts. + It must not compile the CLI through Docker as a hidden fallback. Windows WSL2 + uses the Linux executable; macOS uses an appropriate host executable. +- Deliver and accept Linux amd64 images for Windows/WSL2 and Omarchy first. Add and + accept Linux arm64 for the macOS Apple Silicon stage. Multiarchitecture build + results do not by themselves prove host installation acceptance. +- A maintainer smoke test pulls the published artifacts by their release references. + Consumer acceptance runs must not succeed because of an unpushed locally built + image. Confirm core, frontend and maintenance references all resolve to the release. +- Retain explicit source mode with the same configuration, validation and persistence + rules. It is not the default and is never an automatic recovery action for a pull + failure. Registry recovery is a retry of the selected released artifacts. + +### Non-interactive execution and recovery + +- Execute only a complete, currently validated plan with no blocking errors. The + ordinary sequence pulls the release, prepares runtime projections and isolated + installation storage, starts required services, applies migrations, registers the + workspace source and imports the prepared Catalog bindings before dependent work. +- Apply configuration and migrations through existing service boundaries. Hold an + installation execution lock to prevent concurrent runs from racing over the same + state, containers or bootstrap imports. +- Reuse durable Catalog Sync Runs and their freshness/locking rules. Fresh additive + synchronization can proceed under the existing contract. A destructive diff or + another domain-required human decision stops at an explicit awaiting-review state; + the operator reviews through the existing administration surface and subsequently + resumes. This is a domain decision, not permission to collect missing setup parameters + or add an automatic confirmation bypass. +- Reuse existing curated metadata and Evidence. Do not generate AI descriptions or + new domain rules as an installation side effect. Optional generation remains a + separate explicit administrative action. Required preprocessing can use supported + source comments or curated descriptions without mandatory AI generation. +- Persist a bounded execution journal with installation identity, plan identity, + stage outcomes, released component versions, deferred-check outcomes and recovery + guidance. Do not persist secret values or raw exception output. Write progress + atomically and check actual state on resume. +- A repeated execution of the same completed plan must not recreate bindings, + duplicate data, clear Memory, overwrite Evidence or erase sessions. Reconcile + already completed stages with their actual persistent state before proceeding. +- After a document correction, revalidate and repeat only affected checks/stages. + Do not infer that an interrupted migration or import failed before inspecting its + durable result. Preprocessing, which has no internal resume contract, may need to + rerun as a whole; report that honestly. +- Never implement recovery by deleting all volumes or reverting user data. Report + the failing stage and safe next action. Failed or interrupted execution is not + advertised as a complete installation. +- Report platform state, Workspace Readiness and functional acceptance separately. + Runtime checks exercise the released Pi, embedding and actual container transport. + Final acceptance includes a real human-reviewed question and stop/start persistence. + The workflow's human review is not replaced by unattended benchmark evaluation. + +### Documentation and delivery boundaries + +- Keep Italian and English guides aligned with the six steps. Each step states its + inputs, documents, examples, verification operation, expected output and common + corrections. Provide an advance checklist of information and credentials to collect. +- Separate maintainer publication instructions, consumer prebuilt installation and + source-build instructions. State precisely which components are host prerequisites + and which are shipped inside the release. +- First acceptance uses an available project-specific repository and database. + The Financial, European Football and F1 project stays deferred. Do not expose its + unavailable loader, repository-copy support or data bundles as usable features. +- Preserve the future integration boundary: examples will be selected in documents + and loaded locally after application initialization, without rebuilding the + application or embedding the datasets into its Docker images. + +## Testing Decisions + +### Confirmed test boundaries + +Use the public operator workflow as the principal test boundary: prepared documents +in, stable reports/exit statuses and observable installation outcomes out. Exercise +validation and execution through this boundary while replacing external command +execution and remote services with controllable test counterparts. Keep focused +contract tests at existing workspace parsing and Catalog boundaries where they +prevent divergent schemas or authority rules. Add real release and host acceptance +tests for behavior that simulated external services cannot establish. + +The project owner confirmed this testing boundary on 28 September 2026, completing +the `/to-spec` checkpoint. The product decisions, six-step workflow and testing +scope are approved for specification publication and subsequent ticket decomposition. + +### Existing testing practice to extend + +- Operator setup tests already use temporary installation fixtures and a replaceable + command runner to cover sequencing, startup failures, error preservation and recovery + messages. Extend that boundary to document validation, prebuilt execution and resumption. +- Installation configuration tests cover strict schemas, model catalog rules and + incompatible existing files. Extend them with complete/incomplete preparation + documents, protected references and cross-document consistency. +- Workspace tests cover strict catalog/descriptor parsing, immutable Git revisions, + runtime handoff and secret handling. Reuse their document fixtures and validity + rules to demonstrate equivalence of the preinstallation validator. +- Catalog and preprocessing tests already cover durable runs, locks, binding freshness, + failure recording and preservation of authoritative state. Reuse these boundaries + to verify bootstrap import and resumption without bypassing the domain contracts. +- Existing multiarchitecture image checks provide a starting point for released + artifact verification. They do not replace pulling the published artifacts or + testing supported host environments. + +### Required behavioral coverage + +1. Validate workspace documents with Docker absent and no application runtime; + reject malformed YAML, duplicate keys/identifiers, unsupported fields, broken + references and invalid configured Evidence with actionable locations. +2. Repeated document/host/application checks do not create containers, change source + documents, import data or migrate databases. Only explicitly requested reports + may be written by validation. +3. Complete application documents pass; placeholders, missing required models, + invalid binding transport and unreadable secret references block execution. +4. Existing external-service failures remain errors. Checks genuinely dependent on + newly created local services are listed as deferred and cannot disappear from + final acceptance. +5. Execute with standard input closed. Valid inputs need no responses; missing + values yield an error without waiting for input or prompting for a replacement. +6. Changing documents, workspace revision or release after validation invalidates + dependent results. Credential changes are caught without leaking secret material. +7. A clean prebuilt consumer installation performs pulls and initialization, never + application or operator compilation; absent images fail without a source fallback. +8. Published manifests resolve all required images, platform variants and maintenance + components coherently. Simulated publication interruption does not advertise a + partial release; a real smoke test exercises artifacts pulled from Docker Hub. +9. Prepared database bindings become Catalog state once, use the existing protected + secret store and remain unchanged on equivalent reruns. Administrative divergence + is reported rather than silently overwritten. +10. Interruption after a durable operation but before journal completion resumes by + inspecting state, without duplicating that operation. Concurrent setup runs cannot + mutate the same installation simultaneously. +11. Destructive Catalog synchronization requires its existing review and fresh source + checks. The setup's non-interactive nature does not auto-approve a destructive diff. +12. Existing descriptions, Evidence, sessions and Memory survive validation, rerun, + configuration correction and stop/start. Optional AI generation is not triggered. +13. Runtime Pi, embedding and connectivity checks are executed from the installed + release, and platform success cannot mask failed Workspace Readiness. +14. Source mode remains functional and explicit with equivalent configuration + contracts. A source-mode pass cannot close prebuilt distribution acceptance. +15. Execute real consumer acceptance first on Windows x64/WSL2, then Omarchy x64, + then macOS Apple Silicon. Record each environment, release identity, stage + outcomes, a real reviewed question and a stop/start check separately. + +Good tests assert observable contracts, preserved data and required side effects, +not private helper calls or incidental internal ordering. Use real isolated Catalog +instances where transaction and lock behavior matters. Mock external provider +failures for repeatable tests, while keeping actual DWH/model acceptance separate. + +## Out of Scope + +- Implementing or publishing the three example databases, their curated contents, + example CLI, auxiliary repository layout or autonomous repository-copy mode. +- A new graphical installer, a conversational parameter wizard, or collecting + required parameters in administration pages after an incomplete setup. +- Installing Pi, Python, Node or an application build toolchain on ordinary consumer hosts. +- Changes to server/Omics deployment, upstream authentication or the application's + existing human-in-the-loop workflow. +- Multiple workspace repositories per installation or multiple databases per workspace. +- Automatic release upgrades, destructive reset/uninstall, whole-volume rollback + or backup-policy redesign. Interrupted initial setup recovery remains in scope. +- Automatic semantic certification, generated Evidence without sources, benchmark + SQL targets or automated accuracy scoring. +- Publishing Docker images or executing installations as part of this specification + authoring task. These are implementation and release deliverables described above. + +## Further Notes + +The project owner approved the six-step revision and both final clarifications on +28 September 2026: examples stay deferred, and runtime-only checks are explicit +post-start obligations. Earlier interactive-wizard and incomplete-configuration +installation proposals are superseded where they conflict with this specification. + +This specification follows the existing decisions on PostgreSQL metadata authority, +installation-local bindings, secret references, durable schema synchronization and +the Installation Model Catalog. It does not change those architectural authorities. + +Publication of a usable Docker Hub release is a blocking dependency of consumer +prebuilt-installation acceptance. Availability of the deferred examples is not. +Docker Hub namespace, publishing credentials and concrete release versions are +maintainer release inputs, not values to invent or embed into user templates. + +After publication to Gitea, `/to-tickets` will split this specification into small +end-to-end increments with explicit blockers. Implementation has not started in +this task, and publishing the specification does not attest that a release exists. diff --git a/docs/plans/2026-09-28-installation-ticket-breakdown.md b/docs/plans/2026-09-28-installation-ticket-breakdown.md new file mode 100644 index 00000000..c20fc25b --- /dev/null +++ b/docs/plans/2026-09-28-installation-ticket-breakdown.md @@ -0,0 +1,394 @@ +# Scomposizione della specifica di installazione + +Data: 2026-09-28. Stato: scomposizione approvata dall'utente («approvo»); +pubblicazione `/to-tickets` completata. Verificati testi, etichetta `ready-for-agent` +e dipendenze native delle issue #43–#54; specifica parent invariata. +Parent: [Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +Gli identificatori T01–T12 restano riferimenti della scomposizione; le issue reali +sono elencate sotto. Ogni issue usa `ready-for-agent` e dipendenze native Gitea. +La parent non viene modificata né chiusa. + +## Issue pubblicate + +Avanzamento locale, 2026-09-28: T01/#43 implementato sul branch +`codex/guided-standalone-install`. Disponibili `tht workspace prepare` e +`tht workspace validate`, con helper autonomo che riusa i parser runtime. Guide +IT/EN aggiornate; esempi e pubblicazione degli artefatti restano differiti ai ticket +previsti. Nessuna chiusura o modifica della parent effettuata. + +Verifica: 14 test della CLI passano sia da sorgenti sia con il bundle nativo macOS +arm64 e `PATH` vuoto; artefatti Windows amd64/Linux amd64 cross-compilati, senza +attribuire loro un collaudo host. Backend su Node 24.16: 109 file passati, un file +saltato, 1.417 test passati e 40 saltati; typecheck e build rigorosa documentazione +passati. Suite Go: tutti i pacchetti passati, salvo un primo errore intermittente +nel test di concorrenza authstorage; quel test passa in tre ripetizioni e il pacchetto +completo passa nella verifica isolata. Le revisioni Standards e Spec non lasciano +finding aperti. Il prossimo incremento sequenziale è T02/#44. + +| Ticket | Issue Gitea | Dipendenze dirette | +| --- | --- | --- | +| T01 | [Preparare e validare un repository workspace senza stack](https://git.tylconsulting.it/mptyl/ThothII/issues/43) | Nessuna | +| T02 | [Preparare e validare i documenti applicativi](https://git.tylconsulting.it/mptyl/ThothII/issues/44) | #43 | +| T03 | [Verificare precondizioni e produrre il piano eseguibile](https://git.tylconsulting.it/mptyl/ThothII/issues/45) | #44 | +| T04 | [Produrre e pubblicare un rilascio Docker Hub installabile](https://git.tylconsulting.it/mptyl/ThothII/issues/46) | Nessuna | +| T05 | [Installare la piattaforma dal rilascio senza domande](https://git.tylconsulting.it/mptyl/ThothII/issues/47) | #45, #46 | +| T06 | [Applicare i binding preparati al Catalog](https://git.tylconsulting.it/mptyl/ThothII/issues/48) | #47 | +| T07 | [Portare il workspace alla readiness con controlli runtime](https://git.tylconsulting.it/mptyl/ThothII/issues/49) | #48 | +| T08 | [Conservare il percorso esplicito da sorgente](https://git.tylconsulting.it/mptyl/ThothII/issues/50) | #47 | +| T09 | [Riprendere dopo correzioni e interruzioni senza perdere stato](https://git.tylconsulting.it/mptyl/ThothII/issues/51) | #49, #50 | +| T10 | [Collaudare l'installazione pubblicata su Windows/WSL2](https://git.tylconsulting.it/mptyl/ThothII/issues/52) | #51 | +| T11 | [Collaudare l'installazione su Omarchy](https://git.tylconsulting.it/mptyl/ThothII/issues/53) | #52 | +| T12 | [Pubblicare e collaudare il percorso macOS Apple Silicon](https://git.tylconsulting.it/mptyl/ThothII/issues/54) | #53 | + +Ogni ticket comprende verifiche del comportamento e aggiornamenti pertinenti delle +guide IT/EN. Le dipendenze elencate sono dirette; non si ripetono quelle transitive. +Si riusano il runner dell'operatore e i servizi di dominio esistenti; gli adattamenti +necessari sono inclusi nella prima funzionalità che li usa. Non emerge una necessità +di refactoring trasversale da pubblicare come lavoro orizzontale separato. + +| Ticket | Titolo | Bloccato da | Risultato dimostrabile | +| --- | --- | --- | --- | +| T01 | Preparare e validare un repository workspace senza stack | Nessuno | Template e controllo locale conformi ai contratti, senza Docker attivo. | +| T02 | Preparare e validare i documenti applicativi | T01 | Parametri, modelli e binding completi verificati senza avviare servizi. | +| T03 | Verificare precondizioni e produrre il piano eseguibile | T02 | Rapporto con errori bloccanti e obblighi runtime, legato agli input. | +| T04 | Produrre e pubblicare un rilascio Docker Hub installabile | Nessuno | Comando manutentore e pacchetto pubblico verificato tramite pull. | +| T05 | Installare la piattaforma dal rilascio senza domande | T03, T04 | Pull, inizializzazione e avvio da documenti, con stato e ripresa delle fasi. | +| T06 | Applicare i binding preparati al Catalog | T05 | Database dei workspace configurati senza questionari o duplicazioni. | +| T07 | Portare il workspace alla readiness con controlli runtime | T06 | Schema, preprocessing e servizi verificati senza aggirare la revisione umana. | +| T08 | Conservare il percorso esplicito da sorgente | T05 | Stessi input e contratti, con build scelta esplicitamente. | +| T09 | Riprendere dopo correzioni e interruzioni senza perdere stato | T07, T08 | Recupero dell'intero percorso, compresi binding, sync e preprocessing. | +| T10 | Collaudare l'installazione pubblicata su Windows/WSL2 | T09 | Prima accettazione reale senza sorgenti o compilatori. | +| T11 | Collaudare l'installazione su Omarchy | T10 | Seconda accettazione reale su Linux x64, distinta da Windows. | +| T12 | Pubblicare e collaudare il percorso macOS Apple Silicon | T11 | Terza accettazione con immagini arm64 e comando host compatibile. | + +## T01 — Preparare e validare un repository workspace senza stack + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Un autore prepara un repository ad hoc usando un template documentato e verifica +i documenti localmente prima di installare ThothII. Il validatore è fornito come +capacità eseguibile senza Node, Docker attivo o checkout dei sorgenti applicativi. +Riusa i contratti del runtime, senza creare uno schema workspace alternativo. + +### Acceptance criteria + +- [ ] Il template distingue catalogo workspace, descriptor ed Evidence opzionali e non contiene funzionalità degli esempi ancora indisponibili. +- [ ] Preparare un template non avvia servizi, non sovrascrive documenti esistenti e non richiede accesso in scrittura al repository originale. +- [ ] Il controllo respinge YAML ambiguo o invalido, chiavi duplicate, identificatori duplicati, campi estranei, incoerenze catalogo/directory e riferimenti locali mancanti. +- [ ] Le Evidence configurate sono verificate per ciò che è controllabile localmente; assenza lecita e invalidità sono distinte, senza certificare il significato delle regole di dominio. +- [ ] Gli esiti identificano documento/campo e correzione; output strutturato e codici di uscita sono verificabili senza esporre segreti. +- [ ] Una raccolta condivisa di casi validi/invalidi prova equivalenza con i parser runtime, e il controllo passa senza Docker e senza runtime host aggiuntivi. +- [ ] Guide IT/EN mostrano preparazione, correzione e ripetizione del passo 2. + +### Blocked by + +None (can start immediately). + +## T02 — Preparare e validare i documenti applicativi + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +L'operatore compila template locali per installazione, modelli, binding database e +segreti, e ne verifica completezza e coerenza con i workspace già verificati. +Non viene avviata l'applicazione e nessun valore viene richiesto dal futuro setup. + +### Acceptance criteria + +- [ ] Template commentati ed esempi completi spiegano obblighi, default e riferimenti ai documenti protetti; i placeholder non superano la validazione. +- [ ] Modelli e embedding rispettano l'Installation Model Catalog; generazione metadati opzionale e default sono coerenti con i contratti esistenti. +- [ ] Un input bootstrap locale versionato descrive Workspace Database e Database Binding con riferimenti ai segreti; i descriptor workspace restano conformi allo schema v4. +- [ ] Validazione incrociata di workspace, binding, engine/trasporto, modelli, percorsi e file ambiente, senza migrare o interrogare in scrittura alcun database. +- [ ] La generazione esplicita delle credenziali tecniche produce file protetti prima del setup, senza sovrascritture o segreti nei log/rapporti. +- [ ] I test coprono input completi, mancanti, incompatibili e segreti illeggibili; i documenti dell'utente rimangono invariati durante le verifiche. +- [ ] Guide IT/EN consentono di raccogliere e preparare tutte le informazioni con calma prima dell'esecuzione. + +### Blocked by + +- T01 — Preparare e validare un repository workspace senza stack. + +## T03 — Verificare precondizioni e produrre il piano eseguibile + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +L'operatore verifica host e dipendenze esterne disponibili, quindi ottiene un piano +eseguibile riferito ai documenti e al rilascio scelti. Il rapporto distingue errori, +avvisi e controlli necessariamente rinviati al runtime, senza creare lo stack. + +### Acceptance criteria + +- [ ] I controlli host sono invocabili al passo 3; quelli dipendenti dai parametri finali sono completati o ripetuti al passo 5. +- [ ] Sono verificati Docker/Compose, architettura, WSL2 quando pertinente, percorsi/permessi, risorse e disponibilità del rilascio e dei suoi componenti nel registry. +- [ ] Le prove sulle dipendenze esterne disponibili sono circoscritte e documentate; un servizio esistente irraggiungibile non viene promosso a semplice controllo differito. +- [ ] Pi non è richiesto sull'host; le dipendenze incluse nelle immagini sono riconosciute nel manifest e associate a controlli runtime precisi. +- [ ] Il piano registra input non segreti, revisione/contenuti workspace, release e versione del validatore; nessun segreto o fingerprint pubblico non protetto di segreti. +- [ ] Ogni controllo differito ha un'identità e un'obbligazione runtime; valori obbligatori mancanti o immagini assenti bloccano il piano. +- [ ] Prove con manifest e servizi controllati coprono cambiamento degli input, credenziali, errori di rete e architetture; nessuna creazione di container o mutazione di dati. +- [ ] Guide IT/EN spiegano rapporto, errori e verifiche ancora da eseguire. La prova con il rilascio reale verrà completata dal ticket di esecuzione, dopo T04. + +### Blocked by + +- T02 — Preparare e validare i documenti applicativi. + +## T04 — Produrre e pubblicare un rilascio Docker Hub installabile + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Il manutentore esegue un comando riproducibile che costruisce, verifica e pubblica +core/frontend su Docker Hub, insieme al pacchetto operatore compatibile, e dimostra +che il rilascio pubblicato è scaricabile. La pubblicazione delle immagini oggi +mancanti è un risultato concreto del ticket, non un prerequisito lasciato a mano. + +### Acceptance criteria + +- [ ] Il comando riceve revisione, versione, namespace e architetture e mantiene fuori da bundle/log le credenziali di pubblicazione. +- [ ] Pubblica core/frontend Linux amd64 per la prima tappa; Catalog migration e workspace maintenance risolvono alla stessa immagine core del rilascio. +- [ ] Il bundle contiene comando host precompilato, Compose, inizializzazione e risorse di migrazione, senza dipendenze da checkout sorgente durante l'avvio. +- [ ] Il processo può includere la capacità di validazione preinstallazione prodotta da T01 nelle revisioni che la contengono; non serve duplicarne l'implementazione per questo ticket. +- [ ] Il manifest lega versione/revisione e digest compatibili; una pubblicazione parziale non viene esposta come rilascio completo e una versione immutabile non viene sovrascritta. +- [ ] Viene pubblicato un rilascio reale e viene verificato il pull degli artefatti pubblicati, senza affidarsi a immagini presenti soltanto nella cache di build. +- [ ] Test automatici verificano orchestrazione, fallimenti e retry senza richiedere una pubblicazione reale a ogni test; la prova reale del ticket resta distinta e registrata. +- [ ] Documentazione manutentore IT/EN e istruzioni del bundle distinguono pubblicazione, consumo e futura estensione arm64. Namespace e accessi effettivi sono input del manutentore, non valori inventati. + +### Blocked by + +None (can start immediately). + +## T05 — Installare la piattaforma dal rilascio senza domande + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +L'operatore applica un piano verificato, scarica gli artefatti pubblicati e ottiene +una piattaforma inizializzata e accessibile senza compilazione o domande. Lo stato +registrato permette di ritentare le fasi di piattaforma interrotte; non viene ancora +dichiarato pronto un workspace privo delle successive verifiche Catalog. + +### Acceptance criteria + +- [ ] Avvio da bundle rilasciato e operatore precompilato, senza checkout applicativo o toolchain; la revisione del bundle include i validatori e i comandi effettivamente utilizzati. +- [ ] Il piano viene ricontrollato rispetto a input, release e prerequisiti vivi prima delle mutazioni; un piano mancante o incoerente viene rifiutato. +- [ ] Con standard input chiuso il setup esegue pull, configurazione runtime, reti/volumi/container, inizializzazione Catalog/Memory e migrazioni senza richiedere parametri. +- [ ] L'accesso amministrativo iniziale deriva da materiale protetto preparato prima; non si introduce un endpoint privilegiato senza autenticazione. +- [ ] Un lock impedisce esecuzioni concorrenti; il journal atomico registra le fasi senza segreti e consente di verificare lo stato reale prima di ripetere una fase interrotta. +- [ ] Errori di pull non causano build locali; errori di configurazione rimandano ai documenti e alla nuova verifica, senza prompt di riparazione. +- [ ] La piattaforma accessibile è distinta dalla Workspace Readiness ancora da verificare; nessun messaggio finale prematuro di piena utilizzabilità. +- [ ] Test del runner e prova con artefatti pubblicati coprono successo, stdin chiuso, interruzioni e ripetizione senza cancellare volumi o dati. Guide IT/EN documentano il risultato parziale corretto. + +### Blocked by + +- T03 — Verificare precondizioni e produrre il piano eseguibile. +- T04 — Produrre e pubblicare un rilascio Docker Hub installabile. + +## T06 — Applicare i binding preparati al Catalog + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Il setup rende operativa la configurazione database predisposta nei documenti: +registra il repository, crea i Workspace Database e le Database Binding nel Catalog, +installa i riferimenti segreti e verifica le connessioni. Il risultato è un binding +utilizzabile senza una compilazione manuale dei parametri nell'interfaccia web. + +### Acceptance criteria + +- [ ] Identità e revisioni dei workspace corrispondono al piano; il consumo del repository non richiede push e non sostituisce implicitamente una sorgente esistente. +- [ ] Creazione e modifica dei binding utilizzano servizi autorizzati, controlli di versione e secret store esistenti; nessuna seconda autorità runtime nei documenti bootstrap. +- [ ] La stessa configurazione applicata due volte non duplica record, credenziali o binding. +- [ ] Una modifica amministrativa incompatibile produce un rapporto di riconciliazione invece di essere sovrascritta dai file preparatori. +- [ ] La connessione viene controllata dall'ambiente applicativo; un esito positivo ottenuto dall'host non basta a dichiararla utilizzabile dai container. +- [ ] Un'interruzione dopo il salvataggio ma prima dell'aggiornamento del journal viene riconosciuta alla ripresa, senza duplicazioni o perdita di segreti. +- [ ] Test di contratto e integrazione Catalog coprono autorizzazioni, concorrenza, versioni e rerun; guide IT/EN illustrano diagnosi e riconciliazione. + +### Blocked by + +- T05 — Installare la piattaforma dal rilascio senza domande. + +## T07 — Portare il workspace alla readiness con controlli runtime + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Da un binding applicato, il percorso completa sincronizzazione dello schema e +preparazione necessaria e rende visibili i risultati dei controlli runtime. +Un workspace è pronto solo quando tutti i requisiti applicabili sono verificati; +le decisioni umane già previste dai contratti rimangono esplicite. + +### Acceptance criteria + +- [ ] La sincronizzazione usa i Catalog Sync Runs durabili con lock, freschezza e transazioni esistenti; non introduce una seconda implementazione. +- [ ] Diff distruttive fermano il percorso in attesa della revisione di dominio esistente; ripresa successiva senza auto-conferme né domande sui parametri di setup. +- [ ] Preprocessing e consolidamento riusano descrizioni/commenti ed Evidence curate; nessuna generazione AI implicita, nessuna cancellazione di Memory o sovrascrittura di curation. +- [ ] Assenza lecita di Evidence non blocca; Evidence configurate ma invalide e indici necessari non pronti restano blocchi reali. +- [ ] Pi, modello embedding, trasporto DWH e altri obblighi differiti sono eseguiti a runtime e rendicontati; nessun obbligo scompare o viene considerato superato senza prova. +- [ ] Stato piattaforma, Workspace Readiness e collaudo funzionale sono distinti; una domanda reale con revisione rimane la prova funzionale, senza SQL target. +- [ ] Test di servizio e integrazione dimostrano esiti, conservazione dati e ripresa dei run; guide IT/EN spiegano le eventuali revisioni umane residue. + +### Blocked by + +- T06 — Applicare i binding preparati al Catalog. + +## T08 — Conservare il percorso esplicito da sorgente + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Un operatore sceglie esplicitamente la build da una revisione sorgente e usa gli +stessi documenti, validatori, identità d'installazione e servizi del percorso +precompilato. L'alternativa resta praticabile mentre il default diventa Docker Hub. + +### Acceptance criteria + +- [ ] Modalità sorgente e prerequisiti aggiuntivi sono espliciti; nessun errore del registry la attiva automaticamente. +- [ ] I componenti costruiti sono equivalenti nei contratti di configurazione, migrazione e persistenza; non esistono implementazioni parallele dei binding o della readiness. +- [ ] Il piano identifica modalità e revisione e invalida i controlli dipendenti quando cambiano. +- [ ] L'esecuzione rimane non interattiva e usa journal/lock comuni; i segreti non entrano nelle immagini di sviluppo. +- [ ] Una prova automatizzata dimostra build e avvio espliciti e il mancato fallback da pull; una prova sorgente non chiude l'accettazione del rilascio precompilato. +- [ ] Guide IT/EN separano il percorso avanzato da quello ordinario e rendono visibili i prerequisiti aggiuntivi. + +### Blocked by + +- T05 — Installare la piattaforma dal rilascio senza domande. + +## T09 — Riprendere dopo correzioni e interruzioni senza perdere stato + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +L'operatore corregge un endpoint, una credenziale o un altro documento dopo un errore +e riprende l'intero percorso con verifiche aggiornate. Questo ticket completa il +recupero fra stadi e modalità, oltre ai retry locali già consegnati dai singoli ticket. + +### Acceptance criteria + +- [ ] Cambiamenti ai documenti, ai contenuti/revisioni workspace o al rilascio invalidano le sole verifiche/fasi dipendenti; le altre vengono riconciliate con lo stato reale. +- [ ] La rotazione di una credenziale viene rilevata senza esporla o pubblicarne fingerprint non protetti; si ripetono le prove necessarie. +- [ ] Ripresa dopo interruzione nei confini fra pull, inizializzazione, importazione Catalog, sync e preprocessing non duplica operazioni già persistite. +- [ ] Il preprocessing interrotto è rieseguito secondo il contratto esistente, senza promettere resume interno; un run in attesa di decisione umana conserva tale stato. +- [ ] Interruzioni, errori e concorrenza non corrompono il journal né attivano reset di volumi; diagnosi e stato rimangono privi di segreti. +- [ ] Test del percorso pubblico, con guasti controllati e integrazione dove conta la persistenza, dimostrano conservazione di sessioni, Evidence, descrizioni e Memory in entrambe le modalità. +- [ ] Le guide IT/EN presentano scenari di correzione/ripresa senza suggerire la cancellazione dei dati come normale rimedio. + +### Blocked by + +- T07 — Portare il workspace alla readiness con controlli runtime. +- T08 — Conservare il percorso esplicito da sorgente. + +## T10 — Collaudare l'installazione pubblicata su Windows/WSL2 + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Dimostrare il percorso completo su un PC Windows x64 con Ubuntu WSL2 e Docker +Desktop usando il rilascio realmente pubblicato, repository ad hoc e documenti +predisposti. Il collaudo include le correzioni necessarie a rendere utilizzabile +la prima piattaforma e un rapporto riproducibile. + +### Acceptance criteria + +- [ ] Un rilascio della revisione integrata viene pubblicato tramite T04 e consumato tramite pull; le immagini costruite soltanto localmente non soddisfano la prova. +- [ ] Il consumer non dispone di sorgenti applicativi o toolchain necessarie a compilare; operatore e validatori sono quelli precompilati nel bundle. +- [ ] Tutti i sei passi sono percorsi nell'ordine documentato, con almeno una correzione documentale e una ripresa dopo errore, senza domande durante il setup. +- [ ] Primo workspace ad hoc realmente utilizzabile, una domanda con revisione umana e stop/start con stato preservato; nessun uso presunto degli esempi rinviati. +- [ ] Rapporto con host/runtime, revisione, digest e risultati distinti di piattaforma/workspace/funzione, senza segreti; problemi esterni non sono nascosti. +- [ ] Guide IT/EN sono verificate rispetto ai comandi e agli esiti reali; eventuale assenza di host o credenziali necessarie lascia il collaudo incompleto, non simulato come riuscito. + +### Blocked by + +- T09 — Riprendere dopo correzioni e interruzioni senza perdere stato. + +## T11 — Collaudare l'installazione su Omarchy + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Dopo la tappa Windows, ripetere e rendere funzionante il percorso su Linux Omarchy +x64, producendo un'evidenza di accettazione propria e mantenendo il comportamento +documentale e non interattivo già consegnato. + +### Acceptance criteria + +- [ ] Rilascio pubblico compatibile scaricato da Docker Hub e comando host precompilato; nessuna compilazione nel percorso ordinario. +- [ ] Prerequisiti, permessi, percorsi e rete di Omarchy sono verificati su un host reale, senza trasferire automaticamente l'esito Windows. +- [ ] Sei passi, input invalido/corretto, ripresa, workspace ad hoc, domanda reale e stop/start superano il collaudo. +- [ ] Ogni correzione di portabilità include la relativa verifica e non introduce una divergenza dei contratti rispetto al percorso Windows. +- [ ] Rapporto separato con versioni/digest e guide IT/EN coerenti; senza un host disponibile il gate rimane aperto. + +### Blocked by + +- T10 — Collaudare l'installazione pubblicata su Windows/WSL2. + +## T12 — Pubblicare e collaudare il percorso macOS Apple Silicon + +### Parent + +[Spec: installazione da documenti verificati e distribuzione Docker Hub](https://git.tylconsulting.it/mptyl/ThothII/issues/42). + +### What to build + +Dopo Omarchy, pubblicare e verificare il set di artefatti compatibile con macOS +Apple Silicon, incluse immagini Linux arm64 e comando nativo, e chiudere la terza +tappa di accettazione su un Mac reale. + +### Acceptance criteria + +- [ ] Il comando di rilascio pubblica immagini arm64 e bundle host compatibile, con manifest/digest coerenti e senza dichiarare supporto prima del collaudo. +- [ ] Il consumer usa il rilascio pubblico e non compila; gli script e le risorse di inizializzazione sono presenti nel bundle. +- [ ] Tutti i sei passi, correzione/ripresa, workspace ad hoc, domanda reale e stop/start sono verificati sul Mac. +- [ ] Le eventuali correzioni conservano compatibilità e contratti delle tappe precedenti; le prove multiarch di build non sostituiscono il collaudo host. +- [ ] Rapporto macOS separato e guide IT/EN finalizzate per le tre piattaforme; nessun risultato sintetico viene presentato come prova reale. + +### Blocked by + +- T11 — Collaudare l'installazione su Omarchy. + +## Verifiche della scomposizione + +- I primi ticket lavorabili sono T01 e T04. +- T03 usa manifest e servizi controllati per i propri contratti; non aspetta la + pubblicazione reale. T05 è il primo punto che richiede insieme validazione e + artefatti realmente pubblicati. +- T06 e T08 possono procedere in parallelo dopo T05. T09 riunisce i percorsi per + verificare correzioni e ripresa dell'intera installazione. +- I tre gate host sono sequenziali per scelta esplicita dell'utente, non per una + dipendenza architetturale inventata. +- Gli esempi restano esclusi. Il comando di pubblicazione Docker Hub e almeno una + pubblicazione reale sono inclusi, non demandati a un futuro progetto. +- Nessun aggiornamento o chiusura della parent è previsto dalla pubblicazione. diff --git a/tools/tht/README.md b/tools/tht/README.md index eb3f0fca..14fecacf 100644 --- a/tools/tht/README.md +++ b/tools/tht/README.md @@ -1,5 +1,46 @@ # Native installation CLI +## Offline workspace documents (issue #43) + +`tht workspace prepare --directory NEW_PATH --id ID --name NAME [--language en|it]` +creates an ad hoc workspace repository template. `tht workspace validate --directory +PATH [--json]` checks the working documents without an installation descriptor or +services. Both commands invoke the packaged sibling `tht-workspace-documents`, which +compiles the canonical backend YAML/Zod parsers with its runtime. Neither Node nor +Bun is required on the user's computer. Validation never writes documents or Git +configuration. See the [IT](../../docs/install/standalone-manual-it.md) and +[EN](../../docs/install/standalone-manual-en.md) guides for the correction loop and +explicit runtime checks that local validation cannot satisfy. + +Maintainer build (Go from `go.mod`, Node/npm for build only): + +```sh +cd backend +npm ci +npm run build:workspace-tools +# Cross-compile the complete platform pairs: +npm run build:workspace-tools -- --all +``` + +The output is `dist/workspace-tools/-/` containing `tht[.exe]`, +`tht-workspace-documents[.exe]`, `SHA256SUMS` and `build.json`. Individual targets: +`windows-amd64`, `linux-amd64`, `linux-arm64`, `darwin-amd64`, `darwin-arm64`. +Bun is pinned in `backend/package-lock.json`; compilation embeds the runtime, and +cross-compilation may download the selected Bun target. Deliver the complete pair +from one build, verify checksums and keep the executables together. The previous +`build-tht.sh` / `install-tht.sh` single-binary path remains for existing operator +commands; it does not package this helper. Release publication is tracked separately +in issue #46; building a Windows/Linux artifact does not establish acceptance there. + +Run the same public CLI fixture corpus against the native pair, with subprocess +`PATH` deliberately empty: + +```sh +cd backend +THT_WORKSPACE_TEST_CLI=/absolute/path/dist/workspace-tools/darwin-arm64/tht \ + npx vitest run test/workspace-documents-cli.test.ts +``` + ## Shell configuration The schema-v2 `thothii-installation.yaml` accepts this optional section: diff --git a/tools/tht/cmd/tht/main.go b/tools/tht/cmd/tht/main.go index a1db7ba9..ade2d40a 100644 --- a/tools/tht/cmd/tht/main.go +++ b/tools/tht/cmd/tht/main.go @@ -85,6 +85,10 @@ Commands: pi maintenance recover --yes Verify a terminal installation, remove stale lifecycle files, and clear maintenance. pi logs Show the latest 200 sanitized core log lines (bounded; no follow mode). + workspace prepare --directory NEW_PATH --id ID --name NAME [--language en|it] [--json] + Prepare workspace documents locally, before installation. + workspace validate --directory PATH [--json] + Validate local workspace documents without starting services. workspace inspect --workspace ID [--json] workspace pull [--json] Pull and activate the configured workspace repository. @@ -141,6 +145,9 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) int { } return commandUsageError(stderr, fmt.Sprintf("unknown command %q", command)) } + if command == "workspace" && len(commandArgs) > 0 && (commandArgs[0] == "prepare" || commandArgs[0] == "validate") { + return workspaceDocumentsCommand(ctx, commandArgs, stdout, stderr) + } workingDirectory, err := os.Getwd() if err != nil { fmt.Fprintf(stderr, "tht: current directory is unavailable: %s\n", output.Sanitize(err.Error(), nil)) diff --git a/tools/tht/cmd/tht/workspace_documents.go b/tools/tht/cmd/tht/workspace_documents.go new file mode 100644 index 00000000..df62b474 --- /dev/null +++ b/tools/tht/cmd/tht/workspace_documents.go @@ -0,0 +1,49 @@ +package main + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "os/exec" + "path/filepath" + "runtime" + "slices" +) + +// Resolve only the packaged sibling, never an executable from the workspace or PATH. +func workspaceDocumentsCommand(ctx context.Context, args []string, stdout, stderr io.Writer) int { + executable, err := os.Executable() + if err == nil { + executable, err = filepath.EvalSymlinks(executable) + } + if err == nil { + name := "tht-workspace-documents" + if runtime.GOOS == "windows" { + name += ".exe" + } + command := exec.CommandContext(ctx, filepath.Join(filepath.Dir(executable), name), args...) + command.Stdout, command.Stderr = stdout, stderr + err = command.Run() + if err == nil { + return 0 + } + var exitError *exec.ExitError + if errors.As(err, &exitError) && exitError.ExitCode() >= 0 { + return exitError.ExitCode() + } + } + const correction = "Install tht and tht-workspace-documents from the same platform bundle in the same directory, then retry." + if slices.Contains(args, "--json") { + _ = json.NewEncoder(stdout).Encode(map[string]any{ + "schema_version": 1, "scope": "local-documents", "ok": false, + "workspaces": []any{}, "deferred_checks": []string{}, + "issues": []map[string]string{{"document": "CLI", "field": "$", "code": "workspace_helper_unavailable", "correction": correction}}, + }) + } else { + fmt.Fprintln(stderr, correction) + } + return 1 +} diff --git a/tools/tht/cmd/tht/workspace_documents_test.go b/tools/tht/cmd/tht/workspace_documents_test.go new file mode 100644 index 00000000..fcf44d82 --- /dev/null +++ b/tools/tht/cmd/tht/workspace_documents_test.go @@ -0,0 +1,29 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "testing" +) + +func TestWorkspaceDocumentsNeedsPackagedHelperNotInstallation(t *testing.T) { + t.Setenv("PATH", "") + t.Setenv("THOTHII_INSTALLATION", "/nonexistent/installation.yaml") + for _, action := range []string{"prepare", "validate"} { + var stdout, stderr bytes.Buffer + status := run(context.Background(), []string{"workspace", action, "--directory", t.TempDir(), "--json"}, &stdout, &stderr) + var report struct { + OK bool `json:"ok"` + Issues []struct { + Code string `json:"code"` + } `json:"issues"` + } + if err := json.Unmarshal(stdout.Bytes(), &report); err != nil { + t.Fatalf("missing structured helper error: status=%d stdout=%s stderr=%s", status, &stdout, &stderr) + } + if status != 1 || report.OK || len(report.Issues) != 1 || report.Issues[0].Code != "workspace_helper_unavailable" || stderr.Len() != 0 { + t.Fatalf("unexpected missing-helper result: status=%d stdout=%s stderr=%s", status, &stdout, &stderr) + } + } +}