Skip to content

TASK-345 - Configurable ID prefix system (task-, custom-)#470

Merged
MrLesk merged 27 commits intomainfrom
tasks/task-345-draft-prefix
Jan 15, 2026
Merged

TASK-345 - Configurable ID prefix system (task-, custom-)#470
MrLesk merged 27 commits intomainfrom
tasks/task-345-draft-prefix

Conversation

@MrLesk
Copy link
Copy Markdown
Owner

@MrLesk MrLesk commented Jan 6, 2026

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 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)

Confidence Score: 4/5

  • 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

Important Files Changed

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

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
Loading

MrLesk added 23 commits January 3, 2026 20:49
- 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
Copy link
Copy Markdown

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

90 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment thread src/cli.ts Outdated
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/utils/task-path.ts Outdated
Comment thread src/file-system/operations.ts
MrLesk and others added 4 commits January 13, 2026 19:56
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.
@MrLesk MrLesk merged commit 072421c into main Jan 15, 2026
11 checks passed
@MrLesk MrLesk deleted the tasks/task-345-draft-prefix branch January 15, 2026 19:04
@MrLesk MrLesk changed the title TASK-345 - Configurable ID prefix system (task-, draft-, custom) TASK-345 - Configurable ID prefix system (task-, custom-) Jan 15, 2026
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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant