Adds optional delegation of agent-queue tasks to the SecuAAS secutools AI platform (GPU / Gemini / Claude API) instead of dispatching to a local Claude Code tmux session. Per-task opt-in via YAML frontmatter fields preferred_ai, allow_delegation, complexity_hint — absence keeps the Phase 1 behaviour exactly (zero breaking change). Go side: - internal/secutools: HTTP client with exponential-backoff retries (SubmitJob/GetJob/WaitForResult), DecideProvider map adapter for CLI use, table tests. - internal/router: struct-typed Decide() with strict precedence (needs_claude_code > preferred_ai=claude-code > allow_delegation=false > preferred_ai > fail-safe local on unknown). - internal/delegation: Manager submits jobs, writes .md.delegated markers for on-restart recovery, runs a periodic reaper that moves completed jobs into done/ with provider/cost footer and failed jobs into failed/. - internal/dispatcher: WithDelegation() opt-in, routeTask hook before findFreeSession, skips .md.delegated in assignNextTask. - internal/api: /api/delegated/status (active jobs + counters), /watchdog/status extended with delegation counters. - cmd/ccl-delegate: small CLI exposing submit/get/result/decide so the bash dispatcher can call the same contract without duplicating logic. - cmd/claude-failover: delegation wired opt-in via SECUTOOLS_API_KEY. Tests: - 29+ new unit tests across router, secutools, delegation, dispatcher, api packages. go test -race -count=1 clean. - tests/phase2-E-integration.sh: bash end-to-end against a Python stdlib mock HTTP server, exercising the dev-management scripts. Forward-compat with watchdog (Phase 1 B1 already ignores state=delegated_to_secutools) so delegated tasks aren't flagged stale.
96 lines
5 KiB
Markdown
96 lines
5 KiB
Markdown
# Travaux en Cours - claude-failover
|
|
|
|
## Dernière mise à jour
|
|
2026-04-17 02:20:00
|
|
|
|
## Version Actuelle
|
|
0.4.0 — Phase 2 chantier E livré (multi-provider routing + bash wiring).
|
|
|
|
## Demande en cours
|
|
Aucune demande active. Branche `feat/phase2-E-multi-provider-routing`
|
|
prête à push (en attente de validation utilisateur).
|
|
|
|
## Phase 2 / Chantier E — Statut
|
|
- [x] router (decide tree + 8 tests)
|
|
- [x] secutools client (HTTP + 5 tests + 2 retry tests)
|
|
- [x] secutools/routing.go DecideProvider(map) + 14 table tests
|
|
- [x] delegation manager (submit + reaper + LoadFromDisk + 7 tests)
|
|
- [x] dispatcher integration (routeTask + dispatchProject branch + 5 tests)
|
|
- [x] API endpoints `/api/delegated/status` + `/watchdog/status` enrichi
|
|
- [x] main.go opt-in via `SECUTOOLS_API_KEY`
|
|
- [x] `cmd/ccl-delegate` CLI Go pour bash wrapper
|
|
- [x] tests/phase2-E-integration.sh bout-en-bout avec mock Python
|
|
- [x] tests `-race` clean
|
|
- [ ] smoke test E2E avec vraie API secutools (à faire après push)
|
|
- [ ] dashboard MCP `Secuaas:orchestrator` consommer `/api/delegated/status`
|
|
|
|
## Demande Précédente
|
|
**Phase 1 / Chantier A — Failover robuste** (spec dans `ccl-platform/phases/phase1/A-failover.md`).
|
|
Rendre le failover compte1 ↔ compte2 déterministe en intégrant dans le code les fixes manuels
|
|
(symlinks partagés), en ajoutant un registre UUID fiable, et en durcissant tmux send-keys.
|
|
|
|
Branche : `feat/phase1-A-failover-robust`.
|
|
|
|
## Sous-tâches Chantier A
|
|
- [x] A1 — `internal/symlinks/shared.go` (+ tests) — v0.3.5
|
|
- [ ] A2 — `lifecycle/manager.go` : `ValidateAll` au startup
|
|
- [ ] A3 — `switcher/account_switcher.go` : `EnsureForAccount` post-flip
|
|
- [ ] A4 — `internal/registry/uuid_registry.go` (+ tests)
|
|
- [ ] A5 — `internal/tmux/send.go` avec retry exponentiel (+ tests)
|
|
- [ ] A6 — Capture UUID 200 → 500 lignes
|
|
- [ ] A7 — `scripts/test-failover.sh` dans ccl-platform + scripts associés
|
|
|
|
## Étapes Complétées
|
|
- [x] v0.2.1 — Cooldown post-swap + log forensique (trigger_session, pattern, snippet)
|
|
- [x] v0.2.2 — Confirmation 2-polls pour les hits sans reset time
|
|
- [x] v0.2.3 — Veto 5xx (`api_error` / `overloaded_error` / `internal server error`)
|
|
et retrait du pattern générique `"rate limit"` (remplacé par `rate_limit_error`)
|
|
- [x] Documentation : `docs/architecture.md` §2.2.1 "False-positive protection"
|
|
- [x] Tests unitaires exhaustifs (14 cas pour `isQuotaExhausted` dont les 3 veto 5xx)
|
|
- [x] Déploiement prod : `/usr/local/bin/claude-failover` + service redémarré
|
|
- [x] Push sur Forgejo `origin/main` (commits `7c5f838` et `62e98cb`)
|
|
|
|
## Prochaines Étapes
|
|
- [x] ~~préserver les sessions dédiées lors d'un swap légitime~~ — fait en v0.3.0
|
|
via `saveDedicatedUUIDs` + `relaunchDedicatedSessions`.
|
|
- [ ] **Optionnel** : telegram alert quand `SwapRequested` est émis pour
|
|
que l'opérateur soit au courant sans lire les logs. Le `notifier.Telegram`
|
|
existe déjà — il suffit de câbler.
|
|
- [ ] **Optionnel** : exposer `/quota/status` via `internal/api` avec les champs
|
|
`last_swap_at`, `suspected_hit_at`, `cooldown_remaining` pour le dashboard.
|
|
|
|
## Contexte Important
|
|
- **Symptôme en prod (2026-04-15)** : daemon faisait des swaps compte1↔compte2
|
|
en boucle (intervalle descendant à 1 min), tuant les sessions interactives
|
|
ccl-1 et ccl-2 en permanence. `reset=""` sur tous les events.
|
|
- **Cause racine identifiée via le log forensique v0.2.1** : les 500 d'Anthropic
|
|
contenaient le texte "rate limit" dans leur payload. Snippet capturé :
|
|
`API Error: 500 {"type":"error","error":{"type":"api_error",...}}`.
|
|
Le monitor les confondait avec de vrais 429.
|
|
- **Config `reactivate_cooldown: "5m"`** existait déjà dans config.yaml mais
|
|
n'était consommée que par le dispatcher — pas par le monitor. v0.2.1 a
|
|
branché le monitor dessus.
|
|
- **Comptes disponibles** : `compte1` (Claude Max), `compte2` (Claude Team).
|
|
Symlink actuel : `~/.claude → .claude-compte2`.
|
|
- **Sessions tmux gérées** : pool autonome `ccl-auto-*` (min=2, max=10) +
|
|
dédiées `ccl-1-conformvault`, `ccl-2-scanyze`.
|
|
|
|
## Fichiers Modifiés (cette série de fixes)
|
|
- `internal/quota/monitor.go` — quotaPatterns, serverErrorPatterns, suspectedHitAt, cooldown
|
|
- `internal/quota/monitor_test.go` — 14 sous-tests isQuotaExhausted + 3 tests poll
|
|
- `internal/state/state.go` — LastSwapAt/From/To + RecordSwap + LastSwapInfo
|
|
- `internal/switcher/account_switcher.go` — appel `state.RecordSwap()` après swap
|
|
- `docs/architecture.md` — §2.2.1 False-positive protection
|
|
- `VERSION.md` — changelog 0.2.1 → 0.2.3
|
|
|
|
## Bugs Connus
|
|
- **Sessions dédiées tuées lors d'un swap légitime** : comportement documenté
|
|
et voulu (respawn sur le nouveau compte), mais coupe brutalement le travail
|
|
interactif en cours. Voir Prochaines Étapes.
|
|
|
|
## Historique des Demandes (Récentes)
|
|
| Date | Version | Demande | Status |
|
|
|------|---------|---------|--------|
|
|
| 2026-04-15 | 0.2.1 | Casser le ping-pong + logs forensiques | Terminé |
|
|
| 2026-04-15 | 0.2.2 | Confirmation 2-polls pour absorber les flashes | Terminé |
|
|
| 2026-04-15 | 0.2.3 | Veto 5xx + patterns stricts (root cause) | Terminé |
|