Handoff + migration spec incorrectly claimed Phase 9 added a new parent_pilot_session_id FK. The implementation reuses the existing ai_session_id column; the migration only adds the origin discriminator + partial unique index. Also: ScriptBuilderTab wraps ScriptBuilderChat and ScriptBodyEditor (Monaco), not "ScriptBuilderChat in ephemeral mode" — there is no ephemeral mode on the presentational component. Applies applied_at call-site specifics: handleScriptDecision stamps on one_off/draft_template, TemplateMatchPanel stamps on onMarkRun, Script Builder tab Submit does not stamp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
faf1d8d fix(pilot): applied_at stamps on run-declaring actions, not Apply click
status: Sprint 9/9 phases complete and pushed; PR not yet opened. Open items #1 and #3 resolved by Phase 9.
FlowPilot Migration — Session Handoff
Where the work lives
- Branch:
feat/flowpilot-migration(pushed to Gitea, mirrors to GitHub) - Spec: docs/FlowAssist_Migration/FLOWPILOT-MIGRATION.md
- Mockups: docs/FlowAssist_Migration/mockups/ (PNG + HTML reference)
What shipped
All nine migration phases are merged onto the branch and verified against the live dev stack (resolutionflow_frontend / resolutionflow_backend / resolutionflow_postgres containers).
| Phase | Commit | What landed |
|---|---|---|
| 0 — baseline telemetry | (pre-branch) | analytics events for funnel deltas |
1 — /assistant → /pilot rename |
early commits | route redirects, sidebar updates |
| 2 — What we know (facts) | (mid) | session_facts table, [PROMOTE] marker, fact CRUD endpoints, WhatWeKnow section |
| 3 — Suggested fix + Resolve preview | 7ccf4c6 and prior |
session_suggested_fixes, [SUGGEST_FIX] marker, ResolutionNotePreview popover |
| 4 — Escalate + PSA writeback | 8fd2c1b |
psa_writeback_service with status verification, kind-parameterized preview |
| 5 — inline Script Generator | fa61376 |
TemplateMatchPanel, NoTemplateDialog three-option dialog |
| 6 — post-resolve templatize | 4aaf57a |
draft_templates table, accept/reject endpoints, TemplatizePrompt modal, account preferences |
| 7 — polish | 8a242f5 |
loading/empty states, keyboard shortcuts (⌘↵, ⌘G, ? overlay), responsive bottom-drawer <1200px |
| 8 — Fix Outcome Banner | cdd8bb0..a47ce07 |
Six outcome columns on session_suggested_fixes (status, applied_at, verified_at, partial_notes, failure_reason, ai_outcome_proposal) + PATCH /api/v1/ai-sessions/{sid}/suggested-fixes/{fid}/outcome endpoint + [FIX_OUTCOME] marker; replaces task-lane SuggestedFix card with a chat-composer-anchored ProposalBanner (5 states: proposed/verifying/partial/ai_confirming/nudge + collapsed); EscalateInterceptDialog captures outcome before handoff; Resolve-while-verifying auto-marks success; 17 new tests (8 endpoint + 7 marker + 2 anti-parrot) |
| 9 — Tabbed Script Builder | 5bcb7aa..faf1d8d |
Chat-region tab strip ([Chat] [Script Builder ●]) with ChatTabStrip + new ScriptBuilderTab controller wrapping the existing ScriptBuilderChat + Monaco editor (ScriptBodyEditor); InlineNoTemplateDialog relocates the existing NoTemplateDialog from the narrow task-lane bottomSlot to a chat-region placement wrapper; EscalateInterceptDialog gains a fourth "partial" choice; PATCH /api/v1/ai-sessions/{sid}/suggested-fixes/{fid}/script endpoint for engineer-drafted scripts (does not stamp applied_at); Alembic migration adds origin VARCHAR(20) to script_builder_sessions (reuses existing ai_session_id FK) + partial unique index on (user_id, ai_session_id) WHERE origin='pilot_inline' for idempotent get-or-create; applied_at semantics corrected to stamp only on run-declaring actions (handleScriptDecision for one_off/draft_template; new onMarkRun on TemplateMatchPanel) — not the Apply click |
Plus the structural fixes that came up along the way:
50215b9+d0ebdef— full sweep removing literal payloads from AI system prompts; newtests/test_prompt_anti_parrot.pyguardrailce7c8ac+ddae171— task-lane state-leak across chats (centralizedresetSessionDerivedState()helper)8879f96— droppedsticky top-0from all four lane section headers (they were orphaning over unrelated content on scroll)
How to resume
git checkout feat/flowpilot-migrationdocker compose -f docker-compose.dev.yml up -d(if the stack isn't running)- Verify:
docker exec resolutionflow_frontend sh -c "cd /app && npx tsc -b"should be clean - Live URL: http://localhost:5173/pilot (or
<host-ip>:5173/pilot) - Test users (password
TestPass123!):engineer@resolutionflow.example.com
Open work — pick one
Items #1 and #3 were discovered during Phase 6/7 verification. Item #2 was resolved by Phase 8. Items #1 and #3 are resolved by Phase 9 (see below).
1. NoTemplateDialog narrow-lane bug
Status: RESOLVED by Phase 9.
Phase 9 relocated InlineNoTemplateDialog from the task-lane bottomSlot into a dedicated chat-region placement wrapper (InlineNoTemplateDialog.tsx). The dialog no longer renders inside the narrow 380px task lane, eliminating the sm:grid-cols-3 viewport-breakpoint collision. The disabled-cards bug (when no ai_drafted_script is present) is also resolved: when no draft exists, the engineer is routed into the new ScriptBuilderTab inline chat instead of reaching the three-option dialog with disabled cards.
See docs/FlowAssist_Migration/phase-9-implementation-plan.md and docs/FlowAssist_Migration/phase-9-script-builder-tab.md for full implementation details.
2. Task lane crowding / Suggested Fix discoverability
Status: RESOLVED by Phase 8. The SuggestedFix card no longer lives inside the scrollable task lane. Phase 8 replaced it with a chat-composer-anchored slide-up banner (ProposalBanner) that is always visible at the bottom of the conversation column regardless of how far the task lane has scrolled. The banner is the primary entry point for fix application; the task lane retains a compact read-only summary of the active fix for reference.
See docs/FlowAssist_Migration/phase-8-fix-outcome-banner.md for the implementation plan and design rationale. Because the banner is now the primary entry point, the NoTemplateDialog narrow-lane bug (open item #1) is considerably less visible — the three-option dialog is only reached after the engineer opts in via the banner, at which point they have already acknowledged the fix.
3. Tabbed Script Builder inside the chat (Option A from the modal-vs-tab discussion)
Status: RESOLVED by Phase 9.
Phase 9 shipped the complete tabbed Script Builder integration. The chat region now has a [Chat] [Script Builder ●] tab strip (ChatTabStrip) powered by a new ScriptBuilderTab controller that wraps the existing (untouched) ScriptBuilderChat for AI mode and ScriptBodyEditor (Monaco) for a "Write it myself" editor mode. display: none toggling preserves chat scroll position, draft message, and editor buffer across tab switches.
The PATCH /api/v1/ai-sessions/{sid}/suggested-fixes/{fid}/script endpoint writes ai_drafted_script + ai_drafted_parameters back to the fix record without stamping applied_at — a draft is not an application. Bumps state_version so cached Resolve/Escalate previews regenerate.
The migration added origin VARCHAR(20) NOT NULL DEFAULT 'standalone' (with CHECK constraint on the two valid values + invariant that origin='pilot_inline' requires ai_session_id IS NOT NULL) to script_builder_sessions. It reuses the pre-existing ai_session_id FK rather than adding a new parent column. A partial unique index on (user_id, ai_session_id) WHERE origin='pilot_inline' backs get-or-create idempotency from the inline tab.
See docs/FlowAssist_Migration/phase-9-implementation-plan.md and docs/FlowAssist_Migration/phase-9-script-builder-tab.md for full implementation details.
Loose ends / things to verify on resume
- PR not opened. Branch is pushed but no Gitea PR yet. When ready:
gh pr createworks against the GitHub mirror, but the actual review happens in Gitea. /ultrareviewnot run on the final state of the branch (including Phase 9). Worth doing before PR creation.- Phase 9 browser QA not done. The new tab strip,
ScriptBuilderTab(AI + editor modes),InlineNoTemplateDialogchat-region placement, andEscalateInterceptDialogfourth-choice flow have not been exercised in a headless-browser session. Key states to cover: tab strip renders and toggles without unmounting chat or losing editor buffer; Script Builder tab Submit persists script via PATCH without stampingapplied_at;one_off/draft_templatedecisions DO stamp;build_templatedoes NOT stamp;TemplateMatchPanel"I ran this" stamps viaonMarkRun; partial-attempt choice inEscalateInterceptDialogis recorded correctly. - Phase 8 browser QA not done. The
ProposalBannerandEscalateInterceptDialog(three-choice variant) have not been exercised in a headless-browser session. Key states: banner appears on[FIX_OUTCOME]marker; banner dismisses correctly; escalate mid-fix triggers dialog; banner auto-collapses after session resolved. Use/qaor/design-reviewagainstmockups/06-slide-up-banner.htmlandmockups/07-verify-states.html. - Phase 7 visual verification was structural only —
tsc -bandnpm run buildboth clean, HMR applied each change without error, but no headless-browser screenshot comparison against the mockup PNGs. If you want pixel-level verification,/qaor/design-reviewwould catch deltas. - Anti-parrot test runs as part of
pytestbut is not enforced in any specific CI step yet — verifytests/test_prompt_anti_parrot.pyis discovered by the existing pytest run, and consider failing CI explicitly on regression.
Files most likely to need attention next
- frontend/src/pages/AssistantChatPage.tsx — 1500+ lines, the central pilot orchestrator. Most state-leak and rendering bugs surface here first. Search for
resetSessionDerivedStateto see the chat-switch reset pattern. - frontend/src/components/assistant/TaskLane.tsx — accepts
whatWeKnowSlot/bottomSlotfrom the parent, plus avariant: 'side' | 'drawer'for responsive.bottomSlotremains active (carriesTemplateMatchPanel+ resolve/escalate preview buttons in both side and drawer variants). - backend/app/services/unified_chat_service.py — owns marker parsing for
[PROMOTE],[SUGGEST_FIX],[QUESTIONS],[ACTIONS],[FORK],[TREE_UPDATE]. If markers stop firing in chat, this is the first place to check. - backend/app/services/assistant_chat_service.py —
ASSISTANT_SYSTEM_PROMPTconstant. Anti-parrot test enforces no literal payloads here; use<placeholder>syntax only.