TASK-345 - Configurable ID prefix system (task-, custom-)#470
Merged
Conversation
- Add PrefixConfig interface to src/types/index.ts - Add prefixes field to BacklogConfig type - Create src/utils/prefix-config.ts with helper functions: - normalizeId, extractIdBody, extractIdNumbers - buildGlobPattern, buildIdRegex, buildFilenameIdRegex - hasPrefix, idsEqual, generateNextId, generateNextSubtaskId - Add 52 unit tests in src/test/prefix-config.test.ts This is the foundation for configurable ID prefixes (task-, draft-, custom).
This commit implements the ID normalization system where: - Canonical IDs are uppercase (TASK-123, TASK-0007) - Filenames use lowercase prefix (task-123 - Title.md) Key changes: - normalizeId() now uppercases entire ID (prefix and body) - idForFilename() converts IDs to lowercase for filenames - saveTask/saveDraft use idForFilename() for file paths - getTaskPath/getDraftPath use lowercase for filename matching - reorderTask normalizes IDs before comparison - Commit messages use uppercase normalized IDs - Search service handles uppercase IDs with case-insensitive matching - createTaskIdVariants now includes numeric segments for short queries All test expectations updated to expect uppercase IDs.
- Update listDrafts() to scan for draft-*.md pattern - Update saveDraft() to normalize IDs with draft prefix - Update loadDraft(), archiveDraft(), promoteDraft() for draft-prefixed files - Update getDraftPath() in task-path.ts for draft prefix matching - Update createTaskFromData/createTaskFromInput to use EntityType.Draft - Update Core commit messages for proper draft ID normalization - Update all draft-related tests to use DRAFT-X format Breaking change: Drafts now use DRAFT-X IDs and draft-x filenames. Existing drafts with task- prefix won't be found until migrated.
- Add buildPathIdRegex() helper for matching IDs in file paths (no ^ anchor) - Update buildRemoteTaskIndex() with optional prefix parameter - Update buildLocalBranchTaskIndex() with optional prefix parameter - Update findTaskInRemoteBranches() and findTaskInLocalBranches() with prefix - Update cross-branch-tasks.ts getLatestTaskStatesForIds() with prefix option - Index stores lowercase IDs to match filename convention - All existing tests pass without modification
…ble prefixes - Update parseTaskId() to strip any prefix pattern (task-, draft-, JIRA-, etc.) - Update content-store.ts task watcher to accept any prefix-style filenames - Replace TASK_ID_PREFIX constants with prefix-agnostic helpers - Update task-search.ts and search-service.ts with generic prefix handling - Update server/index.ts ID normalization to be prefix-agnostic - All functions now work with any prefix pattern (/^[a-zA-Z]+-/)
- Add hasAnyPrefix() and stripAnyPrefix() helpers to prefix-config.ts - Update task-watcher.ts to use hasAnyPrefix for file filtering - Update cli.ts to use hasAnyPrefix for task filtering - Update task-viewer-with-search.ts to use hasAnyPrefix - Update unified-view.ts to use hasAnyPrefix (3 locations) - Update simple-unified-view.ts to use hasAnyPrefix (3 locations) - Update SideNavigation.tsx to use generic prefix regex - Update test-helpers.ts to use hasAnyPrefix for ID normalization All task filtering now accepts any prefix pattern (task-, draft-, JIRA-, etc.)
- Update promoteDraft() to generate new task- ID using generateNextId() - Update demoteTask() to generate new draft- ID using generateNextId() - Old files are properly deleted after ID reassignment - ID sequences respect config.zeroPaddedIds setting Also fixes pre-existing test failures: - Update filesystem.test.ts to expect uppercase IDs (TASK-1, TASK-2, etc.) - Update cli.test.ts to expect uppercase IDs in output and comparisons - Tests now properly verify ID reassignment behavior
- Add prefix-migration.ts with needsDraftPrefixMigration() and migrateDraftPrefixes() functions - Integration into Core's ensureConfigMigrated() method - Migration renames task-*.md files in drafts to draft-*.md format - Generates new sequential DRAFT-N IDs for migrated files - Adds prefixes section to config.yml after migration - Migration is idempotent (safe to run multiple times) - Add comprehensive test suite (9 tests covering all scenarios) - Applied migration to project's own draft files
Bug: Promoting drafts with custom task prefix (e.g., JIRA-) generated correct ID but saved/loaded using hardcoded task-* pattern, causing collisions and lookup failures. Fixes: - saveTask: Extract prefix from ID or use configured prefix - listTasks/listCompletedTasks/listArchivedTasks: Use configured prefix - getTaskPath/getTaskFilename: Extract prefix from taskId parameter New helper: - extractAnyPrefix(id): Returns the prefix part (e.g., "task" from "task-123") Tests added: - Custom prefix promote/demote in filesystem.test.ts - Preserve custom prefix in task ID - extractAnyPrefix unit tests
Add ability to set custom task prefix (e.g., JIRA, BUG, ISSUE) during initialization. The prefix is read-only after first setup to prevent breaking existing task IDs. Changes: - CLI: Add --task-prefix flag and interactive prompt (first-time only) - CLI: Show taskPrefix in config list with (read-only) suffix - CLI: Block config set taskPrefix with error message - Browser: Add task prefix input in Advanced Settings (ID Formatting) - Browser: Show read-only task prefix in Settings page - Core: Add taskPrefix to advancedConfig in initializeProject - Tests: Cover custom prefix, preservation on re-init, validation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 121fd60627
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Bug fixes: - Fix subtask case-sensitivity: use case-insensitive comparison for legacy lowercase IDs in subtask detection - Fix draft promotion: include completed tasks in ID generation to prevent ID collisions Cleanup: - Consolidate escapeRegex function (export from prefix-config.ts) - Remove unused draft field from PrefixConfig interface - Add DRAFT_PREFIX constant for hardcoded draft prefix Tests added for both bug scenarios using TDD approach.
agentroadmap
pushed a commit
to agentroadmap/roadmap.md
that referenced
this pull request
Mar 13, 2026
…esk#470) <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR implements a configurable ID prefix system that allows tasks to use `task-` (default) or custom prefixes (e.g., `JIRA-`, `issue-`), while drafts use `draft-` prefix. IDs are now uppercase (`TASK-123`, `DRAFT-5`) while filenames remain lowercase (`task-123.md`, `draft-5.md`). **Major changes:** - Created `src/utils/prefix-config.ts` with comprehensive prefix utilities (normalizeId, generateNextId, buildGlobPattern, etc.) - Added `EntityType` enum to support type-aware ID generation across tasks, drafts, docs, and decisions - Implemented one-time migration (`src/core/prefix-migration.ts`) that renames legacy `task-*.md` files in drafts folder to `draft-*.md` - Updated promote/demote operations to reassign IDs (draft → new task ID, task → new draft ID) rather than just moving files - Added `--task-prefix` option to `backlog init` command (first-time only, read-only after) - Updated file system operations, task loaders, search service, and UI components for prefix awareness - Renamed all draft files in `backlog/drafts/` from `task-X` to `draft-X` format **Test coverage:** - 370+ lines of prefix-config tests covering all edge cases - 299+ lines of migration tests - All filesystem, CLI, and integration tests updated for new prefix system **Issue found:** - `src/cli.ts:2149` uses `EntityType.Task` instead of `EntityType.Draft` for draft creation, with an outdated TODO comment claiming file operations aren't ready (they are) <h3>Confidence Score: 4/5</h3> - This PR is well-implemented with comprehensive tests, but has one logic bug in draft creation that needs fixing - Score reflects excellent architecture (EntityType abstraction, comprehensive prefix utilities, proper migration), thorough test coverage (669 new test lines), and systematic refactoring across all layers. However, the draft creation bug (using wrong EntityType) prevents a score of 5, as it would cause drafts to generate TASK-X IDs instead of DRAFT-X IDs when created via CLI - Pay close attention to `src/cli.ts:2149` - the draft creation logic uses wrong EntityType which will cause runtime behavior mismatch <h3>Important Files Changed</h3> | Filename | Overview | |----------|----------| | src/cli.ts | CLI updated for prefix config support; outdated TODO comment at line 2149 should be removed as draft operations are already updated | | src/utils/prefix-config.ts | New comprehensive utility module for prefix handling with extensive helper functions; well-structured and thoroughly tested | | src/core/prefix-migration.ts | One-time migration for draft files from task- to draft- prefix; handles edge cases cleanly with proper error handling | | src/core/backlog.ts | Core logic updated for entity-type-based ID generation; properly integrates prefix config with cross-branch task handling | | src/file-system/operations.ts | File operations updated for configurable prefixes; promote/demote now correctly reassign IDs as designed | | src/core/init.ts | Init command enhanced to support custom task prefix configuration during first initialization | </details> <h3>Sequence Diagram</h3> ```mermaid sequenceDiagram participant User participant CLI participant Core participant PrefixConfig participant FileSystem participant Migration Note over User,Migration: First-time initialization with custom prefix User->>CLI: backlog init --task-prefix JIRA CLI->>Core: initializeProject(config) Core->>FileSystem: ensureBacklogStructure() Core->>FileSystem: saveConfig({prefixes: {task: "JIRA", draft: "draft"}}) Core-->>CLI: Project initialized CLI-->>User: Success Note over User,Migration: Existing project load (migration check) User->>CLI: backlog task list CLI->>Core: ensureConfigLoaded() Core->>FileSystem: loadConfig() Core->>Migration: needsDraftPrefixMigration(config) alt No prefixes section in config Migration-->>Core: true Core->>Migration: migrateDraftPrefixes(fs) Migration->>FileSystem: listDrafts() FileSystem-->>Migration: [task-1.md, task-2.md] loop For each task-*.md file Migration->>FileSystem: parseTask(content) Migration->>PrefixConfig: generateNextId(existingIds, "draft") PrefixConfig-->>Migration: DRAFT-1 Migration->>FileSystem: saveDraft({id: "DRAFT-1", ...}) Migration->>FileSystem: unlink(task-1.md) end Migration->>FileSystem: saveConfig({prefixes: {...}}) else Has prefixes section Migration-->>Core: false end Note over User,Migration: Creating a draft User->>CLI: backlog draft create "New Feature" CLI->>Core: generateNextId(EntityType.Draft) Core->>PrefixConfig: getPrefixForType(Draft) PrefixConfig-->>Core: "draft" Core->>FileSystem: listDrafts() Core->>PrefixConfig: generateNextId(existingIds, "draft") PrefixConfig-->>Core: DRAFT-5 CLI->>Core: createDraft({id: "DRAFT-5", ...}) Core->>FileSystem: saveDraft(task) FileSystem->>PrefixConfig: normalizeId("DRAFT-5", "draft") PrefixConfig-->>FileSystem: DRAFT-5 FileSystem->>PrefixConfig: idForFilename("DRAFT-5") PrefixConfig-->>FileSystem: draft-5 FileSystem-->>Core: draft-5 - New Feature.md Core-->>User: Created draft DRAFT-5 Note over User,Migration: Promoting draft to task User->>CLI: backlog draft promote DRAFT-5 CLI->>Core: promoteDraft("DRAFT-5") Core->>FileSystem: loadDraft("DRAFT-5") FileSystem->>PrefixConfig: normalizeId("DRAFT-5", "draft") FileSystem-->>Core: {id: "DRAFT-5", ...} Core->>FileSystem: listTasks() Core->>PrefixConfig: generateNextId(existingIds, "JIRA") PrefixConfig-->>Core: JIRA-123 Core->>FileSystem: saveTask({id: "JIRA-123", ...}) FileSystem->>PrefixConfig: idForFilename("JIRA-123") PrefixConfig-->>FileSystem: jira-123 Core->>FileSystem: unlink(draft-5.md) Core-->>User: Promoted to JIRA-123 ``` <!-- greptile_other_comments_section --> <!-- /greptile_comment -->
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.
Greptile Summary
This PR implements a configurable ID prefix system that allows tasks to use
task-(default) or custom prefixes (e.g.,JIRA-,issue-), while drafts usedraft-prefix. IDs are now uppercase (TASK-123,DRAFT-5) while filenames remain lowercase (task-123.md,draft-5.md).Major changes:
src/utils/prefix-config.tswith comprehensive prefix utilities (normalizeId, generateNextId, buildGlobPattern, etc.)EntityTypeenum to support type-aware ID generation across tasks, drafts, docs, and decisionssrc/core/prefix-migration.ts) that renames legacytask-*.mdfiles in drafts folder todraft-*.md--task-prefixoption tobacklog initcommand (first-time only, read-only after)backlog/drafts/fromtask-Xtodraft-XformatTest coverage:
Issue found:
src/cli.ts:2149usesEntityType.Taskinstead ofEntityType.Draftfor draft creation, with an outdated TODO comment claiming file operations aren't ready (they are)Confidence Score: 4/5
src/cli.ts:2149- the draft creation logic uses wrong EntityType which will cause runtime behavior mismatchImportant Files Changed
Sequence Diagram
sequenceDiagram participant User participant CLI participant Core participant PrefixConfig participant FileSystem participant Migration Note over User,Migration: First-time initialization with custom prefix User->>CLI: backlog init --task-prefix JIRA CLI->>Core: initializeProject(config) Core->>FileSystem: ensureBacklogStructure() Core->>FileSystem: saveConfig({prefixes: {task: "JIRA", draft: "draft"}}) Core-->>CLI: Project initialized CLI-->>User: Success Note over User,Migration: Existing project load (migration check) User->>CLI: backlog task list CLI->>Core: ensureConfigLoaded() Core->>FileSystem: loadConfig() Core->>Migration: needsDraftPrefixMigration(config) alt No prefixes section in config Migration-->>Core: true Core->>Migration: migrateDraftPrefixes(fs) Migration->>FileSystem: listDrafts() FileSystem-->>Migration: [task-1.md, task-2.md] loop For each task-*.md file Migration->>FileSystem: parseTask(content) Migration->>PrefixConfig: generateNextId(existingIds, "draft") PrefixConfig-->>Migration: DRAFT-1 Migration->>FileSystem: saveDraft({id: "DRAFT-1", ...}) Migration->>FileSystem: unlink(task-1.md) end Migration->>FileSystem: saveConfig({prefixes: {...}}) else Has prefixes section Migration-->>Core: false end Note over User,Migration: Creating a draft User->>CLI: backlog draft create "New Feature" CLI->>Core: generateNextId(EntityType.Draft) Core->>PrefixConfig: getPrefixForType(Draft) PrefixConfig-->>Core: "draft" Core->>FileSystem: listDrafts() Core->>PrefixConfig: generateNextId(existingIds, "draft") PrefixConfig-->>Core: DRAFT-5 CLI->>Core: createDraft({id: "DRAFT-5", ...}) Core->>FileSystem: saveDraft(task) FileSystem->>PrefixConfig: normalizeId("DRAFT-5", "draft") PrefixConfig-->>FileSystem: DRAFT-5 FileSystem->>PrefixConfig: idForFilename("DRAFT-5") PrefixConfig-->>FileSystem: draft-5 FileSystem-->>Core: draft-5 - New Feature.md Core-->>User: Created draft DRAFT-5 Note over User,Migration: Promoting draft to task User->>CLI: backlog draft promote DRAFT-5 CLI->>Core: promoteDraft("DRAFT-5") Core->>FileSystem: loadDraft("DRAFT-5") FileSystem->>PrefixConfig: normalizeId("DRAFT-5", "draft") FileSystem-->>Core: {id: "DRAFT-5", ...} Core->>FileSystem: listTasks() Core->>PrefixConfig: generateNextId(existingIds, "JIRA") PrefixConfig-->>Core: JIRA-123 Core->>FileSystem: saveTask({id: "JIRA-123", ...}) FileSystem->>PrefixConfig: idForFilename("JIRA-123") PrefixConfig-->>FileSystem: jira-123 Core->>FileSystem: unlink(draft-5.md) Core-->>User: Promoted to JIRA-123