diff --git a/src/config/schema.ts b/src/config/schema.ts index 8dcd3246..ea9314d5 100644 --- a/src/config/schema.ts +++ b/src/config/schema.ts @@ -38,6 +38,8 @@ const JiraConfigSchema = z.object({ const LinearConfigSchema = z.object({ teamId: z.string().min(1), + /** Optional Linear Project (initiative) ID — when set, narrows scope within the team. */ + projectId: z.string().optional(), statuses: z.record(z.string()), // CASCADE status names → Linear state IDs labels: z .object({ diff --git a/tests/unit/config/schema.test.ts b/tests/unit/config/schema.test.ts index 83e7efdc..a0e7953d 100644 --- a/tests/unit/config/schema.test.ts +++ b/tests/unit/config/schema.test.ts @@ -378,6 +378,47 @@ describe.concurrent('validateConfig', () => { expect(result.projects[0].engineSettings?.['claude-code']?.thinking).toBe('adaptive'); }); + it('preserves linear.projectId through validation', () => { + const result = validateConfig({ + projects: [ + { + id: 'test', + orgId: 'default', + name: 'Test', + repo: 'owner/repo', + linear: { + teamId: 'team-uuid', + projectId: 'project-uuid', + statuses: { backlog: 'state-bl' }, + }, + }, + ], + }); + + expect(result.projects[0].linear?.projectId).toBe('project-uuid'); + expect(result.projects[0].linear?.teamId).toBe('team-uuid'); + }); + + it('treats linear.projectId as optional', () => { + const result = validateConfig({ + projects: [ + { + id: 'test', + orgId: 'default', + name: 'Test', + repo: 'owner/repo', + linear: { + teamId: 'team-uuid', + statuses: {}, + }, + }, + ], + }); + + expect(result.projects[0].linear?.projectId).toBeUndefined(); + expect(result.projects[0].linear?.teamId).toBe('team-uuid'); + }); + it('rejects unsupported project engineSettings entries', () => { const config = { projects: [