fix(dashboard): resolve planning 404 for sessions started outside dashboard
_resolve_planning_key searches all active sessions by slug regardless of adapter, so respond/finalize/cancel/advance work even when planning was initiated from Discord or Telegram. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -690,6 +690,35 @@ class ProjectsHandlers:
|
|||||||
"""Dashboard planning sessions live in adapter='dashboard', channel=slug."""
|
"""Dashboard planning sessions live in adapter='dashboard', channel=slug."""
|
||||||
return ("dashboard", slug)
|
return ("dashboard", slug)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _resolve_planning_key(slug: str) -> tuple[str, str]:
|
||||||
|
"""Find the active session's (adapter, channel) for slug, regardless of
|
||||||
|
where it was started. Falls back to ('dashboard', slug) if nothing
|
||||||
|
matches — preserving prior behavior for the no-session case.
|
||||||
|
|
||||||
|
Picks the most-recently-updated entry when multiple exist.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from src.planning_session import _load_planning_state # type: ignore
|
||||||
|
except Exception:
|
||||||
|
return ("dashboard", slug)
|
||||||
|
try:
|
||||||
|
all_state = _load_planning_state()
|
||||||
|
except Exception:
|
||||||
|
return ("dashboard", slug)
|
||||||
|
|
||||||
|
matches = [
|
||||||
|
entry for entry in all_state.values()
|
||||||
|
if (entry.get("slug") or "").lower() == slug.lower()
|
||||||
|
]
|
||||||
|
if not matches:
|
||||||
|
return ("dashboard", slug)
|
||||||
|
matches.sort(key=lambda e: e.get("updated_at") or "", reverse=True)
|
||||||
|
best = matches[0]
|
||||||
|
adapter = best.get("adapter") or "dashboard"
|
||||||
|
channel = best.get("channel_id") or slug
|
||||||
|
return (adapter, channel)
|
||||||
|
|
||||||
# ── POST /api/projects/<slug>/plan/start ──────────────────────
|
# ── POST /api/projects/<slug>/plan/start ──────────────────────
|
||||||
def handle_plan_start(self, slug: str):
|
def handle_plan_start(self, slug: str):
|
||||||
if validate_slug(slug):
|
if validate_slug(slug):
|
||||||
@@ -793,7 +822,7 @@ class ProjectsHandlers:
|
|||||||
self.send_json({"error": "message required"}, 400)
|
self.send_json({"error": "message required"}, 400)
|
||||||
return
|
return
|
||||||
|
|
||||||
adapter, channel = self._planning_key(slug)
|
adapter, channel = self._resolve_planning_key(slug)
|
||||||
try:
|
try:
|
||||||
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
||||||
session, text, phase_ready = PlanningOrchestrator.respond(
|
session, text, phase_ready = PlanningOrchestrator.respond(
|
||||||
@@ -915,7 +944,7 @@ class ProjectsHandlers:
|
|||||||
if validate_slug(slug):
|
if validate_slug(slug):
|
||||||
self.send_json({"error": "invalid_slug"}, 400)
|
self.send_json({"error": "invalid_slug"}, 400)
|
||||||
return
|
return
|
||||||
adapter, channel = self._planning_key(slug)
|
adapter, channel = self._resolve_planning_key(slug)
|
||||||
try:
|
try:
|
||||||
from src.planning_session import get_planning_state, clear_planning_state # type: ignore
|
from src.planning_session import get_planning_state, clear_planning_state # type: ignore
|
||||||
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
||||||
@@ -964,7 +993,7 @@ class ProjectsHandlers:
|
|||||||
if validate_slug(slug):
|
if validate_slug(slug):
|
||||||
self.send_json({"error": "invalid_slug"}, 400)
|
self.send_json({"error": "invalid_slug"}, 400)
|
||||||
return
|
return
|
||||||
adapter, channel = self._planning_key(slug)
|
adapter, channel = self._resolve_planning_key(slug)
|
||||||
try:
|
try:
|
||||||
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
@@ -992,7 +1021,7 @@ class ProjectsHandlers:
|
|||||||
if validate_slug(slug):
|
if validate_slug(slug):
|
||||||
self.send_json({"error": "invalid_slug"}, 400)
|
self.send_json({"error": "invalid_slug"}, 400)
|
||||||
return
|
return
|
||||||
adapter, channel = self._planning_key(slug)
|
adapter, channel = self._resolve_planning_key(slug)
|
||||||
try:
|
try:
|
||||||
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
from src.planning_orchestrator import PlanningOrchestrator # type: ignore
|
||||||
session, text, completed = PlanningOrchestrator.advance(adapter, channel)
|
session, text, completed = PlanningOrchestrator.advance(adapter, channel)
|
||||||
|
|||||||
Reference in New Issue
Block a user