clusev/docs/superpowers/handoffs/2026-06-20-clusev-cli-relea...

7.4 KiB

Session Handoff — Clusev v0.9.25 (clusev host CLI + short commands + help tab-URL)

Date: 2026-06-20 Branch: feat/v1-foundation Status: Code released (tagged + pushed). Production deploy is PENDING — user-side.


TL;DR for the next session

A feature shipping a host CLI (clusev), short operator commands, a help "Commands / CLI" topic, and help-tab URL syncing is fully implemented, tested, Codex-reviewed, locally browser-verified, tagged v0.9.25, and pushed to Gitea. The only remaining step is the production deploy (which I cannot do from this host — no SSH to the prod VM) and the post-deploy domain R12. Pick up there.


What shipped (v0.9.25)

Five user requests, all delivered:

  1. Help tabs in the URL#[Url] on App\Livewire\Help\Index::$topic. Clicking a topic syncs ?topic=<key>; reload/bookmark/share keep the active tab. Default overview is omitted. (Settings tabs already had #[Url].)
  2. Short host commands — new clusev wrapper installed by install.sh to /usr/local/bin. Subcommands: update, reset-admin, restart, logs, ps/status, migrate, artisan, version, help. Wraps the long docker compose -f <prod compose> … invocations. Host command uses a space (clusev reset-admin); the underlying artisan name stays clusev:reset-admin.
  3. All operator-facing surfaces switched to short commands + help explains the real command — new bilingual help topic "Befehle / CLI" (commands) documents every command with purpose, when-to-use, and the real underlying command (shown WITHOUT the long -f <compose> filename). Switched: versions update panel, help recovery/updates/domain-tls, settings/system reset hints, sentinel-write-failed lang strings, MOTD.
  4. Long compose filename hidden, not renamed — user chose "wrapper hides it" (renaming collides with the existing dev docker-compose.yml and risks orphaning a live stack). A regression test (tests/Feature/CommandShortcutsTest.php) forbids docker-compose.prod.yml on any display surface; the new CHANGELOG entry was even worded to avoid the literal (the /versions page renders CHANGELOG.md — see gotcha below).
  5. Detailed help descriptions — done in the commands topic + reworded recovery/updates/versions.

Spec / plan / commits

  • Spec: docs/superpowers/specs/2026-06-19-clusev-cli-and-command-shortcuts-design.md
  • Plan: docs/superpowers/plans/2026-06-19-clusev-cli-and-command-shortcuts.md
  • Commits on feat/v1-foundation (newest first):
    • b619b32 chore: release 0.9.25 (config version bump + CHANGELOG) ← tag v0.9.25
    • 5967b7b fix: shell-escape the install dir when generating the clusev CLI (R15 Codex)
    • 8d2b708 feat: short clusev commands in versions panel, lang strings, MOTD (+ test-string fixups)
    • 437a225 feat: help tab-URL + Commands/CLI topic + short command copy
    • 580c72a feat: clusev host CLI wrapper for short stack commands
  • Branch + tag pushed to git.bave.dev/boban/clusev.git. Working tree clean.

Key files

  • New: docker/clusev/clusev (wrapper template, __CLUSEV_DIR__ placeholder, unquoted assignment — install.sh injects a printf %q shell-escaped path; do NOT re-quote it).
  • New: tests/scripts/test-clusev-cli.sh (host bash test, 8 checks incl. injection-resistance).
  • New: resources/views/livewire/help/content/{de,en}/commands.blade.php.
  • New: tests/Feature/CommandShortcutsTest.php (leak guard + short-command assertions).
  • Changed: install.sh (phase 9 renders+installs the CLI), app/Livewire/Help/Index.php, lang/{de,en}/{help,versions,settings,system}.php, help partials (recovery, updates, domain-tls), resources/views/livewire/versions/index.blade.php, docker/motd/00-clusev, tests/Feature/{HelpPageTest,ForgotPasswordSmtpAwareTest}.php, config/clusev.php, CHANGELOG.md.

Verification done

  • php artisan test (whole suite): 236 passed.
  • bash tests/scripts/test-clusev-cli.sh: green (incl. a real injection-resistance check — confirmed the old quoted-rendering was exploitable, the new one is not).
  • npm run build: OK.
  • Codex R15: PASS. It found ONE security issue — unescaped $(pwd) rendered into the generated CLI, a root-code-injection vector via a hostile checkout path. Fixed (printf %q + unquoted assignment) and re-reviewed PASS, including the embedded-newline edge case.
  • Local R12 (headless Chrome / puppeteer Docker against the dev stack on http://localhost): /help 200, URL syncs to ?topic=commands on click, deep-link reload keeps the topic, commands content renders, 0 console/page/network errors; /versions 200, shows sudo clusev update, no docker-compose.prod.yml. (Used gkonrad id=4, 2FA-off; temp password set + restored to the original hash afterwards.)

NEXT STEP (start here)

1. Deploy v0.9.25 to production (user runs this on the prod VM)

From this host I have no SSH access to the prod VM (10.10.90.165 — publickey/password denied) and there is no local prod stack, so I cannot deploy. The operator runs, on the prod VM:

cd clusev && sudo ./update.sh

(This is the release that first ships the clusev wrapper, so use ./update.sh this once; the update runs install.sh which installs /usr/local/bin/clusev, so from the NEXT release sudo clusev update works. Alternatively click "Jetzt aktualisieren" in the panel.)

After it finishes, which clusev on the VM should resolve.

2. Domain R12 (I CAN do this over HTTP after the deploy)

The public domain panel.test.bave.dev → external proxy 10.10.20.20 (Zoraxy) → prod VM. Only HTTP is needed (no SSH), via the puppeteer Docker image. Only meaningful AFTER the deploy (the domain serves the old image until then). Verify on the domain:

  • /help 200, click "Befehle / CLI" → URL ?topic=commands, reload keeps it, DE/EN switch, no console errors.
  • /versions shows sudo clusev update.
  • On the VM shell (read-only): clusev help, clusev ps, clusev version. Do NOT run reset-admin/update against the real admin.

Constraints & gotchas (carry forward)

  • Respond in German in chat (code/commits/docs stay English). Caveman-terse mode was active.
  • Gitea token: read ONLY at push time from /home/nexxo/.env.gitea (GIT_ACCESS_TOKEN), never echo/commit; sanitize push output sed -E 's#https://[^@]*@#https://<redacted>@#g; s/[0-9a-f]{40,}/<redacted>/g'.
  • De-Claude history: commits intentionally carry NO Co-Authored-By: Claude trailer (the project wants Claude-free history; history rewrite + force-push only at beta with explicit OK).
  • Dev DB: never Server::create to inspect; use RefreshDatabase tests / Blade::render.
  • View-cache flake: run php artisan view:clear before browser-verifying a .blade.php change; for artisan test flakiness isolate with VIEW_COMPILED_PATH=/tmp/vc-xxx.
  • /versions renders CHANGELOG.md — whole-page leak scans flag historical changelog prose (e.g. the v0.4.1 entry literally contains @php); scope leak checks to UI chrome, not changelog.
  • Memory index updated: see clusev-prod-deploy-user-side, clusev-versions-page-renders-changelog.

Not done / possible follow-ups

  • Production deploy + domain R12 (above) — the only open item for THIS feature.
  • (Backlog) de-Claude history rewrite, deferred to beta.
  • (Optional) bash completion for clusev; was intentionally left out (YAGNI).