mirror of
https://github.com/BradGroux/veritas-kanban.git
synced 2026-10-10 21:57:53 +00:00
fix: infinite recursion in project/tag service init, ID mismatch in seed migration
- ProjectService.init() had infinite recursion: list() → init() → seed → list() → init()... - TagService seed checked file existence (always true after super.init creates it) - Seed migrations now use seedItem() with raw project/tag strings as IDs so they match existing task.project and task.tags values - Added seedItem() to ManagedListService base for custom-ID seeding - Cleaned up task-types.json (removed duplicate, restored Research)
This commit is contained in:
parent
93345980d6
commit
d28cf29598
6 changed files with 82 additions and 34 deletions
|
|
@ -1 +1,34 @@
|
|||
[]
|
||||
[
|
||||
{
|
||||
"id": "Sprint 8",
|
||||
"label": "Sprint 8",
|
||||
"color": "bg-blue-500/20",
|
||||
"order": 0,
|
||||
"created": "2026-01-28T04:30:34.608Z",
|
||||
"updated": "2026-01-28T04:30:34.608Z"
|
||||
},
|
||||
{
|
||||
"id": "brain",
|
||||
"label": "brain",
|
||||
"color": "bg-green-500/20",
|
||||
"order": 1,
|
||||
"created": "2026-01-28T04:30:34.608Z",
|
||||
"updated": "2026-01-28T04:30:34.608Z"
|
||||
},
|
||||
{
|
||||
"id": "platform",
|
||||
"label": "platform",
|
||||
"color": "bg-purple-500/20",
|
||||
"order": 2,
|
||||
"created": "2026-01-28T04:30:34.608Z",
|
||||
"updated": "2026-01-28T04:30:34.608Z"
|
||||
},
|
||||
{
|
||||
"id": "veritas-kanban",
|
||||
"label": "veritas-kanban",
|
||||
"color": "bg-orange-500/20",
|
||||
"order": 3,
|
||||
"created": "2026-01-28T04:30:34.608Z",
|
||||
"updated": "2026-01-28T04:30:34.608Z"
|
||||
}
|
||||
]
|
||||
|
|
@ -1 +1,10 @@
|
|||
[]
|
||||
[
|
||||
{
|
||||
"id": "feature",
|
||||
"label": "feature",
|
||||
"color": "bg-blue-500/20 text-blue-400",
|
||||
"order": 0,
|
||||
"created": "2026-01-28T04:30:34.607Z",
|
||||
"updated": "2026-01-28T04:30:34.607Z"
|
||||
}
|
||||
]
|
||||
|
|
@ -4,21 +4,20 @@
|
|||
"label": "Code",
|
||||
"icon": "Code",
|
||||
"color": "border-l-violet-500",
|
||||
"order": 0,
|
||||
"order": 1,
|
||||
"isDefault": true,
|
||||
"created": "2026-01-28T04:03:21.782Z",
|
||||
"updated": "2026-01-28T04:03:21.782Z"
|
||||
"updated": "2026-01-28T04:27:49.587Z"
|
||||
},
|
||||
{
|
||||
"id": "research",
|
||||
"label": "Research",
|
||||
"icon": "Search",
|
||||
"icon": "BookOpen",
|
||||
"color": "border-l-cyan-500",
|
||||
"order": 1,
|
||||
"order": 3,
|
||||
"isDefault": true,
|
||||
"created": "2026-01-28T04:03:21.782Z",
|
||||
"updated": "2026-01-28T04:10:37.498Z",
|
||||
"isHidden": true
|
||||
"updated": "2026-01-28T04:27:49.587Z"
|
||||
},
|
||||
{
|
||||
"id": "content",
|
||||
|
|
@ -28,16 +27,16 @@
|
|||
"order": 2,
|
||||
"isDefault": true,
|
||||
"created": "2026-01-28T04:03:21.782Z",
|
||||
"updated": "2026-01-28T04:03:21.782Z"
|
||||
"updated": "2026-01-28T04:27:49.587Z"
|
||||
},
|
||||
{
|
||||
"id": "automation",
|
||||
"label": "Automation",
|
||||
"icon": "Zap",
|
||||
"color": "border-l-emerald-500",
|
||||
"order": 3,
|
||||
"order": 0,
|
||||
"isDefault": true,
|
||||
"created": "2026-01-28T04:03:21.782Z",
|
||||
"updated": "2026-01-28T04:03:21.782Z"
|
||||
"updated": "2026-01-28T04:27:49.587Z"
|
||||
}
|
||||
]
|
||||
|
|
@ -125,6 +125,16 @@ export class ManagedListService<T extends ManagedListItem> {
|
|||
return newItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Seed a pre-built item with a specific ID (for migrations)
|
||||
* Skips ID generation — caller provides the full item
|
||||
*/
|
||||
async seedItem(item: T): Promise<T> {
|
||||
this.items.push(item);
|
||||
await this.save();
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing item
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { resolve } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import type { ProjectConfig } from '@veritas-kanban/shared';
|
||||
import { ManagedListService } from './managed-list-service.js';
|
||||
import { TaskService } from './task-service.js';
|
||||
|
|
@ -43,14 +42,15 @@ export class ProjectService extends ManagedListService<ProjectConfig> {
|
|||
* Initialize service and perform seed migration if needed
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
// Prevent re-entrant init (list() calls init(), seed calls list())
|
||||
if (this.seeded) return;
|
||||
this.seeded = true;
|
||||
|
||||
// Call parent init first
|
||||
await super.init();
|
||||
|
||||
// Seed projects from existing tasks on first run
|
||||
if (!this.seeded) {
|
||||
await this.seedProjectsFromTasks();
|
||||
this.seeded = true;
|
||||
}
|
||||
await this.seedProjectsFromTasks();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -82,21 +82,23 @@ export class ProjectService extends ManagedListService<ProjectConfig> {
|
|||
});
|
||||
|
||||
// Create ProjectConfig entries for each unique project
|
||||
// Use the raw project string as the ID for backward compatibility
|
||||
// (tasks store project as a plain string that must match the project ID)
|
||||
const projectArray = Array.from(projectStrings).sort();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
for (let i = 0; i < projectArray.length; i++) {
|
||||
const projectName = projectArray[i];
|
||||
const color = PROJECT_COLORS[i % PROJECT_COLORS.length];
|
||||
|
||||
// Create the project using the project name as both id and label
|
||||
const now = new Date().toISOString();
|
||||
await this.create({
|
||||
await this.seedItem({
|
||||
id: projectName, // Must match existing task.project values
|
||||
label: projectName,
|
||||
color,
|
||||
order: i,
|
||||
created: now,
|
||||
updated: now,
|
||||
} as any);
|
||||
} as ProjectConfig);
|
||||
}
|
||||
|
||||
console.log(`✅ Seeded ${projectArray.length} projects from existing tasks`);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { resolve } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
import type { TagConfig } from '@veritas-kanban/shared';
|
||||
import { ManagedListService } from './managed-list-service.js';
|
||||
import { TaskService } from './task-service.js';
|
||||
|
|
@ -42,16 +41,15 @@ export class TagService extends ManagedListService<TagConfig> {
|
|||
* Initialize and seed tags from existing tasks if first run
|
||||
*/
|
||||
async init(): Promise<void> {
|
||||
await super.init();
|
||||
|
||||
// Only seed once
|
||||
// Prevent re-entrant init (list() calls init(), seed calls list())
|
||||
if (this.seeded) return;
|
||||
this.seeded = true;
|
||||
|
||||
const filePath = resolve(process.cwd(), '..', '.veritas-kanban', 'tags.json');
|
||||
await super.init();
|
||||
|
||||
// If file doesn't exist, we need to seed from existing tasks
|
||||
if (!existsSync(filePath)) {
|
||||
// Seed from existing tasks if no tags exist yet
|
||||
const existing = await this.list(true);
|
||||
if (existing.length === 0) {
|
||||
await this.seedFromExistingTasks();
|
||||
}
|
||||
}
|
||||
|
|
@ -75,22 +73,19 @@ export class TagService extends ManagedListService<TagConfig> {
|
|||
// Create TagConfig entries for each unique tag
|
||||
const tagArray = Array.from(uniqueTags).sort();
|
||||
|
||||
const now = new Date().toISOString();
|
||||
for (let i = 0; i < tagArray.length; i++) {
|
||||
const tagString = tagArray[i];
|
||||
const color = TAG_COLORS[i % TAG_COLORS.length];
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const newTag: TagConfig = {
|
||||
id: tagString, // Use tag string as ID for backward compatibility
|
||||
await this.seedItem({
|
||||
id: tagString, // Must match existing task.tags values
|
||||
label: tagString,
|
||||
color,
|
||||
order: i,
|
||||
created: now,
|
||||
updated: now,
|
||||
};
|
||||
|
||||
// Add to items array directly since we're seeding
|
||||
await this.create({ label: tagString, color } as any);
|
||||
} as TagConfig);
|
||||
}
|
||||
|
||||
console.log(`Seeded ${tagArray.length} tags from existing tasks`);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue