sync: stale skill detection, sentry filtering, claim removal, review improvements
Stale Detection: - Detect skills removed/moved from GitHub (3 consecutive 404s threshold) - Hide stale skills from browse/search, show warning banner on detail page - Serve cached files with isStale flag when GitHub returns 404 - Add stale-check crawler command for batch verification - CLI shows warning when installing stale skills from cache Sentry & Error Handling: - Filter browser extension errors and add denyUrls - Anti-inflation measures and sentinel recalibration for curation Claim & Removal: - Enhanced ClaimForm with repo-level removal support - Add repo-removal-request API endpoint with tests - Improved owner page with bilingual content Review Pipeline: - Review version and reviewer tracking in submit API - Source format filter for pending reviews - Updated review tests Other: - Updated i18n strings (en/fa) - BrowseFilters improvements - Dockerfile updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,9 @@ export async function GET(request: NextRequest) {
|
||||
Math.max(parseInt(searchParams.get('owner_limit') ?? '0', 10) || 0, 0),
|
||||
10
|
||||
);
|
||||
const currentReviewVersion = Math.max(
|
||||
parseInt(searchParams.get('review_version') ?? '0', 10) || 0, 0
|
||||
);
|
||||
|
||||
// Run counts in parallel
|
||||
const [totalPending, reReviews] = await Promise.all([
|
||||
@@ -68,6 +71,7 @@ export async function GET(request: NextRequest) {
|
||||
securityPass,
|
||||
reReviewAll: true,
|
||||
ownerLimit,
|
||||
currentReviewVersion,
|
||||
}) as typeof batch;
|
||||
batch = [...allBatch].slice(0, batchSize);
|
||||
} else {
|
||||
|
||||
@@ -22,6 +22,8 @@ interface ReviewItem {
|
||||
i18n_priority?: number;
|
||||
content_hash_at_review?: string;
|
||||
set_verified?: boolean;
|
||||
review_version?: number;
|
||||
reviewer?: string;
|
||||
}
|
||||
|
||||
function validateReviews(body: unknown): { reviews: ReviewItem[] } | { error: string } {
|
||||
@@ -60,6 +62,18 @@ function validateReviews(body: unknown): { reviews: ReviewItem[] } | { error: st
|
||||
return { error: `reviews[${i}].i18n_priority must be an integer 0-2` };
|
||||
}
|
||||
}
|
||||
// Validate review_version (positive integer)
|
||||
if (item.review_version !== undefined && item.review_version !== null) {
|
||||
if (typeof item.review_version !== 'number' || !Number.isInteger(item.review_version) || item.review_version < 1) {
|
||||
return { error: `reviews[${i}].review_version must be a positive integer` };
|
||||
}
|
||||
}
|
||||
// Validate reviewer (non-empty string, max 50 chars)
|
||||
if (item.reviewer !== undefined && item.reviewer !== null) {
|
||||
if (typeof item.reviewer !== 'string' || item.reviewer.length === 0 || item.reviewer.length > 50) {
|
||||
return { error: `reviews[${i}].reviewer must be a non-empty string (max 50 chars)` };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { reviews: reviews as ReviewItem[] };
|
||||
@@ -106,7 +120,7 @@ export async function POST(request: NextRequest) {
|
||||
// Insert review rows
|
||||
const dbReviews = reviews.map((r) => ({
|
||||
skillId: r.skill_id,
|
||||
reviewer: 'claude-code' as const,
|
||||
reviewer: r.reviewer || 'claude-code',
|
||||
aiScore: r.ai_score,
|
||||
instructionQuality: r.instruction_quality,
|
||||
descriptionPrecision: r.description_precision,
|
||||
@@ -119,6 +133,7 @@ export async function POST(request: NextRequest) {
|
||||
needsImprovement: r.needs_improvement ?? undefined,
|
||||
i18nPriority: r.i18n_priority,
|
||||
contentHashAtReview: r.content_hash_at_review,
|
||||
reviewVersion: r.review_version,
|
||||
}));
|
||||
|
||||
await skillReviewQueries.createBatch(db, dbReviews);
|
||||
|
||||
Reference in New Issue
Block a user