Files
ThothII/docs/superpowers/plans/2026-08-20-workspace-install-docs-fixture-self-containment.md
T

12 KiB

Workspace Install Docs Fixture Self-Containment Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Make the workspace-install documentation verifier pass from a clean environment without inheriting TMPDIR or THT_AUTH_CONFIG_ROOT.

Architecture: Keep the existing Bash verifier and test runner structure. The test runner removes both ambient variables at the production-verifier boundary; the verifier supplies its own safe temporary-root fallback and creates one empty authentication-config directory for each Compose-rendering fixture, then proves the rendered core mount has the exact source, target, and read-only mode.

Tech Stack: Bash (set -euo pipefail), Docker Compose configuration rendering through scripts/compose-with-preflight.sh, inline Node.js JSON assertions, Git.

Global Constraints

  • Modify only scripts/verify-workspace-install-docs.sh and scripts/test-verify-workspace-install-docs.sh during implementation.
  • Do not modify Compose, authentication runtime code, documentation examples, the PSD server, Docker resources, or the legacy stack.
  • Every production-verifier mktemp call must use /tmp when TMPDIR is absent and must continue to honor an explicitly provided TMPDIR.
  • Do not mutate or export the caller's environment.
  • Each of the local installation, server installation, and canonical Compose fixtures must own a distinct empty authentication-config directory inside its fixture root.
  • Each generated fixture environment must set THT_AUTH_CONFIG_ROOT explicitly.
  • Each Compose fixture must assert the exact authentication mount source, target /run/thothii-auth, and read_only: true on the rendered core service.
  • Temporary cleanup must remain bounded to directories returned by mktemp.
  • No command in this plan starts, stops, reloads, builds, or modifies the PSD or legacy server stack.

Task 1: Make the verifier fixtures self-contained

Files:

  • Modify: scripts/test-verify-workspace-install-docs.sh:11
  • Modify: scripts/verify-workspace-install-docs.sh:917,1066,1865-1946,1948-2050,2133-2215
  • Test: scripts/test-verify-workspace-install-docs.sh

Interfaces:

  • Consumes: compose.yaml requires host variable THT_AUTH_CONFIG_ROOT and renders it as the core bind mount target /run/thothii-auth with read-only mode.

  • Produces: scripts/verify-workspace-install-docs.sh --fixtures-only succeeds with TMPDIR and THT_AUTH_CONFIG_ROOT absent; every generated Compose fixture supplies and verifies its own authentication mount source.

  • Step 1: Add the clean-environment regression boundary

Change only the production-verifier invocation near the beginning of scripts/test-verify-workspace-install-docs.sh:

-"$root/scripts/verify-workspace-install-docs.sh" --fixtures-only >"$output"
+env -u TMPDIR -u THT_AUTH_CONFIG_ROOT \
+  "$root/scripts/verify-workspace-install-docs.sh" --fixtures-only >"$output"
  • Step 2: Run the regression test and retain the first RED result

Run:

env -u TMPDIR -u THT_AUTH_CONFIG_ROOT \
  bash scripts/test-verify-workspace-install-docs.sh

Expected: non-zero exit; stderr contains TMPDIR: unbound variable. This proves the test reaches the production verifier with no ambient temporary root.

  • Step 3: Add the production-verifier temporary-root fallback

Replace the five unsafe mktemp calls in scripts/verify-workspace-install-docs.sh; leave the calls that already use ${TMPDIR:-/tmp} unchanged:

-  update_fixture="$(mktemp -d "${TMPDIR%/}/thoth-source-update.XXXXXX")"
+  update_fixture="$(mktemp -d "${TMPDIR:-/tmp}/thoth-source-update.XXXXXX")"

-  repair_root="$(mktemp -d "${TMPDIR%/}/thoth-crlf-repair.XXXXXX")"
+  repair_root="$(mktemp -d "${TMPDIR:-/tmp}/thoth-crlf-repair.XXXXXX")"

-  fixture="$(mktemp -d "${TMPDIR%/}/thoth local install.XXXXXX")"
+  fixture="$(mktemp -d "${TMPDIR:-/tmp}/thoth local install.XXXXXX")"

-  fixture="$(mktemp -d "${TMPDIR%/}/thoth server install.XXXXXX")"
+  fixture="$(mktemp -d "${TMPDIR:-/tmp}/thoth server install.XXXXXX")"

-  fixture="$(mktemp -d "${TMPDIR%/}/thoth-install-fixtures.XXXXXX")"
+  fixture="$(mktemp -d "${TMPDIR:-/tmp}/thoth-install-fixtures.XXXXXX")"
  • Step 4: Run the test and retain the second RED result

Run:

env -u TMPDIR -u THT_AUTH_CONFIG_ROOT \
  bash scripts/test-verify-workspace-install-docs.sh

Expected: non-zero exit after the earlier documentation fixtures progress; output contains THT_AUTH_CONFIG_ROOT. This isolates the remaining incomplete Compose-fixture input.

  • Step 5: Make the local installation fixture own and verify its auth root

Apply these exact changes inside verify_local_installation_example:

-  local fixture source_copy operator_dir copied_example env_file
+  local fixture source_copy operator_dir copied_example env_file auth_config_root
   fixture="$(mktemp -d "${TMPDIR:-/tmp}/thoth local install.XXXXXX")"
   trap 'rm -rf "$fixture"' RETURN
@@
   source_copy="$fixture/ThothII source"
   operator_dir="$fixture/operator files"
-  mkdir -p "$source_copy/deploy/pi" "$operator_dir"
+  auth_config_root="$operator_dir/auth config"
+  mkdir -p "$source_copy/deploy/pi" "$operator_dir" "$auth_config_root"
@@
     "THT_WORKSPACE_GIT_SSH_KEY_FILE=$operator_dir/git-ssh-key" \
     "THT_WORKSPACE_GIT_KNOWN_HOSTS_FILE=$operator_dir/git-known-hosts" \
+    "THT_AUTH_CONFIG_ROOT=$auth_config_root" \
     >"$env_file"
@@
-  node - "$rendered" <<'NODE'
+  node - "$rendered" "$auth_config_root" <<'NODE'
 const fs = require("fs");
-const config = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
+const [path, authConfigRoot] = process.argv.slice(2);
+const config = JSON.parse(fs.readFileSync(path, "utf8"));
 if (Object.keys(config.services).sort().join(",") !== "core,embedding,embedding-model-init,frontend,qdrant") {
   throw new Error("local installation example must render the internal semantic stack");
 }
+const authMount = (config.services.core.volumes || []).find(
+  (mount) => mount.target === "/run/thothii-auth",
+);
+if (!authMount || authMount.source !== authConfigRoot || authMount.read_only !== true) {
+  throw new Error("local installation example must mount its fixture auth root read-only");
+}
 const output = JSON.stringify(config);
  • Step 6: Make the server installation fixture own and verify its auth root

Apply these exact changes inside verify_server_installation_example:

-  local fixture source_copy operator_dir copied_example env_file backup_root
+  local fixture source_copy operator_dir copied_example env_file backup_root auth_config_root
   fixture="$(mktemp -d "${TMPDIR:-/tmp}/thoth server install.XXXXXX")"
   trap 'rm -rf "$fixture"' RETURN
@@
   operator_dir="$fixture/server operator files"
   backup_root="$fixture/server backups"
+  auth_config_root="$operator_dir/auth config"
   mkdir -p "$source_copy/deploy/pi" "$source_copy/deploy/workspaces" \
-    "$operator_dir/data/workspace-secrets" "$operator_dir/pi-state" "$operator_dir/workspace-registry" "$backup_root"
+    "$operator_dir/data/workspace-secrets" "$operator_dir/pi-state" "$operator_dir/workspace-registry" \
+    "$auth_config_root" "$backup_root"
@@
     "THT_WORKSPACE_REGISTRY_ROOT=$operator_dir/workspace-registry" \
     "THT_BACKUP_ROOT=$backup_root" \
+    "THT_AUTH_CONFIG_ROOT=$auth_config_root" \
     "THT_SERVER_WORKSPACE_CONFIG=$source_copy/deploy/workspaces/server-sessions.yaml.example" \
@@
-  node - "$rendered" <<'NODE'
+  node - "$rendered" "$auth_config_root" <<'NODE'
 const fs = require("fs");
-const config = JSON.parse(fs.readFileSync(process.argv[2], "utf8"));
+const [path, authConfigRoot] = process.argv.slice(2);
+const config = JSON.parse(fs.readFileSync(path, "utf8"));
 if (Object.keys(config.services).sort().join(",") !== "core,embedding,embedding-model-init,frontend,qdrant") {
   throw new Error("server installation example must render the internal semantic stack");
 }
 const core = config.services.core;
 const frontend = config.services.frontend;
+const authMount = (core.volumes || []).find((mount) => mount.target === "/run/thothii-auth");
+if (!authMount || authMount.source !== authConfigRoot || authMount.read_only !== true) {
+  throw new Error("server installation example must mount its fixture auth root read-only");
+}
  • Step 7: Make the canonical local/server fixture own and verify its auth root

Apply these exact changes inside verify_compose_fixtures:

-  local fixture profile rendered
+  local fixture profile rendered auth_config_root
   fixture="$(mktemp -d "${TMPDIR:-/tmp}/thoth-install-fixtures.XXXXXX")"
   trap 'rm -rf "$fixture"' RETURN
-  mkdir -p "$fixture/data/workspace-secrets" "$fixture/pi-state" "$fixture/workspace-registry"
+  auth_config_root="$fixture/auth-config"
+  mkdir -p "$fixture/data/workspace-secrets" "$fixture/pi-state" \
+    "$fixture/workspace-registry" "$auth_config_root"
@@
     "THT_PI_STATE_ROOT=$fixture/pi-state" \
     "THT_WORKSPACE_REGISTRY_ROOT=$fixture/workspace-registry" \
+    "THT_AUTH_CONFIG_ROOT=$auth_config_root" \
     "THT_SERVER_WORKSPACE_CONFIG=$fixture/server-sessions.yaml" \
@@
-    node - "$rendered" "$profile" <<'NODE'
+    node - "$rendered" "$profile" "$auth_config_root" <<'NODE'
 const fs = require("fs");
-const [path, profile] = process.argv.slice(2);
+const [path, profile, authConfigRoot] = process.argv.slice(2);
 const config = JSON.parse(fs.readFileSync(path, "utf8"));
@@
 const core = config.services.core;
+const authMount = (core.volumes || []).find((mount) => mount.target === "/run/thothii-auth");
+if (!authMount || authMount.source !== authConfigRoot || authMount.read_only !== true) {
+  throw new Error(profile + ": core must mount its fixture auth root read-only");
+}
 for (const target of [
  • Step 8: Run the focused regression test to GREEN

Run:

env -u TMPDIR -u THT_AUTH_CONFIG_ROOT \
  bash scripts/test-verify-workspace-install-docs.sh

Expected: exit 0 with all existing positive and negative fixture checks accepted; no TMPDIR or THT_AUTH_CONFIG_ROOT error.

  • Step 9: Run direct acceptance and syntax checks

Run:

env -u TMPDIR -u THT_AUTH_CONFIG_ROOT \
  bash scripts/verify-workspace-install-docs.sh --fixtures-only
bash -n scripts/verify-workspace-install-docs.sh
bash -n scripts/test-verify-workspace-install-docs.sh
bash scripts/auth-docs-smoke.sh

Expected: both verifier commands and both syntax checks exit 0; auth-docs-smoke.sh prints its PASS summary.

  • Step 10: Verify scope, whitespace, and absence of unsafe expansions

Run:

git diff --check
git status --short
rg -n '\$\{TMPDIR%/\}' scripts/verify-workspace-install-docs.sh
git diff -- scripts/verify-workspace-install-docs.sh scripts/test-verify-workspace-install-docs.sh

Expected: git diff --check exits 0; git status --short lists only the two implementation scripts; the rg command exits 1 with no matches; the diff contains only the clean-environment regression call, five temporary-root fallbacks, three fixture-owned auth roots/environment entries, and three rendered-mount assertions.

  • Step 11: Commit the verified fix
git add scripts/verify-workspace-install-docs.sh scripts/test-verify-workspace-install-docs.sh
git commit -m "test: make install docs fixtures self-contained"

Expected: one commit containing exactly the two implementation files. Do not push, merge, or change the detached-HEAD state.