diff --git a/packages/github/src/__tests__/emit-auxiliary-files.test.ts b/packages/github/src/__tests__/emit-auxiliary-files.test.ts index fb7f8ac1..84cf7d0a 100644 --- a/packages/github/src/__tests__/emit-auxiliary-files.test.ts +++ b/packages/github/src/__tests__/emit-auxiliary-files.test.ts @@ -425,11 +425,48 @@ describe('emitGitHubAuxiliaryFiles', () => { ); assert.equal(client.files.get(githubByStateAliasPath('acme', 'widgets', 'issues', 'open', 7)), canonicalBytes); assert.equal(client.files.get(githubByEditedAliasPath('acme', 'widgets', 'issues', '2026-05-12', 7)), canonicalBytes); + const issueRows = JSON.parse(client.files.get(indexPath) ?? '[]') as Array>; + assert.deepEqual(issueRows, [ + { + id: '7', + title: 'Bug report', + updated: '2026-05-12T00:00:00Z', + number: 7, + state: 'open', + labels: ['P0 Critical'], + assigneeKeys: ['octocat'], + creatorKey: 'monalisa', + priority: 'P0 Critical', + }, + ]); // No writes leaked into the pulls index for this repo. assert.ok(!writtenPaths.includes(githubRepoPullsIndexPath('acme', 'widgets'))); }); + it('retains an explicit empty label array in issue index rows', async () => { + const client = createClient(); + await emitGitHubAuxiliaryFiles(client, { + workspaceId: 'ws-1', + issues: [ + { + owner: 'acme', + repo: 'widgets', + number: 8, + title: 'Unlabeled issue', + state: 'open', + labels: [], + updated_at: '2026-05-13T00:00:00Z', + }, + ], + }); + + const rows = JSON.parse(client.files.get(githubRepoIssuesIndexPath('acme', 'widgets')) ?? '[]') as Array< + Record + >; + assert.deepEqual(rows[0]?.labels, []); + }); + it('uses the newest lifecycle timestamp for PR by-edited aliases', async () => { const client = createClient(); diff --git a/packages/github/src/emit-auxiliary-files.ts b/packages/github/src/emit-auxiliary-files.ts index 5b070ccc..3d16928e 100644 --- a/packages/github/src/emit-auxiliary-files.ts +++ b/packages/github/src/emit-auxiliary-files.ts @@ -780,6 +780,7 @@ function buildRecordIndexRow( updated: readLifecycleEditedAt(record) ?? readUpdatedAt(record), number: Number(number), state: state ?? '', + labels: readGitHubLabelNames(record), ...withOptionalArray('assigneeKeys', readGitHubAssigneeKeys(record)), ...withOptionalString('creatorKey', readGitHubCreatorKey(record)), ...withOptionalString('priority', readPriority(record)), @@ -974,6 +975,15 @@ function readGitHubAssigneeKeys(record: Record): string[] { return uniqueStrings(assignees.map((entry) => readUserKey(entry)).filter((entry): entry is string => Boolean(entry))); } +function readGitHubLabelNames(record: Record): string[] { + const labels = Array.isArray(record.labels) ? record.labels : []; + return uniqueStrings( + labels + .map((label) => readNonEmptyString(label) ?? readNonEmptyString(isRecord(label) ? label.name : undefined)) + .filter((label): label is string => Boolean(label)), + ); +} + function readGitHubCreatorKey(record: Record): string | undefined { return readUserKey(record.user) ?? readNonEmptyString(record.user); }