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
24 changes: 20 additions & 4 deletions .claude/agents/correctness-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,26 @@ performance issue as "correctness" to justify a FAIL.

<workflow>

## 1. Read skill for detailed rules:
- `.claude/skills/correctness/SKILL.md`

SCRIPT=".claude/skills/correctness/scripts/checklist.mjs"
## 1. Resolve the installed skill and read it for detailed rules

CORRECTNESS_SKILL=""
for candidate in \
.agents/skills/correctness \
.claude/skills/correctness \
.cursor/skills/correctness
do
if [ -f "$candidate/scripts/checklist.mjs" ]; then
CORRECTNESS_SKILL="$candidate"
break
fi
done
if [ -z "$CORRECTNESS_SKILL" ]; then
echo "Correctness Review checklist unavailable: run devkit sync-skills" >&2
exit 2
fi
SCRIPT="$CORRECTNESS_SKILL/scripts/checklist.mjs"

Read `$CORRECTNESS_SKILL/SKILL.md` before continuing.

## 2. Generate the checklist
```bash
Expand Down
17 changes: 16 additions & 1 deletion .claude/skills/correctness/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@ description: Correctness bug hunting for a finished diff. Use when reviewing cha
## Review Script

```bash
SCRIPT=".claude/skills/correctness/scripts/checklist.mjs"
CORRECTNESS_SKILL=""
for candidate in \
.agents/skills/correctness \
.claude/skills/correctness \
.cursor/skills/correctness
do
if [ -f "$candidate/scripts/checklist.mjs" ]; then
CORRECTNESS_SKILL="$candidate"
break
fi
done
if [ -z "$CORRECTNESS_SKILL" ]; then
echo "Correctness Review checklist unavailable: run devkit sync-skills" >&2
exit 2
fi
SCRIPT="$CORRECTNESS_SKILL/scripts/checklist.mjs"

node $SCRIPT generate # Enumerate review items from staged source files (all declared roots)
node $SCRIPT status # Show progress
Expand Down
24 changes: 20 additions & 4 deletions .cursor/agents/correctness-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,26 @@ performance issue as "correctness" to justify a FAIL.

<workflow>

## 1. Read skill for detailed rules:
- `.claude/skills/correctness/SKILL.md`

SCRIPT=".claude/skills/correctness/scripts/checklist.mjs"
## 1. Resolve the installed skill and read it for detailed rules

CORRECTNESS_SKILL=""
for candidate in \
.agents/skills/correctness \
.claude/skills/correctness \
.cursor/skills/correctness
do
if [ -f "$candidate/scripts/checklist.mjs" ]; then
CORRECTNESS_SKILL="$candidate"
break
fi
done
if [ -z "$CORRECTNESS_SKILL" ]; then
echo "Correctness Review checklist unavailable: run devkit sync-skills" >&2
exit 2
fi
SCRIPT="$CORRECTNESS_SKILL/scripts/checklist.mjs"

Read `$CORRECTNESS_SKILL/SKILL.md` before continuing.

## 2. Generate the checklist
```bash
Expand Down
17 changes: 16 additions & 1 deletion .cursor/skills/correctness/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@ description: Correctness bug hunting for a finished diff. Use when reviewing cha
## Review Script

```bash
SCRIPT=".claude/skills/correctness/scripts/checklist.mjs"
CORRECTNESS_SKILL=""
for candidate in \
.agents/skills/correctness \
.claude/skills/correctness \
.cursor/skills/correctness
do
if [ -f "$candidate/scripts/checklist.mjs" ]; then
CORRECTNESS_SKILL="$candidate"
break
fi
done
if [ -z "$CORRECTNESS_SKILL" ]; then
echo "Correctness Review checklist unavailable: run devkit sync-skills" >&2
exit 2
fi
SCRIPT="$CORRECTNESS_SKILL/scripts/checklist.mjs"

node $SCRIPT generate # Enumerate review items from staged source files (all declared roots)
node $SCRIPT status # Show progress
Expand Down
24 changes: 20 additions & 4 deletions agents/correctness-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,26 @@ performance issue as "correctness" to justify a FAIL.

<workflow>

## 1. Read skill for detailed rules:
- `.claude/skills/correctness/SKILL.md`

SCRIPT=".claude/skills/correctness/scripts/checklist.mjs"
## 1. Resolve the installed skill and read it for detailed rules

CORRECTNESS_SKILL=""
for candidate in \
.agents/skills/correctness \
.claude/skills/correctness \
.cursor/skills/correctness
do
if [ -f "$candidate/scripts/checklist.mjs" ]; then
CORRECTNESS_SKILL="$candidate"
break
fi
done
if [ -z "$CORRECTNESS_SKILL" ]; then
echo "Correctness Review checklist unavailable: run devkit sync-skills" >&2
exit 2
fi
SCRIPT="$CORRECTNESS_SKILL/scripts/checklist.mjs"

Read `$CORRECTNESS_SKILL/SKILL.md` before continuing.

