diff --git a/packages/server/src/schema/neo4j-schema.ts b/packages/server/src/schema/neo4j-schema.ts index f05642ce..f558cac1 100644 --- a/packages/server/src/schema/neo4j-schema.ts +++ b/packages/server/src/schema/neo4j-schema.ts @@ -5,7 +5,7 @@ export const typeDefs = gql` enum NodeType { # Legacy types (for backward compatibility) OUTCOME - + # Strategic Planning EPIC INITIATIVE @@ -200,6 +200,7 @@ export const typeDefs = gql` VALIDATES REFERENCES CONTAINS + SATISFIES } enum ContributorType { diff --git a/packages/web/src/constants/__tests__/ontology.test.ts b/packages/web/src/constants/__tests__/ontology.test.ts new file mode 100644 index 00000000..d117653c --- /dev/null +++ b/packages/web/src/constants/__tests__/ontology.test.ts @@ -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); + }); +}); diff --git a/packages/web/src/constants/workItemConstants.tsx b/packages/web/src/constants/workItemConstants.tsx index c738f1ed..028ab07a 100644 --- a/packages/web/src/constants/workItemConstants.tsx +++ b/packages/web/src/constants/workItemConstants.tsx @@ -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 @@ -165,6 +165,16 @@ export const WORK_ITEM_TYPES: Record = { 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', @@ -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 { @@ -640,6 +651,14 @@ export const RELATIONSHIP_TYPES: Record = 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', @@ -743,6 +762,10 @@ export const RELATIONSHIP_TYPES: Record = // ============================ // 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; };