Artifact First-Class Implementation Quest Chain
Overview
This quest chain provides a step-by-step implementation plan for elevating artifacts to first-class citizens in the Bike4Mind system. Each quest builds upon the previous ones, with clear deliverables and testing criteria.
Quest Chain Structure
graph TD
Q1[Quest 1: Foundation - Types & Models]
Q2[Quest 2: Database Schema & Migration]
Q3[Quest 3: Core Service Layer]
Q4[Quest 4: API Endpoints]
Q5[Quest 5: Version Management]
Q6[Quest 6: UI Components - Version Dropdown]
Q7[Quest 7: UI Components - Preview Cards]
Q8[Quest 8: Knowledge Viewer Integration]
Q9[Quest 9: WebSocket Real-time Updates]
Q10[Quest 10: Migration & Backward Compatibility]
Q1 --> Q2
Q2 --> Q3
Q3 --> Q4
Q4 --> Q5
Q5 --> Q6
Q6 --> Q7
Q7 --> Q8
Q8 --> Q9
Q9 --> Q10
style Q1 fill:#e1f5fe
style Q2 fill:#e1f5fe
style Q3 fill:#e1f5fe
style Q4 fill:#fff3e0
style Q5 fill:#fff3e0
style Q6 fill:#f3e5f5
style Q7 fill:#f3e5f5
style Q8 fill:#f3e5f5
style Q9 fill:#e8f5e9
style Q10 fill:#e8f5e9
Quest 1: Foundation - Types & Models 🏗️
Objective: Establish the type system foundation for artifacts
Sub-tasks:
1.1 Create Base Artifact Types
File: b4m-core/packages/core/common/types/entities/ArtifactTypes.ts
// Add to existing file
export interface BaseArtifact {
id: string;
type: ArtifactType;
title: string;
description?: string;
metadata: ArtifactMetadata;
// Versioning
version: number;
versionTag?: string;
parentVersionId?: string;
// Timestamps
createdAt: Date;
updatedAt: Date;
publishedAt?: Date;
// Ownership & Access
userId: string;
projectId?: string;
organizationId?: string;
visibility: 'private' | 'project' | 'organization' | 'public';
permissions: ArtifactPermissions;
// Relationships
sourceQuestId?: string;
sessionId?: string;
parentArtifactId?: string;
// Status
status: ArtifactStatus;
tags: string[];
// Content
contentHash: string;
contentSize: number;
}
export enum ArtifactStatus {
DRAFT = 'draft',
REVIEW = 'review',
PUBLISHED = 'published',
ARCHIVED = 'archived',
DELETED = 'deleted'
}
1.2 Create Specific Artifact Types
File: b4m-core/packages/core/common/types/entities/QuestMasterArtifactTypes.ts
export interface QuestMasterArtifact extends BaseArtifact {
type: ArtifactType.QUESTMASTER;
content: {
goal: string;
quests: Quest[];
totalSteps: number;
estimatedDuration?: number;
complexity: 'low' | 'medium' | 'high';
category?: string;
prerequisites?: string[];
};
}
1.3 Create Zod Schemas
File: b4m-core/packages/core/common/schemas/artifacts.ts
import { z } from 'zod';
export const BaseArtifactSchema = z.object({
id: z.string().uuid(),
type: ArtifactTypeSchema,
title: z.string().min(1).max(255),
description: z.string().optional(),
// ... rest of schema
});
export const QuestMasterArtifactSchema = BaseArtifactSchema.extend({
type: z.literal('questmaster'),
content: QuestMasterContentSchema
});
Testing Criteria:
- All TypeScript types compile without errors
- Zod schemas validate sample data correctly
- Unit tests pass for type guards and validators
Deliverables:
- Complete type definitions in
b4m-core
- Zod schemas for validation
- Type guard functions
- Unit tests for types
Quest 2: Database Schema & Migration 🗄️
Objective: Implement MongoDB collections and migration scripts
Sub-tasks:
2.1 Create Mongoose Models
File: b4m-core/packages/core/models/Artifact.ts
import { Schema, model } from 'mongoose';
const ArtifactSchema = new Schema({
id: { type: String, required: true, unique: true },
type: { type: String, required: true, enum: Object.values(ArtifactType) },
title: { type: String, required: true },
description: String,
// Version info
version: { type: Number, default: 1 },
versionTag: String,
currentVersionId: { type: Schema.Types.ObjectId, ref: 'ArtifactVersion' },
// Timestamps
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
publishedAt: Date,
// Ownership
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
projectId: { type: Schema.Types.ObjectId, ref: 'Project' },
organizationId: { type: Schema.Types.ObjectId, ref: 'Organization' },
// Access control
visibility: {
type: String,
enum: ['private', 'project', 'organization', 'public'],
default: 'private'
},
permissions: {
canRead: [{ type: Schema.Types.ObjectId, ref: 'User' }],
canWrite: [{ type: Schema.Types.ObjectId, ref: 'User' }],
canDelete: [{ type: Schema.Types.ObjectId, ref: 'User' }],
isPublic: { type: Boolean, default: false },
inheritFromProject: { type: Boolean, default: true }
},
// Content reference
contentId: { type: Schema.Types.ObjectId, ref: 'ArtifactContent' },
contentHash: String,
contentSize: Number,
// Soft delete
deletedAt: Date
});
// Indexes
ArtifactSchema.index({ userId: 1, status: 1 });
ArtifactSchema.index({ type: 1, status: 1, visibility: 1 });
ArtifactSchema.index({ tags: 1 });
ArtifactSchema.index({ createdAt: -1 });
export const Artifact = model('Artifact', ArtifactSchema);
2.2 Create Migration Script
File: packages/client/server/migrations/001-create-artifact-collections.ts
export async function up(db: Db) {
// Create artifacts collection
await db.createCollection('artifacts');
// Create artifact_contents collection
await db.createCollection('artifact_contents');
// Create artifact_versions collection
await db.createCollection('artifact_versions');
// Create indexes
await db.collection('artifacts').createIndex({ userId: 1, status: 1 });
// ... more indexes
}
export async function down(db: Db) {
await db.dropCollection('artifacts');
await db.dropCollection('artifact_contents');
await db.dropCollection('artifact_versions');
}
Testing Criteria:
- Migration runs successfully on test database
- All indexes are created correctly
- Rollback works properly
- Performance queries execute within acceptable time
Deliverables:
- Mongoose models for all collections
- Migration scripts with up/down functions
- Index performance tests
- Sample data seeders
Quest 3: Core Service Layer 🛠️
Objective: Build the artifact service with CRUD operations
Sub-tasks:
3.1 Create Base Artifact Service
File: b4m-core/packages/core/services/ArtifactService.ts
export class ArtifactService {
constructor(
private db: DatabaseConnection,
private storage: StorageService,
private search: SearchService,
private cache: CacheService,
private events: EventService
) {}
async createArtifact(
data: CreateArtifactRequest,
userId: string
): Promise<Artifact> {
// Validate input
const validated = await this.validateArtifactData(data);
// Create artifact document
const artifact = await this.db.artifacts.create({
...validated,
id: generateUUID(),
version: 1,
userId,
createdAt: new Date(),
updatedAt: new Date(),
status: ArtifactStatus.DRAFT,
contentHash: await this.hashContent(validated.content),
contentSize: this.calculateSize(validated.content)
});
// Store content separately
await this.db.artifactContents.create({
artifactId: artifact.id,
content: validated.content
});
// Create initial version
await this.createVersion(artifact, 'Initial version', userId);
// Index for search
await this.search.indexArtifact(artifact);
// Emit event
await this.events.emit('artifact.created', { artifact, userId });
// Cache
await this.cache.set(`artifact:${artifact.id}`, artifact);
return artifact;
}
// ... other CRUD methods
}
3.2 Create Repository Pattern
File: b4m-core/packages/core/repositories/ArtifactRepository.ts
export class ArtifactRepository {
async findById(id: string): Promise<Artifact | null> {
return Artifact.findOne({ id, deletedAt: null });
}
async findByUser(userId: string, options?: FindOptions): Promise<Artifact[]> {
const query = { userId, deletedAt: null };
return this.applyOptions(Artifact.find(query), options);
}
async create(data: CreateArtifactData): Promise<Artifact> {
const artifact = new Artifact(data);
return artifact.save();
}
async update(id: string, updates: UpdateArtifactData): Promise<Artifact | null> {
return Artifact.findOneAndUpdate(
{ id, deletedAt: null },
{ ...updates, updatedAt: new Date() },
{ new: true }
);
}
async softDelete(id: string): Promise<boolean> {
const result = await Artifact.updateOne(
{ id, deletedAt: null },
{ deletedAt: new Date(), status: ArtifactStatus.DELETED }
);
return result.modifiedCount > 0;
}
}
Testing Criteria:
- All CRUD operations work correctly
- Validation catches invalid data
- Events are emitted properly
- Cache is updated correctly
- Unit tests cover all methods
Deliverables:
- Complete ArtifactService implementation
- Repository pattern implementation
- Service unit tests
- Integration tests with database
Quest 4: API Endpoints 🌐
Objective: Create RESTful API endpoints for artifact operations
Sub-tasks:
4.1 Create Artifact Controller
File: packages/client/pages/api/artifacts/index.ts
import { baseApi } from '@client/server/routers/base';
import { asyncHandler } from '@client/server/middleware/asyncHandler';
export default baseApi()
.get(asyncHandler(async (req, res) => {
const { type, status, search, page = 1, limit = 20 } = req.query;
const artifacts = await artifactService.listArtifacts({
filters: {
type: type ? [type as ArtifactType] : undefined,
status: status ? [status as ArtifactStatus] : undefined,
search: search as string,
userId: req.user.id
},
pagination: {
page: Number(page),
limit: Number(limit)
}
});
res.json({ success: true, data: artifacts });
}))
.post(asyncHandler(async (req, res) => {
const artifact = await artifactService.createArtifact(
req.body,
req.user.id
);
res.status(201).json({ success: true, data: artifact });
}));
4.2 Create Version Endpoints
File: packages/client/pages/api/artifacts/[id]/versions.ts
export default baseApi()
.get(asyncHandler(async (req, res) => {
const { id } = req.query;
const versions = await artifactService.getArtifactVersions(id as string);
res.json({ success: true, data: versions });
}))
.post(asyncHandler(async (req, res) => {
const { id } = req.query;
const newVersion = await artifactService.createNewVersion(
id as string,
req.body,
req.user.id
);
res.status(201).json({ success: true, data: newVersion });
}));
Testing Criteria:
- All endpoints return correct status codes
- Authentication/authorization works properly
- Validation errors are handled gracefully
- Pagination works correctly
- API tests cover all endpoints
Deliverables:
- Complete REST API implementation
- OpenAPI/Swagger documentation
- Postman collection
- API integration tests
Quest 5: Version Management 📚
Objective: Implement comprehensive version control system
Sub-tasks:
5.1 Create Version Service
File: b4m-core/packages/core/services/ArtifactVersionService.ts
export class ArtifactVersionService {
async createVersion(
artifact: Artifact,
changes: VersionChange[],
message: string,
userId: string
): Promise<ArtifactVersion> {
const version = await ArtifactVersion.create({
artifactId: artifact.id,
version: artifact.version + 1,
versionTag: message,
createdAt: new Date(),
createdBy: userId,
parentVersionId: artifact.currentVersionId,
contentSnapshot: artifact.content,
changes
});
// Update artifact's current version
artifact.version = version.version;
artifact.currentVersionId = version.id;
await artifact.save();
return version;
}
async compareVersions(
artifactId: string,
fromVersion: number,
toVersion: number
): Promise<VersionDiff> {
const from = await ArtifactVersion.findOne({ artifactId, version: fromVersion });
const to = await ArtifactVersion.findOne({ artifactId, version: toVersion });
const changes = this.calculateDiff(from.contentSnapshot, to.contentSnapshot);
return {
fromVersion,
toVersion,
changes,
summary: this.generateDiffSummary(changes)
};
}
private calculateDiff(from: any, to: any): VersionChange[] {
// Implement diff algorithm
return [];
}
}
5.2 Create Version UI Components
File: packages/client/app/components/Artifacts/VersionHistory.tsx
export const VersionHistory: React.FC<{ artifactId: string }> = ({ artifactId }) => {
const { data: versions } = useArtifactVersions(artifactId);
return (
<Timeline>
{versions?.map(version => (
<TimelineItem key={version.id}>
<TimelineSeparator>
<TimelineDot color={version.isCurrent ? 'primary' : 'grey'} />
<TimelineConnector />
</TimelineSeparator>
<TimelineContent>
<Typography level="title-sm">
Version {version.version}
{version.versionTag && ` - ${version.versionTag}`}
</Typography>
<Typography level="body-xs">
{formatRelativeTime(version.createdAt)} by {version.createdBy}
</Typography>
<Button size="sm" onClick={() => restoreVersion(version.version)}>
Restore
</Button>
</TimelineContent>
</TimelineItem>
))}
</Timeline>
);
};
Testing Criteria:
- Version creation works correctly
- Version comparison shows accurate diffs
- Version restoration works properly
- UI displays version history correctly
Deliverables:
- Version service implementation
- Diff algorithm implementation
- Version history UI component
- Version comparison UI
Quest 6: UI Components - Version Dropdown 🎨
Objective: Build the version dropdown component
Sub-tasks:
6.1 Create Version Dropdown Component
File: packages/client/app/components/Artifacts/VersionDropdown.tsx
interface VersionDropdownProps {
artifact: Artifact;
currentVersion: number;
onVersionChange: (version: number) => void;
showDiff?: boolean;
}
export const VersionDropdown: React.FC<VersionDropdownProps> = ({
artifact,
currentVersion,
onVersionChange,
showDiff = false
}) => {
const { data: versions } = useArtifactVersions(artifact.id);
const [compareMode, setCompareMode] = useState(false);
const [compareVersion, setCompareVersion] = useState<number | null>(null);
return (
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<Select
value={currentVersion}
onChange={(_, value) => onVersionChange(value as number)}
startDecorator={<HistoryIcon />}
endDecorator={
<Chip size="sm" variant="soft">
v{currentVersion}
</Chip>
}
>
{versions?.map(version => (
<Option key={version.version} value={version.version}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<Typography level="body-sm">
Version {version.version}
{version.versionTag && ` - ${version.versionTag}`}
</Typography>
<Typography level="body-xs" sx={{ color: 'text.secondary' }}>
{formatRelativeTime(version.createdAt)}
</Typography>
</Box>
</Option>
))}
</Select>
{showDiff && (
<IconButton
size="sm"
variant={compareMode ? 'solid' : 'plain'}
onClick={() => setCompareMode(!compareMode)}
>
<CompareIcon />
</IconButton>
)}
</Box>
);
};
6.2 Create Version Diff Viewer
File: packages/client/app/components/Artifacts/VersionDiffViewer.tsx
export const VersionDiffViewer: React.FC<{
artifactId: string;
fromVersion: number;
toVersion: number;
}> = ({ artifactId, fromVersion, toVersion }) => {
const { data: diff } = useVersionDiff(artifactId, fromVersion, toVersion);
return (
<Box>
<Typography level="title-md">
Changes from v{fromVersion} to v{toVersion}
</Typography>
<Stack spacing={2}>
{diff?.changes.map((change, index) => (
<DiffItem key={index} change={change} />
))}
</Stack>
</Box>
);
};
Testing Criteria:
- Dropdown displays all versions correctly
- Version selection updates artifact view
- Compare mode works properly
- Diff viewer shows accurate changes
Deliverables:
- Version dropdown component
- Version diff viewer
- Storybook stories
- Component tests
Quest 7: UI Components - Preview Cards 🃏
Objective: Create artifact preview cards with version support
Sub-tasks:
7.1 Create Artifact Preview Card
File: packages/client/app/components/Artifacts/ArtifactPreviewCard.tsx
export const ArtifactPreviewCard: React.FC<ArtifactPreviewCardProps> = ({
artifact,
onClick,
showVersions = true,
showActions = true
}) => {
const [currentVersion, setCurrentVersion] = useState(artifact.version);
const { data: versionData } = useArtifactVersion(artifact.id, currentVersion);
return (
<Card
variant="outlined"
sx={{
cursor: onClick ? 'pointer' : 'default',
transition: 'all 0.2s',
'&:hover': onClick ? {
transform: 'translateY(-2px)',
boxShadow: 'md'
} : {}
}}
onClick={onClick}
>
<CardContent>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
<Stack direction="row" spacing={1} alignItems="center">
{getArtifactIcon(artifact.type)}
<Typography level="h4">{artifact.title}</Typography>
</Stack>
{showVersions && (
<VersionDropdown
artifact={artifact}
currentVersion={currentVersion}
onVersionChange={setCurrentVersion}
/>
)}
</Box>
<Box sx={{ mb: 2 }}>
{getPreviewContent(artifact, versionData)}
</Box>
{showActions && (
<Stack direction="row" spacing={1} justifyContent="flex-end">
<IconButton size="sm" variant="plain">
<ShareIcon />
</IconButton>
<IconButton size="sm" variant="plain">
<EditIcon />
</IconButton>
</Stack>
)}
</CardContent>
</Card>
);
};
7.2 Create Artifact Grid View
File: packages/client/app/components/Artifacts/ArtifactGrid.tsx
export const ArtifactGrid: React.FC<{
artifacts: Artifact[];
onArtifactClick: (artifact: Artifact) => void;
}> = ({ artifacts, onArtifactClick }) => {
return (
<Grid container spacing={2}>
{artifacts.map(artifact => (
<Grid key={artifact.id} xs={12} sm={6} md={4} lg={3}>
<ArtifactPreviewCard
artifact={artifact}
onClick={() => onArtifactClick(artifact)}
/>
</Grid>
))}
</Grid>
);
};
Testing Criteria:
- Preview cards render all artifact types correctly
- Version dropdown works within cards
- Actions trigger appropriate callbacks
- Grid layout is responsive
Deliverables:
- Artifact preview card component
- Artifact grid component
- Type-specific preview renderers
- Visual regression tests
Quest 8: Knowledge Viewer Integration 🔗
Objective: Integrate artifact system with existing Knowledge Viewer
Sub-tasks:
8.1 Update Knowledge Viewer
File: packages/client/app/components/Knowledge/KnowledgeViewer.tsx
const KnowledgeViewer: React.FC = () => {
const { currentSession } = useSessions();
const [selectedArtifact, setSelectedArtifact] = useState<Artifact | null>(null);
const [selectedVersion, setSelectedVersion] = useState<number | null>(null);
// Fetch artifacts for current session
const { data: artifacts } = useArtifacts({
filters: {
sessionId: currentSession?.id,
status: [ArtifactStatus.PUBLISHED, ArtifactStatus.DRAFT]
},
sort: { field: 'updatedAt', order: 'desc' }
});
return (
<Stack sx={{ height: '100%' }}>
{/* Artifact selector */}
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
<Select
placeholder="Select an artifact..."
value={selectedArtifact?.id}
onChange={(_, value) => {
const artifact = artifacts?.find(a => a.id === value);
if (artifact) setSelectedArtifact(artifact);
}}
>
{artifacts?.map(artifact => (
<Option key={artifact.id} value={artifact.id}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{getArtifactIcon(artifact.type)}
<Typography>{artifact.title}</Typography>
<Chip size="sm" variant="soft">
v{artifact.version}
</Chip>
</Box>
</Option>
))}
</Select>
</Box>
{/* Version selector */}
{selectedArtifact && (
<Box sx={{ p: 2, borderBottom: 1, borderColor: 'divider' }}>
<VersionDropdown
artifact={selectedArtifact}
currentVersion={selectedVersion || selectedArtifact.version}
onVersionChange={setSelectedVersion}
showDiff
/>
</Box>
)}
{/* Content viewer */}
<Box sx={{ flex: 1, overflow: 'auto', p: 2 }}>
{selectedArtifact && selectedVersion && (
<ArtifactViewer
artifact={selectedArtifact}
version={selectedVersion}
onSave={handleSaveArtifact}
editable={selectedVersion === selectedArtifact.version}
/>
)}
</Box>
</Stack>
);
};
8.2 Create Artifact Creation Flow
File: packages/client/app/components/Artifacts/CreateArtifactDialog.tsx
export const CreateArtifactDialog: React.FC<{
open: boolean;
onClose: () => void;
onCreated: (artifact: Artifact) => void;
initialContent?: any;
initialType?: ArtifactType;
}> = ({ open, onClose, onCreated, initialContent, initialType }) => {
const [type, setType] = useState<ArtifactType>(initialType || ArtifactType.CODE);
const [title, setTitle] = useState('');
const [description, setDescription] = useState('');
const handleCreate = async () => {
const artifact = await createArtifact({
type,
title,
description,
content: initialContent || getDefaultContent(type)
});
onCreated(artifact);
onClose();
};
return (
<Modal open={open} onClose={onClose}>
<ModalDialog>
<DialogTitle>Create New Artifact</DialogTitle>
<DialogContent>
<Stack spacing={2}>
<FormControl>
<FormLabel>Type</FormLabel>
<Select value={type} onChange={(_, value) => setType(value as ArtifactType)}>
{Object.values(ArtifactType).map(t => (
<Option key={t} value={t}>{t}</Option>
))}
</Select>
</FormControl>
<FormControl>
<FormLabel>Title</FormLabel>
<Input value={title} onChange={e => setTitle(e.target.value)} />
</FormControl>
<FormControl>
<FormLabel>Description</FormLabel>
<Textarea value={description} onChange={e => setDescription(e.target.value)} />
</FormControl>
</Stack>
</DialogContent>
<DialogActions>
<Button variant="plain" onClick={onClose}>Cancel</Button>
<Button onClick={handleCreate} disabled={!title}>Create</Button>
</DialogActions>
</ModalDialog>
</Modal>
);
};
Testing Criteria:
- Knowledge Viewer displays artifacts correctly
- Version switching works seamlessly
- Save functionality creates new versions
- Creation flow works for all artifact types
Deliverables:
- Updated Knowledge Viewer
- Artifact creation dialog
- Integration with existing viewers
- E2E tests
Quest 9: WebSocket Real-time Updates 🔄
Objective: Implement real-time collaboration features
Sub-tasks:
9.1 Create WebSocket Events
File: b4m-core/packages/core/common/schemas/artifactActions.ts
export const ArtifactCreatedAction = z.object({
action: z.literal('artifact.created'),
artifact: ArtifactSchema,
userId: z.string()
});
export const ArtifactUpdatedAction = z.object({
action: z.literal('artifact.updated'),
artifactId: z.string(),
changes: z.array(VersionChangeSchema),
userId: z.string()
});
export const QuestUpdatedAction = z.object({
action: z.literal('artifact.quest.updated'),
artifactId: z.string(),
questId: z.string(),
updates: z.record(z.unknown()),
userId: z.string()
});
9.2 Create Real-time Hook
File: packages/client/app/hooks/useArtifactSubscription.ts
export const useArtifactSubscription = (artifactId: string) => {
const queryClient = useQueryClient();
const { subscribeToAction } = useWebsocket();
useEffect(() => {
const unsubscribes = [
// Subscribe to artifact updates
subscribeToAction('artifact.updated', async (msg) => {
if (msg.artifactId === artifactId) {
queryClient.invalidateQueries(['artifact', artifactId]);
}
}),
// Subscribe to version updates
subscribeToAction('artifact.version.created', async (msg) => {
if (msg.artifactId === artifactId) {
queryClient.invalidateQueries(['artifact-versions', artifactId]);
}
}),
// Subscribe to quest updates for QuestMaster artifacts
subscribeToAction('artifact.quest.updated', async (msg) => {
if (msg.artifactId === artifactId) {
queryClient.setQueryData(
['artifact', artifactId],
(old: Artifact) => updateQuestInArtifact(old, msg.questId, msg.updates)
);
}
})
];
return () => {
unsubscribes.forEach(unsubscribe => unsubscribe());
};
}, [artifactId, subscribeToAction, queryClient]);
};
Testing Criteria:
- Real-time updates work across multiple clients
- Quest status updates propagate correctly
- Version changes are reflected immediately
- No race conditions or conflicts
Deliverables:
- WebSocket event definitions
- Real-time subscription hooks
- Collaborative editing support
- Integration tests
Quest 10: Migration & Backward Compatibility 🔄
Objective: Migrate existing artifacts and ensure backward compatibility
Sub-tasks:
10.1 Create Migration Script
File: packages/client/server/migrations/002-migrate-existing-artifacts.ts
export async function up(db: Db) {
// Find all sessions with artifacts
const sessions = await db.collection('sessions').find({
'chatHistory.replies': { $regex: '<artifact' }
}).toArray();
for (const session of sessions) {
for (const quest of session.chatHistory) {
if (!quest.replies) continue;
for (const reply of quest.replies) {
const artifacts = parseArtifacts(reply);
for (const artifact of artifacts) {
// Create artifact document
await db.collection('artifacts').insertOne({
id: artifact.identifier || generateUUID(),
type: mapToArtifactType(artifact.type),
title: artifact.title,
content: artifact.content,
sourceQuestId: quest.id,
sessionId: session.id,
userId: session.userId,
createdAt: quest.timestamp,
updatedAt: quest.timestamp,
version: 1,
status: 'published'
});
// Replace artifact content with reference
reply = reply.replace(
artifact.fullMatch,
`<artifact-ref id="${artifact.id}" />`
);
}
// Update quest reply
await db.collection('sessions').updateOne(
{ _id: session._id, 'chatHistory.id': quest.id },
{ $set: { 'chatHistory.$.reply': reply } }
);
}
}
}
}
10.2 Create Compatibility Layer
File: packages/client/app/utils/artifactCompatibility.ts
export const getArtifactFromLegacy = async (
questId: string,
artifactId: string
): Promise<Artifact | null> => {
// First try to get from new artifact system
const artifact = await artifactService.getArtifact(artifactId);
if (artifact) return artifact;
// Fallback to parsing from quest reply
const quest = await questService.getQuest(questId);
if (!quest?.reply) return null;
const { artifacts } = parseArtifacts(quest.reply);
const legacyArtifact = artifacts.find(a => a.identifier === artifactId);
if (!legacyArtifact) return null;
// Convert to new format
return {
id: artifactId,
type: mapToArtifactType(legacyArtifact.type),
title: legacyArtifact.title,
content: legacyArtifact.content,
version: 1,
status: ArtifactStatus.PUBLISHED,
createdAt: quest.timestamp,
updatedAt: quest.timestamp,
userId: quest.userId,
sessionId: quest.sessionId
};
};
Testing Criteria:
- Migration successfully converts all existing artifacts
- No data loss during migration
- Backward compatibility works for old sessions
- Performance is acceptable for large datasets
Deliverables:
- Migration scripts with progress tracking
- Compatibility layer for legacy artifacts
- Rollback procedures
- Migration documentation
Implementation Timeline
Phase 1: Foundation (Quests 1-3) - 2 weeks
- Week 1: Types, schemas, and database models
- Week 2: Core service implementation
Phase 2: API Layer (Quests 4-5) - 2 weeks
- Week 3: REST API endpoints
- Week 4: Version management system
Phase 3: UI Components (Quests 6-8) - 3 weeks
- Week 5: Version dropdown and diff viewer
- Week 6: Preview cards and grid
- Week 7: Knowledge Viewer integration
Phase 4: Advanced Features (Quests 9-10) - 2 weeks
- Week 8: WebSocket real-time updates
- Week 9: Migration and compatibility
Total Timeline: 9 weeks
Success Metrics
-
Performance
- Artifact loading < 200ms
- Version switching < 100ms
- Search results < 500ms
-
Reliability
- 99.9% uptime for artifact service
- Zero data loss during migrations
- Successful rollback capability
-
User Experience
- Intuitive version management
- Seamless Knowledge Viewer integration
- Real-time collaboration features
-
Developer Experience
- Clear API documentation
- Comprehensive test coverage (>80%)
- Easy extension for new artifact types
Risk Mitigation
-
Data Migration Risks
- Create comprehensive backups
- Test on staging environment first
- Implement gradual rollout
-
Performance Risks
- Implement caching early
- Use pagination for large datasets
- Monitor query performance
-
Compatibility Risks
- Maintain legacy support for 6 months
- Provide clear migration guides
- Implement feature flags
This quest chain provides a structured, incremental approach to implementing artifacts as first-class citizens, with clear deliverables and testing criteria at each step.