Four narrowly-scoped fixes against the LCM + Session Manager lane
from an external review of the current backend state. R2 (failed-restore
lifecycle stranding) is intentionally deferred to PR #15, which already
closes it via the new OnSpawnInitiated path; R3 also stays on that PR.
- R1 (BLOCKER): Manager.Spawn never persisted AgentSessionID, so
Manager.Restore's hard-required metadata key was always missing and
every restore failed. Persist the assembled launch prompt as
MetaPrompt at spawn time and add a fresh-launch fallback to Restore
that uses Agent.GetLaunchCommand with the seeded prompt when the
captured agent session id is absent (the id-capture hook is a separate
path that may never have run). Restore still fails fast when neither
the id nor a prompt is on hand — there is nothing to relaunch from.
- RA (BLOCKER): adapters/workspace/gitworktree/commands.go's
worktreeRemoveForceArgs passed --force, which deletes uncommitted
agent work. Renamed to worktreeRemoveArgs and dropped --force so the
post-prune "still registered" guard in Workspace.Destroy surfaces the
refusal to Manager.Cleanup, which routes the session to Skipped
instead of destroying in-progress changes.
- R11 (SHOULD-FIX): reactions.go's two Notifier.Notify call sites
(executeReaction's notify and escalate) built OrchestratorEvent
without ProjectID. Captured projectID on the transition (via a
store.Get in mutate) and on reactionTracker (so TickEscalations can
still populate it on duration-based escalations), and threaded it
through executeReaction/sendToAgent/escalate.
- RB (SHOULD-FIX): gitworktree.Workspace.managedPath used filepath.Join
which cleans .. segments before validateManagedPath ran, so
session=\"../other\" stayed inside managedRoot while breaking
per-project isolation. validateConfig now rejects path separators and
the . / .. components on ProjectID and SessionID at the source.
go build ./..., go vet ./..., and go test -race ./... all pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>