From ce646550cde2be468f9331fb316215abffaf9a84 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 15 Apr 2026 00:57:25 +0200 Subject: [PATCH] fix(ai): bubble proposal-reject feedback into Mission iteration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Planner reads `mission.iterations[].userFeedback` — not `pendingProposals.userFeedback` — so feedback stored only on the proposal row never reached the next plan. rejectProposal now also appends to the matching iteration's `userFeedback` (merging with any existing reasons via "\n· " bullets) when the proposal carries missionId + iterationId. Lazy imports the mission store to avoid a proposal↔mission cycle. Best-effort: proposal rejection still succeeds even if the bubble fails. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../web/src/lib/data/ai/proposals/store.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/mana/apps/web/src/lib/data/ai/proposals/store.ts b/apps/mana/apps/web/src/lib/data/ai/proposals/store.ts index 021e91c3b..bcfdf132a 100644 --- a/apps/mana/apps/web/src/lib/data/ai/proposals/store.ts +++ b/apps/mana/apps/web/src/lib/data/ai/proposals/store.ts @@ -101,6 +101,29 @@ export async function rejectProposal(id: string, userFeedback?: string): Promise userFeedback, }; await table().update(id, updated); + + // Bubble the feedback up to the Mission iteration so the next Planner + // pass (which reads `mission.iterations[].userFeedback` — NOT + // `pendingProposals.userFeedback`) can course-correct. Lazy-import + // to avoid a cycle: mission store ↔ proposal store. + if (userFeedback && proposal.missionId && proposal.iterationId) { + try { + const { addIterationFeedback, getMission } = await import('../missions/store'); + const mission = await getMission(proposal.missionId); + // Merge with any existing feedback on the iteration — different + // steps within one iteration can produce different reasons. + const existingIt = mission?.iterations.find((it) => it.id === proposal.iterationId); + const merged = existingIt?.userFeedback + ? `${existingIt.userFeedback}\n· ${userFeedback}` + : userFeedback; + await addIterationFeedback(proposal.missionId, proposal.iterationId, merged); + } catch (err) { + // Feedback bubble is best-effort — the proposal was still + // rejected successfully if this fails. + console.error('[rejectProposal] failed to bubble feedback to iteration:', err); + } + } + return { ...proposal, ...updated }; }