From 53cce9522e3d5de38c97d96bfe62e7a7ad738ff5 Mon Sep 17 00:00:00 2001 From: Zbigniew Sobiecki Date: Sat, 18 Apr 2026 09:13:47 +0000 Subject: [PATCH] fix(config): add projectId to LinearConfigSchema so Zod stops stripping it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #1138 added projectId to the configMapper, but Zod's LinearConfigSchema didn't declare projectId — and z.object() defaults to "strip" mode, silently removing unknown keys during .parse(). So projectId survived the mapper but got dropped by validateConfig(), never reaching augmentProjectSecrets() or the LinearPMProvider. Result: new Linear issues created by splitting / planning agents had no project assignment despite the configMapper fix. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/config/schema.ts | 2 ++ tests/unit/config/schema.test.ts | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) 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: [