Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions layers/identity/server/utils/digest-select.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export async function selectDigestForUser(
WHERE a.id > ?2 AND a.id <= ?3
AND (sub.muted_until IS NULL OR sub.muted_until <= ?4)
AND r.repo_kind != 'aggregator'
AND s.source_resolved = 1
AND s.current_sha IS NOT NULL
AND TRIM(s.current_sha) != ''
AND s.rendered_skill_path IS NOT NULL
Expand Down Expand Up @@ -105,6 +106,7 @@ export async function selectDigestForUser(
AND a.repo = ?3
AND a.id > ?4
AND a.id <= ?5
AND s.source_resolved = 1
AND s.current_sha IS NOT NULL
AND TRIM(s.current_sha) != ''
AND s.rendered_skill_path IS NOT NULL
Expand All @@ -120,6 +122,7 @@ export async function selectDigestForUser(
AND a.repo = ?2
AND a.id > ?3
AND a.id <= ?4
AND s.source_resolved = 1
AND s.current_sha IS NOT NULL
AND TRIM(s.current_sha) != ''
AND s.rendered_skill_path IS NOT NULL
Expand Down
1 change: 1 addition & 0 deletions test/unit/digest-delivery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,7 @@ const TEST_SCHEMA = `
description TEXT,
current_sha TEXT,
rendered_skill_path TEXT,
source_resolved INTEGER NOT NULL DEFAULT 1,
PRIMARY KEY (owner, repo, name)
);
CREATE TABLE repos (
Expand Down
39 changes: 29 additions & 10 deletions test/unit/digest-select.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ describe('digest selection', () => {
name TEXT,
description TEXT,
current_sha TEXT,
rendered_skill_path TEXT
rendered_skill_path TEXT,
source_resolved INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE repos (
owner TEXT,
Expand Down Expand Up @@ -57,7 +58,7 @@ describe('digest selection', () => {
PRIMARY KEY (owner, repo, name, sha)
);

INSERT INTO skills VALUES ('nuxt', 'nuxt', 'nuxt', 'Nuxt framework', 'blob-new', 'skills/nuxt/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'nuxt', 'nuxt', 'Nuxt framework', 'blob-new', 'skills/nuxt/SKILL.md');
INSERT INTO repos VALUES ('nuxt', 'nuxt', 'source', 'main');
INSERT INTO skill_subscriptions VALUES (1, 'nuxt', 'nuxt', 'manual', NULL);
INSERT INTO activity VALUES (1, 'nuxt', 'nuxt', 'nuxt', 500, 1500, 'blob-old');
Expand Down Expand Up @@ -102,7 +103,7 @@ describe('digest selection', () => {

it('retains sorted skill names and per-skill counts for a multi-skill repo', async () => {
sqlite.exec(`
INSERT INTO skills VALUES ('nuxt', 'nuxt', 'zeta', 'Zeta helper', 'blob-z2', 'skills/zeta/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'nuxt', 'zeta', 'Zeta helper', 'blob-z2', 'skills/zeta/SKILL.md');
INSERT INTO activity VALUES (3, 'nuxt', 'nuxt', 'zeta', 100, 1700, 'blob-z1');
INSERT INTO activity VALUES (4, 'nuxt', 'nuxt', 'zeta', 101, 1701, 'blob-z2');
INSERT INTO skill_revisions VALUES ('nuxt', 'nuxt', 'zeta', 'commit-z1', 100, 'zeta first');
Expand All @@ -127,8 +128,8 @@ describe('digest selection', () => {

it('narrows a like-sourced subscription to the skills actually liked', async () => {
sqlite.exec(`
INSERT INTO skills VALUES ('nuxt', 'ui', 'design-tokens', 'Tokens', 'a', 'design-tokens/SKILL.md');
INSERT INTO skills VALUES ('nuxt', 'ui', 'motion', 'Motion', 'b', 'motion/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'ui', 'design-tokens', 'Tokens', 'a', 'design-tokens/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'ui', 'motion', 'Motion', 'b', 'motion/SKILL.md');
INSERT INTO repos VALUES ('nuxt', 'ui', 'source', 'main');
INSERT INTO skill_subscriptions VALUES (2, 'nuxt', 'ui', 'like', NULL);
INSERT INTO skill_likes VALUES (2, 'nuxt', 'ui', 'design-tokens', 1);
Expand All @@ -153,8 +154,8 @@ describe('digest selection', () => {

it('keeps whole-repo scope for a manually watched repo', async () => {
sqlite.exec(`
INSERT INTO skills VALUES ('nuxt', 'ui', 'design-tokens', 'Tokens', 'a', 'design-tokens/SKILL.md');
INSERT INTO skills VALUES ('nuxt', 'ui', 'motion', 'Motion', 'b', 'motion/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'ui', 'design-tokens', 'Tokens', 'a', 'design-tokens/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'ui', 'motion', 'Motion', 'b', 'motion/SKILL.md');
INSERT INTO repos VALUES ('nuxt', 'ui', 'source', 'main');
INSERT INTO skill_subscriptions VALUES (3, 'nuxt', 'ui', 'manual', NULL);
INSERT INTO skill_likes VALUES (3, 'nuxt', 'ui', 'design-tokens', 1);
Expand All @@ -176,8 +177,8 @@ describe('digest selection', () => {

it('drops a like-sourced repo entirely when nothing liked in it changed', async () => {
sqlite.exec(`
INSERT INTO skills VALUES ('nuxt', 'ui', 'design-tokens', 'Tokens', 'a', 'design-tokens/SKILL.md');
INSERT INTO skills VALUES ('nuxt', 'ui', 'motion', 'Motion', 'b', 'motion/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'ui', 'design-tokens', 'Tokens', 'a', 'design-tokens/SKILL.md');
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'ui', 'motion', 'Motion', 'b', 'motion/SKILL.md');
INSERT INTO repos VALUES ('nuxt', 'ui', 'source', 'main');
INSERT INTO skill_subscriptions VALUES (4, 'nuxt', 'ui', 'like', NULL);
INSERT INTO skill_likes VALUES (4, 'nuxt', 'ui', 'design-tokens', 1);
Expand Down Expand Up @@ -214,9 +215,27 @@ describe('digest selection', () => {
})
})

it.each(['manual', 'like'])('omits removed sources from a %s watch', async (scope) => {
sqlite.prepare('UPDATE skill_subscriptions SET source = ?').run(scope)
sqlite.exec(`
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path, source_resolved)
VALUES ('nuxt', 'nuxt', 'removed', 'Removed Skill', 'old-blob', 'removed/SKILL.md', 0);
INSERT INTO activity VALUES (3, 'nuxt', 'nuxt', 'removed', 1600, 1700, 'old-blob');
INSERT INTO skill_likes VALUES (1, 'nuxt', 'nuxt', 'nuxt', 1);
INSERT INTO skill_likes VALUES (1, 'nuxt', 'nuxt', 'removed', 1);
`)

const selection = await selectDigestForUser(db, digestUser(), 2000, { cursorStart: 0, cursorEnd: 3 })

expect(selection?.entries[0]).toMatchObject({ skillNames: ['nuxt'], changeCount: 2 })
expect(selection?.entries[0]?.skills.map(skill => skill.sourceUrl)).toEqual([
'https://github.com/nuxt/nuxt/blob/main/skills/nuxt/SKILL.md',
])
})

it('omits a Skill when its exact source cannot be resolved', async () => {
sqlite.exec(`
INSERT INTO skills VALUES ('nuxt', 'nuxt', 'missing-source', 'Missing source', NULL, NULL);
INSERT INTO skills (owner, repo, name, description, current_sha, rendered_skill_path) VALUES ('nuxt', 'nuxt', 'missing-source', 'Missing source', NULL, NULL);
INSERT INTO activity VALUES (3, 'nuxt', 'nuxt', 'missing-source', 1600, 1700, 'missing');
`)

Expand Down