## 2. Generate the checklist
```bash
Expand Down
15 changes: 15 additions & 0 deletions cli/__tests__/review-asset-runtime.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,21 @@ describe('packaged reviewer asset runtime', () => {
}
});

it('makes correctness checklist discovery portable across agent providers', () => {
const sources = [
readFileSync(join(HERE, '../../agents/correctness-reviewer.md'), 'utf8'),
readFileSync(join(HERE, '../../skills/correctness/SKILL.md'), 'utf8'),
];

for (const source of sources) {
expect(source).toContain('.agents/skills/correctness');
expect(source).toContain('.claude/skills/correctness');
expect(source).toContain('.cursor/skills/correctness');
expect(source).toContain('[ -f "$candidate/scripts/checklist.mjs" ]');
expect(source).toContain('Correctness Review checklist unavailable: run devkit sync-skills');
}
});

it('copies only registered assets, dereferences links, preserves executability, and keeps preflight identity', () => {
const source = packageFixture('devkit review package ');
const originalBrief = join(source, 'agents/api-security-reviewer.md');
Expand Down
24 changes: 20 additions & 4 deletions dist/agents/correctness-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,26 @@ performance issue as "correctness" to justify a FAIL.

<workflow>

## 1. Read skill for detailed rules:
- `.claude/skills/correctness/SKILL.md`

SCRIPT=".claude/skills/correctness/scripts/checklist.mjs"
## 1. Resolve the installed skill and read it for detailed rules

CORRECTNESS_SKILL=""
for candidate in \
.agents/skills/correctness \
.claude/skills/correctness \
.cursor/skills/correctness
do
if [ -f "$candidate/scripts/checklist.mjs" ]; then
CORRECTNESS_SKILL="$candidate"
break
fi
done
if [ -z "$CORRECTNESS_SKILL" ]; then
echo "Correctness Review checklist unavailable: run devkit sync-skills" >&2
exit 2
fi
SCRIPT="$CORRECTNESS_SKILL/scripts/checklist.mjs"

Read `$CORRECTNESS_SKILL/SKILL.md` before continuing.

## 2. Generate the checklist
```bash
Expand Down
17 changes: 16 additions & 1 deletion dist/skills/correctness/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,22 @@ description: Correctness bug hunting for a finished diff. Use when reviewing cha
## Review Script

```bash
SCRIPT=".claude/skills/correctness/scripts/checklist.mjs"
CORRECTNESS_SKILL=""
for candidate in \
.agents/skills/correctness \
.claude/skills/correctness \
.cursor/skills/correctness
do
if [ -f "$candidate/scripts/checklist.mjs" ]; then
CORRECTNESS_SKILL="$candidate"
break
fi
done
if [ -z "$CORRECTNESS_SKILL" ]; then
echo "Correctness Review checklist unavailable: run devkit sync-skills" >&2
exit 2
fi
SCRIPT="$CORRECTNESS_SKILL/scripts/checklist.mjs"

node $SCRIPT generate # Enumerate review items from staged source files (all declared roots)
node $SCRIPT status # Show progress
Expand Down
3 changes: 1 addition & 2 deletions eslint/baselines/size-lines.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"gate-engine/review/__tests__/run-review.test.mts": 2461,
"gate-engine/review/eval/bench.mts": 878,
"gate-engine/review/eval/conventions/bench.mts": 988,
"gate-engine/review/eval/reviewers/bench.mts": 881,
"gate-engine/review/run-review.mts": 547
"gate-engine/review/eval/reviewers/bench.mts": 881
}
}
2 changes: 1 addition & 1 deletion gate-engine/judge/__tests__/diff-focus.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ describe('diffCacheIdentity — conservative matching + function anchors', () =>
"import { captureException } from '../utils/presentry-shim';",
],
['a nested call smuggled as the argument', 'Sentry.captureException(mutateGlobalState(err));'],
['a template-literal argument', 'Sentry.captureMessage(`${sideEffect()}`);'],
['a template-literal argument', ['Sentry.captureMessage(`', '$', '{sideEffect()}`);'].join('')],
])('does NOT strip %s — the restage re-reviews', (_label, line) => {
const fixed = edit(10, ['handle();']);
const d1 = gitDiffOf(BASE, fixed);
Expand Down
23 changes: 22 additions & 1 deletion gate-engine/review/__tests__/consumer-identity.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
* ship-mode identities are two incomparable namespaces and every cross-mode rate is a blend.
* 2. It never throws. It feeds telemetry, and telemetry must never fail a gate.
*/
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join } from 'node:path';
import { afterEach, describe, expect, it } from 'vitest';
import { resolveGuardConfig } from '../../config.mts';
import { consumerChecklistAssetRoot } from '../cascade/consumer-assets.mts';
import {
checklistAssetPath,
hasChecklist,
Expand Down Expand Up @@ -82,6 +83,26 @@ describe('consumerReviewerIdentity', () => {
}
});

it('uses a provider-projected skill root for execution identity when Claude skills are absent', () => {
const { root, packaged } = consumerFixture();
mkdirSync(join(root, '.agents'), { recursive: true });
renameSync(join(root, '.claude/skills'), join(root, '.agents/skills'));
const cfg = resolveGuardConfig(root);
const selected: ReviewerSelection[] = REVIEWERS.map((reviewer) => ({
reviewer,
files: ['src/example.ts'],
}));
const packagedIdentities = preflightReviewAssets(packaged, selected, cfg);

for (const reviewer of REVIEWERS) {
if (hasChecklist(reviewer))
expect(consumerChecklistAssetRoot(root, reviewer)).toBe('.agents');
expect(consumerReviewerIdentity(root, cfg, reviewer)).toBe(
packagedIdentities.get(reviewer.name),
);
}
});

it('changes when the brief changes, and only for that reviewer', () => {
const { root } = consumerFixture();
const cfg = resolveGuardConfig(root);
Expand Down
25 changes: 24 additions & 1 deletion gate-engine/review/__tests__/reviewers.test.mts
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,11 @@ describe('allowedToolsFor', () => {
expect(tools).toContain(',mcp__codebase__searchCode');
expect(tools).toContain('Bash(node .claude/skills/commit-guard/scripts/checklist.mjs:*)');
});
it('grants a provider-projected checklist path when the consumer resolver supplies one', () => {
const tools = allowedToolsFor(REVIEWERS[0], cfg, '.agents');
expect(tools).toContain('Bash(node .agents/skills/api-security/scripts/checklist.mjs:*)');
expect(tools).not.toContain('Bash(node .claude/skills/api-security/scripts/checklist.mjs:*)');
});
it('a skill-less reviewer (conventions) gets EXACTLY Read,Grep,Glob — no Bash at all, per its AC', () => {
const conv = REVIEWERS.find((r) => r.name === 'conventions-reviewer');
expect(allowedToolsFor(conv, cfg)).toBe('Read,Grep,Glob');
Expand Down Expand Up @@ -416,16 +421,34 @@ describe('wrapPrompt / escalatePrompt / stripFrontmatter', () => {
expect(p).toContain('check-item <name> --pass');
expect(p).toContain('Never delete the checklist artifact');
});
it('rewrites ordinary commit/ship prompts to the resolved consumer checklist root', () => {
const p = wrapPrompt(
'Try .agents/skills/api-security/SKILL.md, .claude/skills/api-security/SKILL.md, then .cursor/skills/api-security/SKILL.md.',
REVIEWERS[0],
['src/main/a.ts'],
undefined,
undefined,
undefined,
'.agents',
);
expect(p).toContain('node .agents/skills/api-security/scripts/checklist.mjs generate');
expect(p.match(/\.agents\/skills\/api-security/g)).toHaveLength(6);
expect(p).not.toContain('.claude/skills/api-security');
expect(p).not.toContain('.cursor/skills/api-security');
expect(p).toContain('MANDATORY CHECKLIST WORKFLOW');
});
it('lets the packaged brief own enumeration and rewrites its skill paths in review mode', () => {
const guard = REVIEWERS.find((r) => r.name === 'commit-guard');
const p = wrapPrompt(
'Read .claude/skills/commit-guard/SKILL.md.',
'Try .agents/skills/commit-guard/SKILL.md, .claude/skills/commit-guard/SKILL.md, then .cursor/skills/commit-guard/SKILL.md.',
guard,
['src/a.ts'],
'/tmp/devkit-review-assets',
);
expect(p).toContain('/tmp/devkit-review-assets/skills/commit-guard/SKILL.md');
expect(p).not.toContain('.claude/skills/commit-guard/SKILL.md');
expect(p).not.toContain('.agents/skills/commit-guard');
expect(p).not.toContain('.cursor/skills/commit-guard');
expect(p).toContain('The reviewer brief owns checklist enumeration');
expect(p).not.toContain('check-file <name>');
});
Expand Down
32 changes: 32 additions & 0 deletions gate-engine/review/cascade/consumer-assets.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { existsSync, readFileSync } from 'node:fs';
import path from 'node:path';
import type { GuardConfig } from '../../config.mts';
import { checklistAssetPath, hasChecklist, type Reviewer } from '../reviewers.mts';

const CONSUMER_SKILL_ROOTS = ['.claude', '.agents', '.cursor'] as const;

/** Resolve the provider-projected checklist root actually present in a consumer checkout. */
export function consumerChecklistAssetRoot(cwd: string, reviewer: Reviewer): string {
if (!hasChecklist(reviewer)) return '.claude';
const relativePath = checklistAssetPath(reviewer);
return (
CONSUMER_SKILL_ROOTS.find((root) => existsSync(path.resolve(cwd, root, relativePath))) ??
'.claude'
);
}

/** Read one package-relative asset from its consumer-projected brief or skill root. */
export function readConsumerReviewAsset(
cwd: string,
cfg: GuardConfig,
skillRoot: string,
relativePath: string,
): Buffer {
const agentsPrefix = 'agents/';
if (relativePath.startsWith(agentsPrefix)) {
const dir = cfg.review.agentsDir;
const base = path.isAbsolute(dir) ? dir : path.resolve(cwd, dir);
return readFileSync(path.join(base, relativePath.slice(agentsPrefix.length)));
}
return readFileSync(path.resolve(cwd, skillRoot, relativePath));
}
Loading
Loading