cclaw-cli 0.51.11 → 0.51.12

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.
@@ -348,35 +348,6 @@ function getMarkdownTableRows(sectionBody) {
348
348
  }
349
349
  return rows;
350
350
  }
351
- function getApproachRows(sectionBody) {
352
- const tableRows = getMarkdownTableRows(sectionBody).map((row) => row.join(" "));
353
- const headingRows = sectionBody
354
- .split(/\r?\n/u)
355
- .map((line) => line.trim())
356
- .filter((line) => /^#{3,6}\s+\S/u.test(line))
357
- .map((line) => line.replace(/^#{3,6}\s+/u, ""));
358
- const bulletRows = sectionBody
359
- .split(/\r?\n/u)
360
- .map((line) => line.trim())
361
- .filter((line) => /^(?:[-*]|\d+\.)\s+\S/u.test(line));
362
- return [...tableRows, ...headingRows, ...bulletRows];
363
- }
364
- function hasSemanticChallenger(row) {
365
- const normalized = row
366
- .replace(/[_`*]/gu, " ")
367
- .replace(/\s+/gu, " ")
368
- .trim()
369
- .toLowerCase();
370
- const isChallenger = /\bchallenger\b/u.test(normalized);
371
- if (!isChallenger)
372
- return false;
373
- return (/\bhigher[-\s]?upside\b/u.test(normalized) ||
374
- /\bhigh[-\s]?upside\b/u.test(normalized) ||
375
- /\bupside\s*:?\s*(?:high|higher|strong|large|meaningful)\b/u.test(normalized) ||
376
- /\b(?:high|higher|strong|large|meaningful)\s+upside\b/u.test(normalized) ||
377
- /\b(?:10-star|ten-star|ambitious|higher leverage|leverage)\b/u.test(normalized) ||
378
- /\bhigh\b/u.test(normalized));
379
- }
380
351
  function parseBinaryFlag(value) {
381
352
  const normalized = value.trim().toLowerCase();
382
353
  if (/^(?:y|yes|true|1)$/u.test(normalized))
@@ -596,6 +567,114 @@ function validateScopeSummary(sectionBody) {
596
567
  details: "Scope Summary names the selected mode and the next-stage handoff."
597
568
  };
598
569
  }
570
+ const APPROACH_ROLE_VALUES = ["baseline", "challenger", "wild-card"];
571
+ const APPROACH_UPSIDE_VALUES = ["low", "modest", "high", "higher"];
572
+ const REQUIREMENT_PRIORITY_VALUES = ["P0", "P1", "P2", "P3", "DROPPED"];
573
+ function normalizeTableToken(value) {
574
+ return value
575
+ .replace(/[`*_]/gu, "")
576
+ .trim()
577
+ .toLowerCase()
578
+ .replace(/\s+/gu, "-");
579
+ }
580
+ function columnIndex(header, expected) {
581
+ return header.findIndex((cell) => normalizeTableToken(cell) === expected);
582
+ }
583
+ function validateApproachesTaxonomy(sectionBody) {
584
+ const header = tableHeaderCells(sectionBody);
585
+ const rows = getMarkdownTableRows(sectionBody);
586
+ if (!header) {
587
+ return {
588
+ rowCount: 0,
589
+ roleUpsideOk: false,
590
+ challengerOk: false,
591
+ details: "Approaches must be a markdown table with canonical Role and Upside columns."
592
+ };
593
+ }
594
+ const roleIndex = columnIndex(header, "role");
595
+ const upsideIndex = columnIndex(header, "upside");
596
+ if (roleIndex < 0 || upsideIndex < 0) {
597
+ return {
598
+ rowCount: rows.length,
599
+ roleUpsideOk: false,
600
+ challengerOk: false,
601
+ details: "Approaches table must include canonical `Role` and `Upside` columns (Role: baseline | challenger | wild-card; Upside: low | modest | high | higher)."
602
+ };
603
+ }
604
+ let challengerRows = 0;
605
+ let challengerHasHighUpside = false;
606
+ for (const [index, row] of rows.entries()) {
607
+ const role = normalizeTableToken(row[roleIndex] ?? "");
608
+ const upside = normalizeTableToken(row[upsideIndex] ?? "");
609
+ if (!APPROACH_ROLE_VALUES.includes(role)) {
610
+ return {
611
+ rowCount: rows.length,
612
+ roleUpsideOk: false,
613
+ challengerOk: false,
614
+ details: `Approaches row ${index + 1} has invalid Role "${row[roleIndex] ?? ""}". Expected one of: ${APPROACH_ROLE_VALUES.join(", ")}.`
615
+ };
616
+ }
617
+ if (!APPROACH_UPSIDE_VALUES.includes(upside)) {
618
+ return {
619
+ rowCount: rows.length,
620
+ roleUpsideOk: false,
621
+ challengerOk: false,
622
+ details: `Approaches row ${index + 1} has invalid Upside "${row[upsideIndex] ?? ""}". Expected one of: ${APPROACH_UPSIDE_VALUES.join(", ")}.`
623
+ };
624
+ }
625
+ if (role === "challenger") {
626
+ challengerRows += 1;
627
+ if (upside === "high" || upside === "higher") {
628
+ challengerHasHighUpside = true;
629
+ }
630
+ }
631
+ }
632
+ const challengerOk = challengerRows === 1 && challengerHasHighUpside;
633
+ return {
634
+ rowCount: rows.length,
635
+ roleUpsideOk: true,
636
+ challengerOk,
637
+ details: challengerOk
638
+ ? "Approaches table uses canonical Role/Upside values and exactly one high/higher-upside challenger."
639
+ : `Approaches table must include exactly one challenger row with Upside high or higher. Found ${challengerRows} challenger row(s).`
640
+ };
641
+ }
642
+ function validateRequirementsTaxonomy(sectionBody) {
643
+ const header = tableHeaderCells(sectionBody);
644
+ if (!header) {
645
+ return {
646
+ ok: false,
647
+ details: "Requirements must be a markdown table with a Priority column."
648
+ };
649
+ }
650
+ const priorityIndex = columnIndex(header, "priority");
651
+ if (priorityIndex < 0) {
652
+ return {
653
+ ok: false,
654
+ details: "Requirements table must include a canonical `Priority` column."
655
+ };
656
+ }
657
+ const rows = getMarkdownTableRows(sectionBody);
658
+ if (rows.length === 0) {
659
+ return {
660
+ ok: false,
661
+ details: "Requirements table must include at least one requirement row."
662
+ };
663
+ }
664
+ for (const [index, row] of rows.entries()) {
665
+ const rawPriority = (row[priorityIndex] ?? "").replace(/[`*_]/gu, "").trim().toUpperCase();
666
+ if (!REQUIREMENT_PRIORITY_VALUES.includes(rawPriority)) {
667
+ return {
668
+ ok: false,
669
+ details: `Requirements row ${index + 1} has invalid Priority "${row[priorityIndex] ?? ""}". Expected one of: ${REQUIREMENT_PRIORITY_VALUES.join(", ")}.`
670
+ };
671
+ }
672
+ }
673
+ return {
674
+ ok: true,
675
+ details: "Requirements table uses canonical Priority values."
676
+ };
677
+ }
599
678
  const INTERACTION_EDGE_CASE_REQUIREMENTS = [
600
679
  { label: "double-click", pattern: /\bdouble[\s-]?click\b/iu },
601
680
  {
@@ -1322,6 +1401,9 @@ function validateSectionBody(sectionBody, rule, sectionName) {
1322
1401
  if (sectionNameNormalized === "premise challenge") {
1323
1402
  return validatePremiseChallenge(sectionBody);
1324
1403
  }
1404
+ if (sectionNameNormalized === "requirements") {
1405
+ return validateRequirementsTaxonomy(sectionBody);
1406
+ }
1325
1407
  if (sectionNameNormalized === "data flow") {
1326
1408
  return validateInteractionEdgeCaseMatrix(sectionBody);
1327
1409
  }
@@ -1533,31 +1615,29 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
1533
1615
  }
1534
1616
  const approachesBody = sectionBodyByName(sections, "Approaches");
1535
1617
  if (approachesBody !== null) {
1536
- const tableRows = getMarkdownTableRows(approachesBody);
1537
- const bulletRows = approachesBody
1538
- .split(/\r?\n/u)
1539
- .map((line) => line.trim())
1540
- .filter((line) => /^(?:[-*]|\d+\.)\s+\S/u.test(line));
1541
- const rowCount = Math.max(tableRows.length, bulletRows.length);
1542
- const approachRows = getApproachRows(approachesBody);
1543
- const hasChallenger = approachRows.some(hasSemanticChallenger);
1618
+ const approachesTaxonomy = validateApproachesTaxonomy(approachesBody);
1544
1619
  findings.push({
1545
1620
  section: "Distinct Approaches Enforcement",
1546
1621
  required: true,
1547
1622
  rule: "Approaches section must document at least 2 distinct approaches so the Iron Law comparison is meaningful.",
1548
- found: rowCount >= 2,
1549
- details: rowCount >= 2
1550
- ? `Detected ${rowCount} approach row(s).`
1551
- : `Detected ${rowCount} approach row(s); at least 2 required.`
1623
+ found: approachesTaxonomy.rowCount >= 2,
1624
+ details: approachesTaxonomy.rowCount >= 2
1625
+ ? `Detected ${approachesTaxonomy.rowCount} approach row(s).`
1626
+ : `Detected ${approachesTaxonomy.rowCount} approach row(s); at least 2 required.`
1627
+ });
1628
+ findings.push({
1629
+ section: "Approaches Role/Upside Taxonomy",
1630
+ required: true,
1631
+ rule: "Approaches table must use canonical Role and Upside enum values.",
1632
+ found: approachesTaxonomy.roleUpsideOk,
1633
+ details: approachesTaxonomy.details
1552
1634
  });
1553
1635
  findings.push({
1554
1636
  section: "Challenger Alternative Enforcement",
1555
1637
  required: true,
1556
1638
  rule: "Approaches must include one challenger option with explicit high/higher upside.",
1557
- found: hasChallenger,
1558
- details: hasChallenger
1559
- ? "Semantic challenger with high/higher upside detected."
1560
- : "Missing a challenger option with explicit high/higher upside. Example: `| C | challenger | high upside | More ambitious path with clear trade-offs |`."
1639
+ found: approachesTaxonomy.challengerOk,
1640
+ details: approachesTaxonomy.details
1561
1641
  });
1562
1642
  }
1563
1643
  const reactionIndex = headingLineIndex(raw, "Approach Reaction");
@@ -31,11 +31,11 @@ const STAGE_EXAMPLES = {
31
31
 
32
32
  ## Approaches
33
33
 
34
- | Approach | Role | Architecture | Trade-offs | Recommendation |
35
- | --- | --- | --- | --- | --- |
36
- | A: Reusable validation module | baseline | Shared TS module with typed validators, imported by CI scripts and local CLI. Existing \`pre-publish.sh\` calls the module. | Medium upfront effort, high reuse. Requires test coverage for the module. | **Recommended** — best balance of reuse and delivery speed. |
37
- | B: Hardened shell scripts | fallback | Keep existing script approach, add stricter checks and error messages. | Lowest effort. Weak reuse, CI/local divergence risk grows over time. | Viable fallback if TS module is blocked. |
38
- | C: Full release framework | challenger: higher-upside | New release orchestrator with plugin system, config files, rollback commands. | Maximum flexibility. High risk, delivery delay, over-engineered for current needs. | Not recommended for v1. |
34
+ | Approach | Role | Upside | Architecture | Trade-offs | Recommendation |
35
+ | --- | --- | --- | --- | --- | --- |
36
+ | A: Reusable validation module | baseline | high | Shared TS module with typed validators, imported by CI scripts and local CLI. Existing \`pre-publish.sh\` calls the module. | Medium upfront effort, high reuse. Requires test coverage for the module. | **Recommended** — best balance of reuse and delivery speed. |
37
+ | B: Hardened shell scripts | baseline | modest | Keep existing script approach, add stricter checks and error messages. | Lowest effort. Weak reuse, CI/local divergence risk grows over time. | Viable fallback if TS module is blocked. |
38
+ | C: Full release framework | challenger | higher | New release orchestrator with plugin system, config files, rollback commands. | Maximum flexibility. High risk, delivery delay, over-engineered for current needs. | Not recommended for v1. |
39
39
 
40
40
  ## Approach Reaction
41
41
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.51.11",
3
+ "version": "0.51.12",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {