cclaw-cli 0.51.2 → 0.51.4
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
CHANGED
|
@@ -1633,25 +1633,34 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1633
1633
|
// IDs all fail the lint; absence of the section remains advisory so
|
|
1634
1634
|
// scope stays optional for small/quick tracks.
|
|
1635
1635
|
if (headingPresent(sections, "Locked Decisions (D-XX)")) {
|
|
1636
|
-
const
|
|
1637
|
-
const bulletLines = lockedDecisionsBody
|
|
1636
|
+
const listDecisionLines = lockedDecisionsBody
|
|
1638
1637
|
.split(/\r?\n/u)
|
|
1639
1638
|
.map((line) => line.trim())
|
|
1640
|
-
.filter((line) => /^
|
|
1641
|
-
const
|
|
1639
|
+
.filter((line) => /^[-*]\s+\S/u.test(line));
|
|
1640
|
+
const tableDecisionRows = getMarkdownTableRows(lockedDecisionsBody);
|
|
1641
|
+
const tableDecisionLines = tableDecisionRows.map((row) => row.join(" | "));
|
|
1642
|
+
const decisionLines = [...listDecisionLines, ...tableDecisionLines];
|
|
1643
|
+
const orphanDecisionLines = decisionLines.filter((line) => !/\bD-\d+\b/u.test(line));
|
|
1644
|
+
const rowDecisionIds = [
|
|
1645
|
+
...listDecisionLines.map((line) => /\bD-\d+\b/u.exec(line)?.[0]),
|
|
1646
|
+
...tableDecisionRows.map((row) => /\bD-\d+\b/u.exec(row[0] ?? "")?.[0])
|
|
1647
|
+
].filter((id) => typeof id === "string");
|
|
1642
1648
|
const duplicateIds = (() => {
|
|
1643
|
-
const all = lockedDecisionsBody.match(/\bD-\d+\b/gu) ?? [];
|
|
1644
1649
|
const counts = new Map();
|
|
1645
|
-
for (const id of
|
|
1650
|
+
for (const id of rowDecisionIds)
|
|
1646
1651
|
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
1647
1652
|
return [...counts.entries()].filter(([, n]) => n > 1).map(([id]) => id);
|
|
1648
1653
|
})();
|
|
1649
1654
|
const issues = [];
|
|
1650
|
-
if (
|
|
1655
|
+
if (rowDecisionIds.length === 0 && decisionLines.length === 0) {
|
|
1651
1656
|
issues.push("section is empty");
|
|
1652
1657
|
}
|
|
1653
|
-
if (
|
|
1654
|
-
|
|
1658
|
+
if (orphanDecisionLines.length > 0) {
|
|
1659
|
+
const examples = orphanDecisionLines
|
|
1660
|
+
.slice(0, 3)
|
|
1661
|
+
.map((line) => `\`${line.slice(0, 120)}\``)
|
|
1662
|
+
.join(", ");
|
|
1663
|
+
issues.push(`${orphanDecisionLines.length} decision row(s) missing a D-XX ID${examples.length > 0 ? `: ${examples}` : ""}`);
|
|
1655
1664
|
}
|
|
1656
1665
|
if (duplicateIds.length > 0) {
|
|
1657
1666
|
issues.push(`duplicate IDs: ${duplicateIds.join(", ")}`);
|
|
@@ -1662,7 +1671,7 @@ export async function lintArtifact(projectRoot, stage, track = "standard") {
|
|
|
1662
1671
|
rule: "Locked Decisions section must list each decision with a unique stable D-XX ID.",
|
|
1663
1672
|
found: issues.length === 0,
|
|
1664
1673
|
details: issues.length === 0
|
|
1665
|
-
? `${
|
|
1674
|
+
? `${rowDecisionIds.length} decision ID(s) recorded with no duplicates.`
|
|
1666
1675
|
: issues.join("; ")
|
|
1667
1676
|
});
|
|
1668
1677
|
}
|
|
@@ -48,7 +48,7 @@ export const BRAINSTORM = {
|
|
|
48
48
|
],
|
|
49
49
|
interactionProtocol: [
|
|
50
50
|
"Start from observed project context; if the idea is vague, first narrow the project type.",
|
|
51
|
-
"Ask at most one question per turn, only when decision-changing;
|
|
51
|
+
"Ask at most one question per turn, only when decision-changing; if using a structured question tool, send exactly one question object, not a multi-question form.",
|
|
52
52
|
"If likely answers do not change architecture or scope boundaries, choose the default and state the assumption.",
|
|
53
53
|
"Show approaches before the recommendation; include a higher-upside challenger and gather reaction first.",
|
|
54
54
|
"State exactly what is being approved, then **STOP** until the user explicitly approves the artifact."
|
|
@@ -119,9 +119,9 @@ export const BRAINSTORM = {
|
|
|
119
119
|
{ section: "Clarifying Questions", required: false, validationRule: "Must capture question, answer, and decision impact for each clarifying question." },
|
|
120
120
|
{ section: "Approach Tier", required: true, validationRule: "Must classify depth as Lightweight/Standard/Deep and explain why." },
|
|
121
121
|
{ section: "Short-Circuit Decision", required: false, validationRule: "Must include Status/Why/Scope handoff lines when short-circuit is discussed; compact stubs are valid for concrete asks." },
|
|
122
|
-
{ section: "Approaches", required: true, validationRule: "Must compare 2-3 architecturally distinct options with real trade-offs
|
|
123
|
-
{ section: "Approach Reaction", required: true, validationRule: "Must summarize user reaction before recommendation, including
|
|
124
|
-
{ section: "Selected Direction", required: true, validationRule: "Must include the selected approach, rationale tied to user reaction/feedback
|
|
122
|
+
{ section: "Approaches", required: true, validationRule: "Must compare 2-3 architecturally distinct options with real trade-offs; include at least one table/bullet row containing both `challenger` and `higher-upside`." },
|
|
123
|
+
{ section: "Approach Reaction", required: true, validationRule: "Must appear before Selected Direction and summarize user reaction before recommendation, including `Closest option`, `Concerns`, and what changed after reaction." },
|
|
124
|
+
{ section: "Selected Direction", required: true, validationRule: "Must include the selected approach, an explicit approval marker, and rationale tied to user reaction/feedback/concerns so the recommendation traces to the user response." },
|
|
125
125
|
{ section: "Design", required: false, validationRule: "Must cover architecture, key components, and data flow scaled to complexity." },
|
|
126
126
|
{ section: "Visual Companion", required: false, validationRule: "If architecture/data-flow complexity is medium+, include compact ASCII/Mermaid diagram or explicitly justify omission." },
|
|
127
127
|
{ section: "Assumptions and Open Questions", required: false, validationRule: "Must capture unresolved assumptions/open questions, or explicitly state none." }
|
package/dist/gate-evidence.js
CHANGED
|
@@ -215,11 +215,21 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
215
215
|
if (shouldValidateArtifact) {
|
|
216
216
|
const lint = await lintArtifact(projectRoot, stage, flowState.track);
|
|
217
217
|
if (!lint.passed) {
|
|
218
|
-
const
|
|
219
|
-
.filter((finding) => finding.required && !finding.found)
|
|
220
|
-
|
|
218
|
+
const failedRequiredFindings = lint.findings
|
|
219
|
+
.filter((finding) => finding.required && !finding.found);
|
|
220
|
+
const failedRequired = failedRequiredFindings.map((finding) => finding.section);
|
|
221
221
|
if (failedRequired.length > 0) {
|
|
222
|
-
|
|
222
|
+
const failureDetails = failedRequiredFindings
|
|
223
|
+
.map((finding) => {
|
|
224
|
+
const details = finding.details?.trim();
|
|
225
|
+
const rule = finding.rule?.trim();
|
|
226
|
+
const explanation = details && details.length > 0 ? details : rule;
|
|
227
|
+
return explanation && explanation.length > 0
|
|
228
|
+
? `${finding.section}: ${explanation}`
|
|
229
|
+
: finding.section;
|
|
230
|
+
})
|
|
231
|
+
.join("; ");
|
|
232
|
+
issues.push(`artifact validation failed for required sections: ${failedRequired.join(", ")}. ${failureDetails}`);
|
|
223
233
|
}
|
|
224
234
|
}
|
|
225
235
|
if (stage === "review") {
|