Phase 1 / Chantier A — Failover robuste (symlinks + startup validation) #1

Open
olivier wants to merge 0 commits from feat/phase1-A-failover-robust into main
Owner

Résumé

Phase 1 — Chantier A (failover robuste). Encode en code la convention des 3 symlinks partagés par compte (session-env, file-history, projects) et ajoute une validation au startup pour refuser de démarrer si un lien est absent ou divergent.

Commits

  • 91091d7 feat(symlinks): add shared-state symlink manager (A1)
  • e16e352 feat(lifecycle): validate shared symlinks at daemon startup (A2)

Changements

  • Nouveau package internal/symlinks : EnsureForAccount, ValidateAll, RequiredShared
  • Manager.ValidateSharedSymlinks() appelé avant EnsureAllSessions au boot
  • Fail-fast sur divergence (pas d'auto-heal silencieux)

Tests

  • go test ./... — tous packages pass
  • 9/9 tests symlinks (création, idempotence, divergence, home vide, etc.)

Plan test manuel

  • Déploiement sur VM neuve : le daemon refuse de démarrer sans les liens
  • Un lien divergent → erreur explicite, pas de destruction silencieuse
  • Liens existants corrects → boot normal

Voir VERSION.md 0.3.6 pour le changelog détaillé.

## Résumé Phase 1 — Chantier A (failover robuste). Encode en code la convention des 3 symlinks partagés par compte (`session-env`, `file-history`, `projects`) et ajoute une validation au startup pour refuser de démarrer si un lien est absent ou divergent. ## Commits - `91091d7` feat(symlinks): add shared-state symlink manager (A1) - `e16e352` feat(lifecycle): validate shared symlinks at daemon startup (A2) ## Changements - Nouveau package `internal/symlinks` : `EnsureForAccount`, `ValidateAll`, `RequiredShared` - `Manager.ValidateSharedSymlinks()` appelé avant `EnsureAllSessions` au boot - Fail-fast sur divergence (pas d'auto-heal silencieux) ## Tests - ✅ `go test ./...` — tous packages pass - ✅ 9/9 tests symlinks (création, idempotence, divergence, home vide, etc.) ## Plan test manuel - [ ] Déploiement sur VM neuve : le daemon refuse de démarrer sans les liens - [ ] Un lien divergent → erreur explicite, pas de destruction silencieuse - [ ] Liens existants corrects → boot normal Voir `VERSION.md` 0.3.6 pour le changelog détaillé.
olivier added 2 commits 2026-04-16 19:28:16 +00:00
Adds internal/symlinks package that encodes in code the convention
previously maintained by hand on the VM: every Claude account home
must expose `session-env`, `file-history` and `projects` as symlinks
to a single shared target, so account failover does not create
divergent state (duplicate JSONL transcripts, broken undo history).

- EnsureForAccount(home, required) creates missing links and target
  directories, refuses to auto-correct a divergent link (risks data
  loss), and errors when a regular file sits where the link belongs.
- ValidateAll(homes, required) aggregates errors across both accounts
  so the operator sees every problem at once rather than fixing one
  per restart cycle.
- RequiredShared exposes the production defaults so lifecycle and
  switcher (A2/A3) can depend on it directly.

9/9 unit tests green.

Part of Phase 1 Chantier A — Failover robuste.
Wire symlinks.ValidateAll into the lifecycle manager so the daemon
refuses to start if any configured account is missing one of the
shared-state symlinks or if a link diverges from the canonical target.

Previously, a missing link on a freshly deployed VM would silently
create a divergent state tree per account (duplicate JSONL transcripts,
broken undo history) — exactly the failure mode the symlinks package
(A1) was introduced to prevent.

The check runs once at startup before EnsureAllSessions, guarding a
single well-defined invariant: "every account home shares the same
projects/, file-history/ and session-env/ roots". No auto-heal on
divergence — we fail fast with an explicit error so the operator fixes
it manually rather than one account's state being overwritten.

Part of Phase 1 Chantier A — Failover robuste.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This branch is already included in the target branch. There is nothing to merge.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/phase1-A-failover-robust:feat/phase1-A-failover-robust
git checkout feat/phase1-A-failover-robust

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git checkout main
git merge --no-ff feat/phase1-A-failover-robust
git checkout feat/phase1-A-failover-robust
git rebase main
git checkout main
git merge --ff-only feat/phase1-A-failover-robust
git checkout feat/phase1-A-failover-robust
git rebase main
git checkout main
git merge --no-ff feat/phase1-A-failover-robust
git checkout main
git merge --squash feat/phase1-A-failover-robust
git checkout main
git merge --ff-only feat/phase1-A-failover-robust
git checkout main
git merge feat/phase1-A-failover-robust
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: olivier/claude-failover#1
No description provided.