Skip to content
Open
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
29 changes: 27 additions & 2 deletions src/scorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,12 +579,37 @@ export class SkillScorer {
findings.push({ type: 'warning', message: 'File operations found without clear file-related purpose', points: 0 });
}

// (1) Network access matches stated purpose
// (1) Network access and social account changes match stated purpose
const networkTerms = ['curl', 'wget', 'fetch', 'http', 'api', 'endpoint'];
const hasNetwork = networkTerms.some(term => contentLower.includes(term));
const hasNetworkScope = contentLower.includes('api') || contentLower.includes('url') ||
contentLower.includes('endpoint') || contentLower.includes('request') || contentLower.includes('upload');
if (!hasNetwork || hasNetworkScope) {
const socialAccountActions = [
'post tweet',
'publish tweet',
'reply to tweet',
'send dm',
'send direct message',
'follow user',
'like tweet',
'retweet',
'schedule post'
];
const approvalTerms = [
'approval',
'approve',
'confirm',
'manual review',
'human review',
'ask before',
'review before'
];
const changesSocialAccount = socialAccountActions.some(action => contentLower.includes(action));
const hasApprovalGate = approvalTerms.some(term => contentLower.includes(term));

if (changesSocialAccount && !hasApprovalGate) {
findings.push({ type: 'warning', message: 'Social account-changing actions need explicit approval or review gates', points: 0 });
} else if (!hasNetwork || hasNetworkScope) {
score += 1;
findings.push({ type: 'pass', message: 'Network access matches stated purpose', points: 1 });
} else {
Expand Down
21 changes: 21 additions & 0 deletions tests/fixtures/social-action-skill/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
name: social-action-skill
description: Reviews public X/Twitter conversation notes and prepares account-changing recommendations for managed social media workflows.
---

# Social Action Skill

Use this skill when a user wants help deciding whether to post tweet updates,
reply to tweet threads, send DM follow-ups, or schedule post drafts from public
campaign context.

## Workflow

1. Read the supplied campaign notes.
2. Draft the social action plan.
3. Call the configured social media API endpoint.
4. Return a summary of completed actions.

## Validation

Run `npm --version` before using package scripts.
13 changes: 13 additions & 0 deletions tests/unit/scorer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,19 @@ describe('SkillScorer', () => {
expect(timeFinding).toBeDefined();
expect(timeFinding!.type).toBe('pass');
});

it('should flag social account actions without approval gates', async () => {
const skill = await parser.parseSkill(path.join(fixturesPath, 'social-action-skill'));
const score = await scorer.scoreSkill(skill);

const safety = score.categoryScores.find(cat => cat.category.id === 'safety');
expect(safety).toBeDefined();

const approvalFinding = safety!.findings.find(f => f.message.includes('Social account-changing actions'));
expect(approvalFinding).toBeDefined();
expect(approvalFinding!.type).toBe('warning');
expect(approvalFinding!.points).toBe(0);
});
});

describe('Scoring Edge Cases', () => {
Expand Down