Skip to content

fix: address LCM/SM review blockers R1, RA, R11, RB#23

Merged
harshitsinghbhandari merged 1 commit into
mainfrom
fix/lcm-sm-review-blockers
May 29, 2026
Merged

fix: address LCM/SM review blockers R1, RA, R11, RB#23
harshitsinghbhandari merged 1 commit into
mainfrom
fix/lcm-sm-review-blockers

Conversation

@harshitsinghbhandari
Copy link
Copy Markdown
Collaborator

@harshitsinghbhandari harshitsinghbhandari commented May 28, 2026

Summary

Four narrowly-scoped fixes against the LCM + Session Manager lane, from an external review of the current backend state.

Out of scope for this PR:

  • R2 (failed-restore lifecycle stranding) and R3 (Restore bypassing LCM lock) — both close via aa-11's PR fix: reconcile LCM writer contract #15 (fix/lcm-writer-contract), which introduces LifecycleManager.OnSpawnInitiated and re-routes Manager.Restore through the LCM. Implementing R2 here would duplicate logic and conflict.
  • Persistence-pipeline changes (Apply*/Upsert) — aa-11's territory.

Fixes

R1 (BLOCKER) — Spawn never persisted AgentSessionID so Restore always failed

Manager.Spawn built SpawnOutcome with an empty AgentSessionID, so MetaAgentSessionID was never written, and Manager.Restore's metadata check hard-failed every time.

Picked the fresh-launch fallback path (the agent's id-capture is a separate hook that may never have run, so there is nowhere to plumb an id-bearing return today):

  • Added Prompt to ports.SpawnOutcome and MetaPrompt to the lifecycle metadata key set.
  • Manager.Spawn now passes the assembled prompt; spawnMetadata persists it.
  • Manager.Restore falls back to Agent.GetLaunchCommand with the seeded prompt when no agent session id is on hand. Restore still fails fast when neither is present — there's nothing to relaunch from.

RA (BLOCKER) — git worktree remove --force deleted dirty worktrees

adapters/workspace/gitworktree/commands.go passed --force, destroying uncommitted agent work and violating the worktree-remove safety invariant.

  • Renamed worktreeRemoveForceArgsworktreeRemoveArgs and dropped --force. The existing post-prune "still registered" guard in Workspace.Destroy now surfaces the refusal to Manager.Cleanup, which routes the session to Skipped.
  • Added a belt-and-braces assertion in the destroy test that --force/-f are NEVER passed.

R11 (SHOULD-FIX) — OrchestratorEvent.ProjectID never populated

Both Notify call sites in reactions.go built events without ProjectID even though the struct supports it.

  • Captured projectID on the transition struct (read from the record already loaded at the top of mutate) and on reactionTracker (so TickEscalations, which has no transition on hand, can still populate it).
  • Threaded the value through react → executeReaction → sendToAgent → escalate and set it on both Notify call sites.

RB (SHOULD-FIX) — session/project ids could escape the project root

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.

Test plan

  • go build ./... passes
  • go vet ./... passes
  • go test -race ./... passes (238 tests across 12 packages)
  • New focused tests:
    • TestRestore_NoAgentSessionID_FreshLaunchFallback — restore succeeds via fresh launch when no id was captured
    • TestRestore_NoIDAndNoPrompt_Errors — restore still fails fast with no id and no prompt
    • TestReaction_ProjectIDOnNotifyAndEscalateEvents (notify path, numeric-escalate path, TickEscalations path)
    • TestValidateConfigRejectsPathEscapingIDs + TestValidateConfigAcceptsBenignIDs
    • TestDestroyRefusesStillRegisteredPathAndPreservesDirectory extended to assert no --force flag
  • Existing TestSpawn_HappyPath updated to assert MetaPrompt is persisted

🤖 Generated with Claude Code

@harshitsinghbhandari harshitsinghbhandari force-pushed the fix/lcm-sm-review-blockers branch from 3641ebc to 44cc6e9 Compare May 29, 2026 11:36
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>
@harshitsinghbhandari harshitsinghbhandari force-pushed the fix/lcm-sm-review-blockers branch from 44cc6e9 to 650ecdf Compare May 29, 2026 12:28
@harshitsinghbhandari harshitsinghbhandari merged commit 8781906 into main May 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant