Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/server/src/schema/neo4j-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const typeDefs = gql`
enum NodeType {
# Legacy types (for backward compatibility)
OUTCOME

# Strategic Planning
EPIC
INITIATIVE
Expand Down Expand Up @@ -200,6 +200,7 @@ export const typeDefs = gql`
VALIDATES
REFERENCES
CONTAINS
SATISFIES
}

enum ContributorType {
Expand Down
28 changes: 28 additions & 0 deletions packages/web/src/constants/__tests__/ontology.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { describe, it, expect } from 'vitest';
import {
isValidWorkItemType, isValidRelationshipType, getTypeConfig, getRelationshipConfig,
WORK_ITEM_TYPES, RELATIONSHIP_TYPES,
} from '../workItemConstants';

describe('ontology layer: REQUIREMENT type + SATISFIES edge (#27)', () => {
it('REQUIREMENT is a valid work item type with a config', () => {
expect(isValidWorkItemType('REQUIREMENT')).toBe(true);
const cfg = getTypeConfig('REQUIREMENT' as any);
expect(cfg.label).toBe('Requirement');
expect(cfg.value).toBe('REQUIREMENT');
expect(cfg.hexColor).toMatch(/^#/);
expect(WORK_ITEM_TYPES.REQUIREMENT).toBeTruthy();
});

it('SATISFIES is a valid relationship type with a config', () => {
expect(isValidRelationshipType('SATISFIES')).toBe(true);
const cfg = getRelationshipConfig('SATISFIES' as any);
expect(cfg.label).toBe('Satisfies');
expect(cfg.type).toBe('SATISFIES');
expect(RELATIONSHIP_TYPES.SATISFIES).toBeTruthy();
});

it('isValidRelationshipType rejects unknown types', () => {
expect(isValidRelationshipType('NONSENSE')).toBe(false);
});
});
25 changes: 24 additions & 1 deletion packages/web/src/constants/workItemConstants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ export {
// ============================

// Work item types define what kind of work this represents
export type WorkItemType = 'EPIC' | 'MILESTONE' | 'OUTCOME' | 'FEATURE' | 'TASK' | 'BUG' | 'IDEA' | 'RESEARCH' | 'DEFAULT';
export type WorkItemType = 'EPIC' | 'MILESTONE' | 'OUTCOME' | 'FEATURE' | 'TASK' | 'BUG' | 'IDEA' | 'RESEARCH' | 'REQUIREMENT' | 'DEFAULT';

// Work item statuses represent the current state of progress (9 total statuses)
// NOT_STARTED is the default status for new items
Expand Down Expand Up @@ -165,6 +165,16 @@ export const WORK_ITEM_TYPES: Record<WorkItemType, TypeOption> = {
borderColor: 'border-gray-500/30',
hexColor: '#9ca3af'
},
REQUIREMENT: {
value: 'REQUIREMENT',
label: 'Requirement',
description: 'A requirement that tasks satisfy (ontology layer)',
icon: ClipboardList,
color: 'text-sky-400',
bgColor: 'bg-sky-400/10',
borderColor: 'border-sky-400/30',
hexColor: '#38bdf8'
},
EPIC: {
value: 'EPIC',
label: 'Epic',
Expand Down Expand Up @@ -620,6 +630,7 @@ export type RelationshipType =
| 'VALIDATES' // Source tests/validates target
| 'REFERENCES' // Source references/cites target
| 'CONTAINS' // Source contains/encompasses target
| 'SATISFIES' // Source (task) satisfies target (requirement) — ontology layer
| 'DEFAULT_EDGE'; // Generic connection

export interface RelationshipOption {
Expand All @@ -640,6 +651,14 @@ export const RELATIONSHIP_TYPES: Record<RelationshipType, RelationshipOption> =
color: 'text-gray-400',
hexColor: '#9ca3af'
},
SATISFIES: {
type: 'SATISFIES',
label: 'Satisfies',
description: 'Source task satisfies the target requirement (ontology layer)',
icon: CheckCircle,
color: 'text-sky-400',
hexColor: '#38bdf8'
},
DEPENDS_ON: {
type: 'DEPENDS_ON',
label: 'Depends On',
Expand Down Expand Up @@ -743,6 +762,10 @@ export const RELATIONSHIP_TYPES: Record<RelationshipType, RelationshipOption> =
// ============================

// Get relationship configuration
export const isValidRelationshipType = (type: string): type is RelationshipType => {
return Object.keys(RELATIONSHIP_TYPES).includes(type as RelationshipType);
};

export const getRelationshipConfig = (type: RelationshipType): RelationshipOption => {
return RELATIONSHIP_TYPES[type] || RELATIONSHIP_TYPES.RELATES_TO;
};
Expand Down
Loading