Repository navigation
feat(drafts): backfill Aye into existing workspaces + Cmd/Ctrl+Enter to send - #223
Merged
Merged
Conversation
Workspaces created before Drafts shipped have no Aye row, so StartDraftTurn can't resolve AyeAgentID and the Send-turn 409s with "could not start a turn". Add an idempotent boot-time backfill, modeled on the migrateShipHubSecrets one-shot migrator: it lists every workspace with its owner, and for any missing Aye at her deterministic id, runs the existing seedAyeAgent (verbatim — no duplicated persona/skill content) in its own transaction. - New ListWorkspacesWithOwner query sources the owner from the owner-role member (the skill's created_by references "user"); workspaces with no owner are skipped. - BackfillAyeAgents lives in package handler so it reuses the unexported seedAyeAgent/AyeAgentID; wired as a goroutine in main.go alongside the other boot tasks so it never blocks startup. - Idempotent: a workspace that already has Aye is skipped after one probe, so it's safe to run on every boot. Logs the count seeded. Test: a workspace without Aye gets a correctly-seeded Aye (agent + skill + agent_skill link, attributed to the owner) after the backfill; a second run is a no-op. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pressing Cmd+Enter (mac) / Ctrl+Enter triggers the same Send action as the header button. The gotcha: StarterKit's HardBreak binds Mod-Enter to insert a line break, so a plain window listener would BOTH break AND send. Intercept at the editor level with a drafts-local Tiptap extension (createDraftSendShortcutExtension) bound to Mod-Enter + Ctrl-Enter; it returns true to consume the event before HardBreak sees it, and runs at priority 1000 so its keymap plugin is ordered ahead of HardBreak's. The shared createEditorExtensions factory is untouched — the keymap is added only to the drafts editor. Lift the send action: useStartDraftTurn + handleSend (with its in-flight guard) move up to AnnotationDraftEditor, which already owns activeTurnTaskId. DraftSendControl becomes presentational (onSend/working/pending props). The same handleSend backs the button, the editor keymap, and a window-level fallback for when focus is outside the editor (guarded on editor.isFocused to avoid a double-fire). The Send button's tooltip now hints the shortcut (⌘↵ / Ctrl+Enter) via the shared platform formatShortcut helper. Tests: a dedicated send-shortcut unit test asserts both bindings fire onSend and return true (so no hard break is inserted, even when onSend no-ops mid-turn) and that priority > HardBreak's default; the narration test is updated for the presentational DraftSendControl. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two small, related Drafts improvements so the Send-turn works in existing (pre-Drafts-deploy) workspaces AND can be triggered by keyboard.
Task 1 — Idempotent boot-time Aye backfill
Aye (the Drafts lead agent) is seeded only on workspace creation (
seedAyeAgent, inside the create txn). Workspaces created before the Drafts deploy have no Aye row, soStartDraftTurnderivesAyeAgentID(wsId), finds nothing, and 409s with "could not start a turn" (confirmed on prod: 3 existing workspaces, zero Aye agents).BackfillAyeAgents(inpackage handler, so it reuses the unexportedseedAyeAgent/AyeAgentIDverbatim — no duplicated persona/skill content), modeled on the existing one-shotmigrateShipHubSecretsboot migrator.main.goalongside the other boot tasks — runs once on boot, never blocks startup.ListWorkspacesWithOwnerquery joins each workspace to itsowner-rolemember(DISTINCT ON (w.id), earliest owner). The skill'screated_byreferences"user", so the owner-member'suser_idis the right attribution. Workspaces with no owner member are skipped.GetAgent(AyeAgentID(wsId)); only seed (in its own transaction) when she's missing. A re-run on every boot is a cheap no-op. Logs the count seeded.Test: a workspace without Aye gets a correctly-seeded Aye (agent at
AyeAgentID+ the Drafts skill + theagent_skilllink, attributed to the owner); a second run is a no-op (still exactly one agent + one skill link).Task 2 — Cmd/Ctrl+Enter sends the draft turn
Pressing Cmd+Enter (mac) / Ctrl+Enter triggers the same action as the Send button.
HardBreakbindsMod-Enterto insert a line break, so a plain window listener would BOTH break AND send. Intercepted at the editor level with a drafts-local Tiptap extension (createDraftSendShortcutExtension) bound toMod-Enter+Ctrl-Enter; it returnstrueto consume the event before HardBreak sees it, and runs at priority 1000 so its keymap plugin is ordered ahead of HardBreak's in ProseMirror's stack. The sharedcreateEditorExtensionsfactory is untouched — the keymap is added only to the drafts editor.useStartDraftTurn+handleSend(with its in-flight guard) move up toAnnotationDraftEditor(which already ownsactiveTurnTaskId).DraftSendControlis now presentational (onSend/working/pendingprops). The samehandleSendbacks the button, the editor keymap, and a window-level fallback for when focus is outside the editor (e.g. the thread panel) — guarded oneditor.isFocusedso it never double-fires.formatShortcuthelper (the "Send" word stays localized; the shortcut glyphs are platform-derived, so no new locale string).Test: a dedicated
send-shortcutunit test asserts both bindings fireonSendand returntrue(so no hard break is inserted, even whenonSendno-ops mid-turn) and that priority > HardBreak's default; the narration test is updated for the presentationalDraftSendControl.Verification
go build ./...✓ ·go vet ./...✓ ·gofmtclean ✓ ·go test ./internal/handler✓ (new backfill test + all existing Aye/draft-turn tests pass)pnpm typecheck✓ (6/6) ·pnpm --filter @multica/views exec vitest run✓ (851 passed) ·pnpm --filter @multica/core exec vitest run✓ (364 passed) · eslint clean on changed files (only the two pre-existing Yjsexhaustive-depswarnings, unchanged)🤖 Generated with Claude Code