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.
This commit is contained in:
Codex
2026-09-28 15:35:25 +02:00
parent 67ee52624c
commit 64e6b9664a
20 changed files with 2195 additions and 4 deletions
+11
View File
@@ -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.
+8 -1
View File
@@ -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
+206
View File
@@ -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",
+2
View File
@@ -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"
+47
View File
@@ -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);
}
+6
View File
@@ -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;
+3 -3
View File
@@ -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);
}
}
+222
View File
@@ -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<T>(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/<id>. 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<string, string>): 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 <id>/workspace.yaml together. Evidence is optional and absent by default. Add reviewed source material under <id>/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 <id>/workspace.yaml. Le Evidence sono facoltative e inizialmente assenti. Inserire materiale verificato in <id>/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 <repository>`. 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<string, string>();
const seen = new Set<string>();
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 };
}
@@ -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);
});
+57
View File
@@ -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:
+58
View File
@@ -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:
@@ -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
@@ -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.
@@ -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.
@@ -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.
@@ -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.
+41
View File
@@ -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/<os>-<arch>/` 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:
+7
View File
@@ -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))
+49
View File
@@ -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
}
@@ -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)
}
}
}