- Add optional globalDir parameter to resolveProject(), getDefaultProject(), setDefaultProject(), and clearDefaultProject() for test isolation - Remove ESLint suppression by using void operator for intentionally unused var - Update all tests to use isolated globalDir parameter - Complete tests for default project resolution
14 KiB
Task: KB-621 - AI Title Summarization from Long Descriptions
Created: 2026-03-31 Size: M
Review Level: 2 (Plan and Code)
Assessment: This involves adding settings, creating a new AI service in core, modifying task creation flow, and dashboard UI changes. The blast radius spans core types, store behavior, core AI service, dashboard API, and UI. Score: 5/8 — Blast radius: 1, Pattern novelty: 1, Security: 2, Reversibility: 1
Mission
Implement an optional AI-powered title summarization feature that automatically generates concise titles from task descriptions longer than 140 characters. When enabled and a task is created without a title, the system uses AI to create a summary (≤60 characters) from the description. Users can choose which AI model to use for summarization via project settings.
Dependencies
- None
Context to Read First
packages/core/src/types.ts— Settings interfaces, PROJECT_SETTINGS_KEYS, TaskCreateInputpackages/core/src/store.ts— createTask method (line ~575)packages/dashboard/src/routes.ts— API routes structure, existing/ai/refine-textendpoint at line 4645packages/dashboard/src/ai-refine.ts— Pattern for AI text generation services (used for reference, but service goes in core)packages/dashboard/app/components/SettingsModal.tsx— Settings UI structure, SETTINGS_SECTIONS at line 78packages/engine/src/pi.ts— createKbAgent pattern for AI sessions
File Scope
packages/core/src/types.ts— AddautoSummarizeTitles,titleSummarizerProvider,titleSummarizerModelIdto ProjectSettings; addsummarize?: booleanto TaskCreateInputpackages/core/src/ai-summarize.ts— New AI summarization service (in core package, not dashboard)packages/core/src/store.ts— Modify createTask to trigger summarizationpackages/core/src/store.test.ts— Add tests for title summarization behaviorpackages/dashboard/src/routes.ts— Add POST/ai/summarize-titleendpointpackages/dashboard/app/api.ts— Add API client for summarize-title endpointpackages/dashboard/app/api.test.ts— Add tests for summarizeTitle API clientpackages/dashboard/app/components/SettingsModal.tsx— Add "AI Summarization" settings section.changeset/ai-title-summarization.md— Changeset for the feature
Steps
Step 1: Add Settings Types
- Add
autoSummarizeTitles?: booleantoProjectSettingsinterface inpackages/core/src/types.ts - Add
titleSummarizerProvider?: stringtoProjectSettingsinterface - Add
titleSummarizerModelId?: stringtoProjectSettingsinterface - Add
summarize?: booleantoTaskCreateInputinterface in types.ts - Add new keys to
PROJECT_SETTINGS_KEYSarray:autoSummarizeTitles,titleSummarizerProvider,titleSummarizerModelId - Add default values to
DEFAULT_PROJECT_SETTINGS:autoSummarizeTitles: false - Run TypeScript typecheck in packages/core to verify no errors
Artifacts:
packages/core/src/types.ts(modified)
Step 2: Create Core AI Summarization Service
- Create
packages/core/src/ai-summarize.tsfollowing the AI pattern from dashboard'sai-refine.ts - Implement
SUMMARIZE_SYSTEM_PROMPTconstant with instructions:- "Create a concise title (max 60 characters) that summarizes the given task description"
- "Return only the title text, no quotes, no markdown, no explanations"
- "Capture the essence of what the task is about"
- Implement
summarizeTitle()function that:- Accepts description text, rootDir, and optional provider/modelId
- Uses dynamic import of
@fusion/engine(like ai-refine.ts pattern) to getcreateKbAgent - Returns a single-line summary (≤60 characters)
- Truncates result to 60 chars if AI returns longer text
- Returns null for descriptions ≤140 chars (validation, not generation)
- Add rate limiting state and
checkRateLimit(ip: string)function (10 requests per hour per IP) - Add validation: description must be 141-2000 characters
- Export custom error classes:
ValidationError,AiServiceError,RateLimitError - Ensure graceful handling when engine import fails (throw AiServiceError)
- Create
packages/core/src/ai-summarize.test.tswith unit tests for validation, rate limiting, and error handling - Run targeted tests for new file
Artifacts:
packages/core/src/ai-summarize.ts(new)packages/core/src/ai-summarize.test.ts(new)
Step 3: Modify Task Store for Summarization
- Add optional
onSummarize?: (description: string) => Promise<string | null>callback parameter tocreateTask()method signature - In
createTask()method body, add logic after validation:- If
input.summarize === trueOR (settings hasautoSummarizeTitles: trueAND no title provided):- Check if description.length > 140
- If so, call
onSummarize?.(input.description)if callback provided - If callback returns a title, use it; if null/undefined, proceed without title
- If callback throws, log warning via
taskLogbut don't block task creation
- If
- Do NOT import ai-summarize.ts directly in store.ts — use the callback pattern to avoid core→engine dependency issues
- The store should remain agnostic to how summarization is implemented
- Update
packages/core/src/store.test.ts:- Add test for createTask with summarize callback returning a title
- Add test for createTask with summarize callback returning null
- Add test for createTask with autoSummarizeTitles setting enabled
- Add test for graceful handling when summarize callback throws
- Run store tests to verify behavior
Artifacts:
packages/core/src/store.ts(modified)packages/core/src/store.test.ts(modified)
Step 4: Add Dashboard API Endpoint
- Add POST
/ai/summarize-titleroute inpackages/dashboard/src/routes.ts(note: no/apiprefix — router is already mounted at/api) - Request body validation:
description: string(required, 141-2000 chars)provider?: string(optional)modelId?: string(optional)
- Model selection hierarchy when called:
- Use request body provider+modelId if provided
- Else use settings
titleSummarizerProvider+titleSummarizerModelIdif configured - Else use settings
planningProvider+planningModelIdif configured - Else use settings
defaultProvider+defaultModelIdif configured - Else use automatic model resolution (no explicit model)
- Import
summarizeTitleandcheckRateLimitfrom@fusion/core(since it's now in core package) - Get client IP for rate limiting:
req.iporreq.socket.remoteAddress - Response:
{ title: string }(the generated summary, guaranteed ≤60 chars) - Error handling:
- 400 for validation errors (description too short/long, missing)
- 429 for rate limit exceeded (include reset time in error message if available)
- 503 for AI service unavailable
- 500 for unexpected errors
- Add debug logging behind
KB_DEBUG_AIenv flag - Run dashboard tests to verify route integration
Artifacts:
packages/dashboard/src/routes.ts(modified)
Step 5: Wire Up Summarization in Dashboard Routes
- In
packages/dashboard/src/routes.ts, locate wherestore.createTaskis called (around line 1365) - Pass
onSummarizecallback tostore.createTask():onSummarize: async (description) => { const settings = await store.getSettings(); // Resolve model selection hierarchy const provider = settings.titleSummarizerProvider || settings.planningProvider || settings.defaultProvider; const modelId = settings.titleSummarizerModelId || settings.planningModelId || settings.defaultModelId; // Call core's summarizeTitle function return await summarizeTitle(description, store.getRootDir(), provider, modelId); } - Import
summarizeTitlefrom@fusion/coreat top of routes.ts - Ensure the callback handles errors gracefully (returns null on error so task creation proceeds)
- Verify other createTask calls (planning mode, subtask breakdown, etc.) can optionally pass onSummarize
Artifacts:
packages/dashboard/src/routes.ts(modified)
Step 6: Add Dashboard API Client
- Add
summarizeTitle()function topackages/dashboard/app/api.ts - Function signature:
summarizeTitle(description: string, provider?: string, modelId?: string): Promise<string> - Makes POST request to
/api/ai/summarize-title - Handle 429 responses specifically with retry-after message
- Handle 503 with "AI service temporarily unavailable"
- Throw meaningful error messages for other failures
- Add test coverage in
packages/dashboard/app/api.test.ts:- Success case returns title
- 400 validation error handling
- 429 rate limit handling
- 503 service unavailable handling
- Run api tests
Artifacts:
packages/dashboard/app/api.ts(modified)packages/dashboard/app/api.test.ts(modified)
Step 7: Add Settings UI
- Add new "AI Summarization" section to
SETTINGS_SECTIONSin SettingsModal.tsx - Insert between "model-presets" (index 2) and "appearance" (index 3) sections
- Set scope: "project" (stored in
.kb/config.json) - Add UI controls in renderSectionFields() for the new section:
- Checkbox: "Auto-summarize long descriptions as titles"
- Binds to
form.autoSummarizeTitles - Help text: "When enabled, tasks created without a title but with descriptions over 140 characters will automatically get an AI-generated title (max 60 characters)."
- Binds to
- Model selector for "Title summarization model" (only visible when checkbox enabled)
- Use
CustomModelDropdowncomponent for provider selection - Use a second dropdown for model selection (re-use pattern from ModelSelectorTab)
- Show "(using planning model)" or "(using default model)" when not explicitly set
- Use
- Quick action buttons:
- "Use planning model" — sets to planningProvider/planningModelId
- "Use default model" — sets to defaultProvider/defaultModelId
- Checkbox: "Auto-summarize long descriptions as titles"
- Ensure form state properly initializes from settings
- Add scope banner in renderScopeBanner() for the new section
- Verify UI doesn't break when availableModels is loading
- Test UI rendering: open settings, navigate to AI Summarization section, verify controls appear
Artifacts:
packages/dashboard/app/components/SettingsModal.tsx(modified)
Step 8: Testing & Verification
ZERO test failures allowed. Full test suite as quality gate.
- Run
pnpm testin packages/core — all tests pass including new ai-summarize tests - Run
pnpm testin packages/dashboard — all tests pass including new API tests - Run
pnpm build— builds without errors in all packages - Verify TypeScript typecheck passes:
pnpm typecheck(or equivalent) - Test rate limiting: make 11 rapid requests to summarize-title endpoint, verify 429 on 11th
- Test model fallback hierarchy by:
- Setting only default model → should use default
- Setting planning model → should prefer planning over default
- Setting titleSummarizer model → should prefer over planning/default
- Manual UI test:
- Open dashboard settings, enable auto-summarize
- Select a specific model for summarization
- Create task with no title but long description (>140 chars) via quick entry
- Verify task appears with auto-generated title
- Disable setting, create another long-description task
- Verify no title is generated (task shows ID only)
Step 9: Documentation & Delivery
- Create changeset file:
.changeset/ai-title-summarization.md--- "@gsxdsm/fusion": patch --- Add AI-powered title summarization feature. When enabled via settings, tasks created without titles but with descriptions longer than 140 characters will automatically receive an AI-generated title (max 60 characters). Includes configurable model selection for the summarization with fallback to planning and default models. - Update AGENTS.md settings documentation section if it exists:
- Add documentation for
autoSummarizeTitles,titleSummarizerProvider,titleSummarizerModelId - Document the model selection hierarchy: titleSummarizer → planning → default
- Add documentation for
- Verify all commits include
KB-621prefix - Verify no files modified outside File Scope
Documentation Requirements
Must Update:
.changeset/ai-title-summarization.md— New changeset describing the feature
Check If Affected:
AGENTS.md— Check if settings documentation section exists and update if relevant
Completion Criteria
- All steps complete
- All tests passing
- Documentation updated with changeset
- Feature works end-to-end (settings → task creation with AI summary)
- Rate limiting verified working
- Model fallback hierarchy verified working
Git Commit Convention
Commits at step boundaries. All commits include the task ID:
- Step completion:
feat(KB-621): complete Step N — description - Bug fixes:
fix(KB-621): description - Tests:
test(KB-621): description - Changeset:
feat(KB-621): add changeset for AI title summarization
Do NOT
- Expand task scope to include automatic summarization of existing tasks (only new tasks)
- Skip rate limiting on the summarize endpoint
- Modify files outside the File Scope without good reason
- Commit without the KB-621 prefix
- Use AI summarization when user explicitly provided a title (respect user input)
- Create circular dependencies between packages (core must not depend on dashboard or engine)