cclaw-cli 0.51.10 → 0.51.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-linter.js +8 -43
- package/package.json +1 -1
package/dist/artifact-linter.js
CHANGED
|
@@ -303,26 +303,6 @@ function tokensFromRule(rule) {
|
|
|
303
303
|
}
|
|
304
304
|
return [];
|
|
305
305
|
}
|
|
306
|
-
/**
|
|
307
|
-
* Extract required keywords from validation rules that contain *backticked*
|
|
308
|
-
* stable tokens after a colon. We only fire on machine-surface enumerations
|
|
309
|
-
* (e.g., `` Must contain: `Status:`, `WAIT_FOR_CONFIRM`, `Approved:` ``);
|
|
310
|
-
* descriptive English prose with bare comma lists is intentionally ignored so
|
|
311
|
-
* authors can write rationale freely without triggering hardcoded keyword
|
|
312
|
-
* matches. Sections that need richer structural enforcement use a dedicated
|
|
313
|
-
* `validateSectionBody` dispatch (see `validateScopeSummary`, etc.).
|
|
314
|
-
*/
|
|
315
|
-
function extractRequiredKeywords(rule) {
|
|
316
|
-
const colonMatch = /:\s*(.+)$/u.exec(rule);
|
|
317
|
-
if (!colonMatch)
|
|
318
|
-
return [];
|
|
319
|
-
const tail = colonMatch[1];
|
|
320
|
-
const backtickedTokens = Array.from(tail.matchAll(/`([^`]+)`/gu)).map((m) => m[1].trim());
|
|
321
|
-
const phrases = backtickedTokens.filter((p) => p.length >= 2);
|
|
322
|
-
if (phrases.length < 3)
|
|
323
|
-
return [];
|
|
324
|
-
return phrases;
|
|
325
|
-
}
|
|
326
306
|
const VAGUE_AC_ADJECTIVES = [
|
|
327
307
|
"fast",
|
|
328
308
|
"quick",
|
|
@@ -550,13 +530,14 @@ function validatePremiseChallenge(sectionBody) {
|
|
|
550
530
|
if (rowCount < 3) {
|
|
551
531
|
return {
|
|
552
532
|
ok: false,
|
|
553
|
-
details: `Premise Challenge needs at least 3
|
|
533
|
+
details: `Premise Challenge needs at least 3 substantive rows in a table or bullet list. Found ${rowCount}.`
|
|
554
534
|
};
|
|
555
535
|
}
|
|
556
536
|
// For tables, each data row must have at least 2 non-empty cells so the
|
|
557
|
-
// section is genuinely a
|
|
558
|
-
// bullet lists, each line must be substantive
|
|
559
|
-
//
|
|
537
|
+
// section is genuinely a premise/answer comparison, not a list of headlines.
|
|
538
|
+
// For bullet lists, each line must be substantive so we don't accept
|
|
539
|
+
// placeholders like `- a`; punctuation style and natural language do not
|
|
540
|
+
// matter.
|
|
560
541
|
if (tableRows.length >= 3) {
|
|
561
542
|
const sparseRows = tableRows.filter((row) => {
|
|
562
543
|
const filledCells = row.filter((cell) => cell.replace(/[\s|]/gu, "").length >= 2);
|
|
@@ -572,14 +553,13 @@ function validatePremiseChallenge(sectionBody) {
|
|
|
572
553
|
else if (bulletRows.length >= 3) {
|
|
573
554
|
const sparseBullets = bulletRows.filter((line) => {
|
|
574
555
|
const cleaned = line.replace(/^[-*\d.\s]+/u, "").replace(/[`*_]/gu, "").trim();
|
|
575
|
-
const hasQuestionMark = /\?/u.test(cleaned);
|
|
576
556
|
const meaningful = cleaned.match(/[\p{L}\p{N}]/gu)?.length ?? 0;
|
|
577
|
-
return
|
|
557
|
+
return meaningful < 12;
|
|
578
558
|
});
|
|
579
|
-
if (sparseBullets.length >
|
|
559
|
+
if (sparseBullets.length > 0) {
|
|
580
560
|
return {
|
|
581
561
|
ok: false,
|
|
582
|
-
details: "Premise Challenge bullet list must include at least 3 substantive
|
|
562
|
+
details: "Premise Challenge bullet list must include at least 3 substantive rows, not placeholders."
|
|
583
563
|
};
|
|
584
564
|
}
|
|
585
565
|
}
|
|
@@ -1379,21 +1359,6 @@ function validateSectionBody(sectionBody, rule, sectionName) {
|
|
|
1379
1359
|
};
|
|
1380
1360
|
}
|
|
1381
1361
|
}
|
|
1382
|
-
if (sectionNameNormalized !== "architecture diagram") {
|
|
1383
|
-
const keywords = extractRequiredKeywords(rule);
|
|
1384
|
-
if (keywords.length > 0) {
|
|
1385
|
-
const bodyLower = sectionBody.toLowerCase();
|
|
1386
|
-
const found = keywords.filter((kw) => bodyLower.includes(kw.toLowerCase()));
|
|
1387
|
-
const threshold = Math.ceil(keywords.length * 0.5);
|
|
1388
|
-
if (found.length < threshold) {
|
|
1389
|
-
const missing = keywords.filter((kw) => !bodyLower.includes(kw.toLowerCase()));
|
|
1390
|
-
return {
|
|
1391
|
-
ok: false,
|
|
1392
|
-
details: `Rule expects keywords (${threshold}/${keywords.length} minimum): missing ${missing.join(", ")}.`
|
|
1393
|
-
};
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
1362
|
if (sectionNameNormalized === "acceptance criteria" &&
|
|
1398
1363
|
/observable[\s,]*measurable[\s,]+(and )?falsifiable/iu.test(rule)) {
|
|
1399
1364
|
const rows = getMarkdownTableRows(sectionBody);
|