Migrate all repos into monorepo context folders

Bahn: aisupport, Analyse-O2C-C2S, awesome-bahn-mcp-servers, beam-mcp,
      Confluence_Bot, db-planet-mcp-server, O2C-Harness, project-audit,
      Projekt-KIQ-HP, teamlandkarte-mcp
Dhive: Jury-Voting
Privat: CV, NoteGraph (NOTE: NoteGraph needs complete redo after consolidation)
Shared: AI-Orchestrator, OrgMyLife, power_skills_and_more
Shared/references: symphony (read-only)

Bahn repos remain available as independent remotes - this monorepo
pulls them in via subtree, the originals are untouched.
This commit is contained in:
2026-06-30 20:39:52 +02:00
parent 2f2b295531
commit a5f8fb49ab
1717 changed files with 447332 additions and 0 deletions
+338
View File
@@ -0,0 +1,338 @@
import { describe, it, expect } from 'vitest';
import fc from 'fast-check';
import { selectRelevant } from './relevance-selection';
import { generateTandemData } from './tandem-generator';
import type { Experience, Skill, Project, Person, Tandem } from '../schemas/types';
// --- Arbitraries ---
const kebabCaseId = fc
.array(
fc.stringMatching(/^[a-z0-9]{1,8}$/),
{ minLength: 1, maxLength: 4 }
)
.map(parts => parts.join('-'));
const isoDate = fc
.record({
year: fc.integer({ min: 1990, max: 2030 }),
month: fc.integer({ min: 1, max: 12 }),
})
.map(({ year, month }) => `${year}-${String(month).padStart(2, '0')}`);
const skillCategory = fc.constantFrom(
'programming-language' as const,
'framework' as const,
'methodology' as const,
'soft-skill' as const,
'domain' as const
);
const skillLevel = fc.constantFrom(
'beginner' as const,
'intermediate' as const,
'advanced' as const,
'expert' as const
);
/** Generates a Skill entity */
const skillArb = fc.record({
id: kebabCaseId,
created: isoDate,
modified: isoDate,
name: fc.string({ minLength: 1, maxLength: 20 }).filter(s => s.trim().length > 0),
category: skillCategory,
level: skillLevel,
}).map(f => ({
id: f.id,
type: 'skill' as const,
created: f.created,
modified: f.modified,
name: f.name,
category: f.category,
level: f.level,
}));
/** Generates an Experience entity */
const experienceArb = fc.record({
id: kebabCaseId,
created: isoDate,
modified: isoDate,
title: fc.string({ minLength: 1, maxLength: 30 }).filter(s => s.trim().length > 0),
organization: kebabCaseId,
start: isoDate,
description: fc.string({ minLength: 0, maxLength: 50 }),
skillsUsed: fc.array(fc.string({ minLength: 1, maxLength: 15 }).filter(s => s.trim().length > 0), { minLength: 0, maxLength: 5 }),
}).map(f => ({
id: f.id,
type: 'experience' as const,
created: f.created,
modified: f.modified,
title: f.title,
organization: f.organization,
start: f.start,
end: null,
description: f.description,
skillsUsed: f.skillsUsed,
}));
/** Generates a Project entity */
const projectArb = fc.record({
id: kebabCaseId,
created: isoDate,
modified: isoDate,
name: fc.string({ minLength: 1, maxLength: 30 }).filter(s => s.trim().length > 0),
organization: kebabCaseId,
start: isoDate,
description: fc.string({ minLength: 0, maxLength: 50 }),
skillsUsed: fc.array(fc.string({ minLength: 1, maxLength: 15 }).filter(s => s.trim().length > 0), { minLength: 0, maxLength: 5 }),
}).map(f => ({
id: f.id,
type: 'project' as const,
created: f.created,
modified: f.modified,
name: f.name,
organization: f.organization,
start: f.start,
description: f.description,
skillsUsed: f.skillsUsed,
}));
/** Generates a Person entity */
const personArb = fc.record({
id: kebabCaseId,
created: isoDate,
modified: isoDate,
firstName: fc.string({ minLength: 1, maxLength: 15 }).filter(s => s.trim().length > 0),
lastName: fc.string({ minLength: 1, maxLength: 15 }).filter(s => s.trim().length > 0),
summary: fc.string({ minLength: 0, maxLength: 50 }),
}).map(f => ({
id: f.id,
type: 'person' as const,
created: f.created,
modified: f.modified,
name: { first: f.firstName, last: f.lastName, display: `${f.firstName} ${f.lastName}` },
summary: f.summary || undefined,
skills: [] as string[],
experiences: [] as string[],
}));
/** Generates a Tandem entity referencing two partner IDs */
const tandemArb = (partner1Id: string, partner2Id: string) => fc.record({
id: kebabCaseId,
created: isoDate,
modified: isoDate,
sharedVision: fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
complementarySkills: fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
collaborationModel: fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
combinedNarrative: fc.string({ minLength: 0, maxLength: 50 }),
jointCompetencies: fc.array(fc.string({ minLength: 1, maxLength: 20 }).filter(s => s.trim().length > 0), { minLength: 0, maxLength: 3 }),
}).map(f => ({
id: f.id,
type: 'tandem' as const,
created: f.created,
modified: f.modified,
partners: [partner1Id, partner2Id] as [string, string],
sharedVision: f.sharedVision,
complementarySkills: f.complementarySkills,
collaborationModel: f.collaborationModel,
combinedNarrative: f.combinedNarrative || undefined,
jointCompetencies: f.jointCompetencies.length > 0 ? f.jointCompetencies : undefined,
}));
// --- Property 9: CV relevance selection ordering ---
describe('Property 9: CV relevance selection ordering', () => {
it('matching items have score > 0 and appear before score-0 items in the output', () => {
fc.assert(
fc.property(
fc.array(fc.string({ minLength: 1, maxLength: 15 }).filter(s => s.trim().length > 0), { minLength: 1, maxLength: 5 }),
fc.array(experienceArb, { minLength: 0, maxLength: 5 }),
fc.array(skillArb, { minLength: 0, maxLength: 5 }),
fc.array(projectArb, { minLength: 0, maxLength: 5 }),
(requirements, experiences, skills, projects) => {
const results = selectRelevant(requirements, experiences, skills, projects);
// All items with matchedRequirements.length > 0 must have score > 0
for (const r of results) {
if (r.matchedRequirements.length > 0) {
expect(r.score).toBeGreaterThan(0);
}
}
// Items are sorted: all score > 0 items appear before score === 0 items
let seenZero = false;
for (const r of results) {
if (r.score === 0) {
seenZero = true;
} else if (seenZero) {
// A non-zero score item appeared after a zero-score item — ordering violated
expect(seenZero && r.score > 0).toBe(false);
}
}
// Results are sorted by score descending
for (let i = 1; i < results.length; i++) {
expect(results[i - 1].score).toBeGreaterThanOrEqual(results[i].score);
}
}
),
{ numRuns: 100 }
);
});
/**
* Validates: Requirements 5.1, 5.2
*/
});
// --- Property 10: Tandem application document set (exactly 3 docs) ---
describe('Property 10: Tandem application document set', () => {
it('generateTandemData produces data with exactly 2 partner profiles referencing both partners', () => {
fc.assert(
fc.property(
personArb,
personArb,
fc.array(experienceArb, { minLength: 0, maxLength: 3 }),
fc.array(experienceArb, { minLength: 0, maxLength: 3 }),
fc.array(skillArb, { minLength: 0, maxLength: 3 }),
fc.array(skillArb, { minLength: 0, maxLength: 3 }),
(partner1, partner2, exp1, exp2, skills1, skills2) => {
// Ensure distinct partner IDs
const p1 = { ...partner1, id: partner1.id + '-p1' };
const p2 = { ...partner2, id: partner2.id + '-p2' };
const tandem: Tandem = {
id: 'tandem-test',
type: 'tandem',
created: '2025-01-01',
modified: '2025-01-01',
partners: [p1.id, p2.id],
sharedVision: 'Shared vision text',
complementarySkills: 'Complementary skills text',
collaborationModel: 'Collaboration model text',
};
const result = generateTandemData(
tandem,
p1 as Person,
p2 as Person,
exp1,
exp2,
skills1,
skills2
);
// Exactly 2 partner profiles in the output
expect(result.partnerProfiles).toHaveLength(2);
// The tandem CV references both partners
expect(result.partners).toContain(p1.id);
expect(result.partners).toContain(p2.id);
// Each partner profile corresponds to one of the partners
const profileIds = result.partnerProfiles.map(p => p.personId);
expect(profileIds).toContain(p1.id);
expect(profileIds).toContain(p2.id);
// The tandem data contains shared information from both profiles
expect(result.sharedVision).toBeTruthy();
expect(result.complementarySkills).toBeTruthy();
expect(result.collaborationModel).toBeTruthy();
}
),
{ numRuns: 100 }
);
});
/**
* Validates: Requirements 5.3, 5.4
*/
});
// --- Property 11: CV output validity and traceability ---
describe('Property 11: CV output validity and traceability', () => {
it('all data in TandemDocumentData traces back to input entities', () => {
fc.assert(
fc.property(
personArb,
personArb,
fc.array(experienceArb, { minLength: 1, maxLength: 4 }),
fc.array(experienceArb, { minLength: 1, maxLength: 4 }),
fc.array(skillArb, { minLength: 1, maxLength: 4 }),
fc.array(skillArb, { minLength: 1, maxLength: 4 }),
(partner1, partner2, exp1, exp2, skills1, skills2) => {
const p1 = { ...partner1, id: partner1.id + '-p1' };
const p2 = { ...partner2, id: partner2.id + '-p2' };
const tandem: Tandem = {
id: 'tandem-trace',
type: 'tandem',
created: '2025-01-01',
modified: '2025-01-01',
partners: [p1.id, p2.id],
sharedVision: 'Vision',
complementarySkills: 'Skills complement',
collaborationModel: 'Model',
combinedNarrative: 'Narrative',
jointCompetencies: ['competency-1', 'competency-2'],
};
const result = generateTandemData(
tandem,
p1 as Person,
p2 as Person,
exp1,
exp2,
skills1,
skills2
);
// All skill names in partner profiles must come from input skills
const allInputSkillNames = [...skills1, ...skills2].map(s => s.name);
for (const profile of result.partnerProfiles) {
for (const skillName of profile.topSkills) {
expect(allInputSkillNames).toContain(skillName);
}
}
// All experience titles in partner profiles must come from input experiences
const allInputExpTitles = [...exp1, ...exp2].map(e => e.title);
for (const profile of result.partnerProfiles) {
for (const exp of profile.topExperiences) {
expect(allInputExpTitles).toContain(exp.title);
}
}
// All organization references in experiences must come from input experiences
const allInputOrgs = [...exp1, ...exp2].map(e => e.organization);
for (const profile of result.partnerProfiles) {
for (const exp of profile.topExperiences) {
expect(allInputOrgs).toContain(exp.organization);
}
}
// Display names must come from input persons
const inputDisplayNames = [p1.name.display, p2.name.display];
for (const profile of result.partnerProfiles) {
expect(inputDisplayNames).toContain(profile.displayName);
}
// Tandem-level data must trace back to the tandem entity
expect(result.sharedVision).toBe(tandem.sharedVision);
expect(result.complementarySkills).toBe(tandem.complementarySkills);
expect(result.collaborationModel).toBe(tandem.collaborationModel);
expect(result.combinedNarrative).toBe(tandem.combinedNarrative);
expect(result.jointCompetencies).toEqual(tandem.jointCompetencies);
}
),
{ numRuns: 100 }
);
});
/**
* Validates: Requirements 5.5, 5.6
*/
});