@attalabs/vinaya 0.2.0 → 0.3.0
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/aeg-root/enforcement.md +2 -2
- package/aeg-root/roles/archivist.md +1 -1
- package/aeg-root/roles/developer.md +2 -2
- package/aeg-root/state-machine.md +17 -3
- package/aeg-root/tranche-model.md +5 -3
- package/dist/checks/bin/check-branch-topology.js +69 -11
- package/dist/checks/bin/check-brief-shape.js +69 -11
- package/dist/checks/bin/check-closes-n.js +69 -11
- package/dist/checks/bin/check-coherence.js +69 -11
- package/dist/checks/bin/check-dead-branch-push.js +69 -11
- package/dist/checks/bin/check-dispatch-readiness.js +69 -11
- package/dist/checks/bin/check-doc-coverage-push.js +69 -11
- package/dist/checks/bin/check-doc-coverage.js +69 -11
- package/dist/checks/bin/check-first-push-dispatch.js +69 -11
- package/dist/checks/bin/check-issue-assignment.js +69 -11
- package/dist/checks/bin/check-no-disk-state.js +69 -11
- package/dist/checks/bin/check-reader-resolvable-prose.js +69 -11
- package/dist/checks/bin/check-registry-gates.js +69 -11
- package/dist/checks/bin/check-review-gate.js +69 -11
- package/dist/checks/bin/check-single-plan-pr.js +69 -11
- package/dist/checks/bin/check-test-plan.js +69 -11
- package/dist/index.js +104 -38
- package/package.json +1 -1
|
@@ -1513,15 +1513,58 @@ function pointerToPath(p) {
|
|
|
1513
1513
|
return idx === -1 ? p : p.slice(0, idx);
|
|
1514
1514
|
}
|
|
1515
1515
|
var SEPARATOR = /(?:[ \t]*[—–][ \t]*|[ \t]+-[ \t]+)/.source;
|
|
1516
|
-
function
|
|
1517
|
-
const
|
|
1518
|
-
const re = new RegExp(`^[ \\t]
|
|
1516
|
+
function readPointerNoteField(body, field) {
|
|
1517
|
+
const out = [];
|
|
1518
|
+
const re = new RegExp(`^[ \\t]*${field}[ \\t]*:[ \\t]*(.+?)${SEPARATOR}(.+?)[ \\t]*$`, "gim");
|
|
1519
1519
|
for (const m of body.matchAll(re)) {
|
|
1520
|
-
|
|
1520
|
+
out.push({ surface: (m[1] ?? "").trim(), note: (m[2] ?? "").trim() });
|
|
1521
|
+
}
|
|
1522
|
+
return out;
|
|
1523
|
+
}
|
|
1524
|
+
function readDocAcks(body) {
|
|
1525
|
+
return readPointerNoteField(body, "Doc-ack");
|
|
1526
|
+
}
|
|
1527
|
+
function readDocNeutrals(body) {
|
|
1528
|
+
return readPointerNoteField(body, "Doc-neutral");
|
|
1529
|
+
}
|
|
1530
|
+
var LANGUAGE_COMMENT_PREFIXES = [
|
|
1531
|
+
{ test: /\.(ts|tsx|js|jsx|mjs|cjs)$/, prefixes: ["//"] },
|
|
1532
|
+
{ test: /(^|\/)(pre-push|pre-commit|pre-merge-commit)$/, prefixes: ["#"] },
|
|
1533
|
+
{ test: /\.(sh|bash)$/, prefixes: ["#"] },
|
|
1534
|
+
{ test: /\.ya?ml$/, prefixes: ["#"] },
|
|
1535
|
+
{ test: /\.py$/, prefixes: ["#"] }
|
|
1536
|
+
];
|
|
1537
|
+
function commentPrefixesForPath(path) {
|
|
1538
|
+
for (const { test, prefixes } of LANGUAGE_COMMENT_PREFIXES) {
|
|
1539
|
+
if (test.test(path))
|
|
1540
|
+
return prefixes;
|
|
1541
|
+
}
|
|
1542
|
+
return [];
|
|
1543
|
+
}
|
|
1544
|
+
function isNeutralDiffContentLine(line, prefixes) {
|
|
1545
|
+
const t = line.trim();
|
|
1546
|
+
if (t === "")
|
|
1547
|
+
return true;
|
|
1548
|
+
if (t.startsWith("#!"))
|
|
1549
|
+
return false;
|
|
1550
|
+
return prefixes.some((p) => t.startsWith(p));
|
|
1551
|
+
}
|
|
1552
|
+
function isMechanicallyNeutralDiff(diffText, path) {
|
|
1553
|
+
const prefixes = commentPrefixesForPath(path);
|
|
1554
|
+
let sawChange = false;
|
|
1555
|
+
for (const raw of diffText.split(`
|
|
1556
|
+
`)) {
|
|
1557
|
+
if (raw.startsWith("+++ ") || raw.startsWith("--- ") || raw.startsWith("@@"))
|
|
1558
|
+
continue;
|
|
1559
|
+
if (raw.startsWith("+") || raw.startsWith("-")) {
|
|
1560
|
+
if (!isNeutralDiffContentLine(raw.slice(1), prefixes))
|
|
1561
|
+
return false;
|
|
1562
|
+
sawChange = true;
|
|
1563
|
+
}
|
|
1521
1564
|
}
|
|
1522
|
-
return
|
|
1565
|
+
return sawChange;
|
|
1523
1566
|
}
|
|
1524
|
-
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive) {
|
|
1567
|
+
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive, getDiff) {
|
|
1525
1568
|
const out = { errors: [], notes: [] };
|
|
1526
1569
|
if (docOwnersContent === null)
|
|
1527
1570
|
return out;
|
|
@@ -1534,13 +1577,15 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1534
1577
|
const fired = [];
|
|
1535
1578
|
for (const b of bindings) {
|
|
1536
1579
|
const re = globToRegex(b.glob);
|
|
1537
|
-
|
|
1538
|
-
|
|
1580
|
+
const matchedFiles = codeFiles.filter((f) => re.test(f));
|
|
1581
|
+
if (matchedFiles.length > 0)
|
|
1582
|
+
fired.push({ binding: b, matchedFiles });
|
|
1539
1583
|
}
|
|
1540
1584
|
if (fired.length === 0)
|
|
1541
1585
|
return out;
|
|
1542
1586
|
const acks = readDocAcks(prBody);
|
|
1543
|
-
|
|
1587
|
+
const neutrals = readDocNeutrals(prBody);
|
|
1588
|
+
for (const { binding: b, matchedFiles } of fired) {
|
|
1544
1589
|
if (waiverActive) {
|
|
1545
1590
|
out.notes.push(`C5 doc-waiver active for ${b.pointer} (binding ${DOC_OWNERS_PATH}:${b.lineNum})`);
|
|
1546
1591
|
continue;
|
|
@@ -1557,9 +1602,22 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1557
1602
|
out.errors.push(`C5 doc-owners-dangling: ${DOC_OWNERS_PATH}:${b.lineNum} points to ${b.pointer}, which does not exist on disk. Fix the binding or add the doc.`);
|
|
1558
1603
|
continue;
|
|
1559
1604
|
}
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1605
|
+
if (changed.includes(pointerPath))
|
|
1606
|
+
continue;
|
|
1607
|
+
const declared = neutrals.find((n) => n.surface === b.pointer);
|
|
1608
|
+
if (declared) {
|
|
1609
|
+
const evidenced = getDiff !== undefined && matchedFiles.every((f) => {
|
|
1610
|
+
const diff = getDiff(f);
|
|
1611
|
+
return diff !== null && isMechanicallyNeutralDiff(diff, f);
|
|
1612
|
+
});
|
|
1613
|
+
if (evidenced) {
|
|
1614
|
+
out.notes.push(`C5 doc-neutral: ${b.pointer} not required for ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\`) — declared neutral ("${declared.note}"), diff of ${matchedFiles.join(", ")} confirmed comment/whitespace-only.`);
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
out.errors.push(`C5 doc-neutral-unverified: ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}) declared \`Doc-neutral: ${b.pointer} — ${declared.note}\`, but the diff of ${matchedFiles.join(", ")} contains changes beyond comments/whitespace. Update ${pointerPath}, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1618
|
+
continue;
|
|
1562
1619
|
}
|
|
1620
|
+
out.errors.push(`C5 doc-coverage: code change matched ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}), but ${pointerPath} is not in the PR diff. Update it, declare \`Doc-neutral: ${b.pointer} — <why>\` if the change is genuinely mechanically-neutral, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1563
1621
|
}
|
|
1564
1622
|
return out;
|
|
1565
1623
|
}
|
|
@@ -1515,15 +1515,58 @@ function pointerToPath(p) {
|
|
|
1515
1515
|
return idx === -1 ? p : p.slice(0, idx);
|
|
1516
1516
|
}
|
|
1517
1517
|
var SEPARATOR = /(?:[ \t]*[—–][ \t]*|[ \t]+-[ \t]+)/.source;
|
|
1518
|
-
function
|
|
1519
|
-
const
|
|
1520
|
-
const re = new RegExp(`^[ \\t]
|
|
1518
|
+
function readPointerNoteField(body, field) {
|
|
1519
|
+
const out = [];
|
|
1520
|
+
const re = new RegExp(`^[ \\t]*${field}[ \\t]*:[ \\t]*(.+?)${SEPARATOR}(.+?)[ \\t]*$`, "gim");
|
|
1521
1521
|
for (const m of body.matchAll(re)) {
|
|
1522
|
-
|
|
1522
|
+
out.push({ surface: (m[1] ?? "").trim(), note: (m[2] ?? "").trim() });
|
|
1523
|
+
}
|
|
1524
|
+
return out;
|
|
1525
|
+
}
|
|
1526
|
+
function readDocAcks(body) {
|
|
1527
|
+
return readPointerNoteField(body, "Doc-ack");
|
|
1528
|
+
}
|
|
1529
|
+
function readDocNeutrals(body) {
|
|
1530
|
+
return readPointerNoteField(body, "Doc-neutral");
|
|
1531
|
+
}
|
|
1532
|
+
var LANGUAGE_COMMENT_PREFIXES = [
|
|
1533
|
+
{ test: /\.(ts|tsx|js|jsx|mjs|cjs)$/, prefixes: ["//"] },
|
|
1534
|
+
{ test: /(^|\/)(pre-push|pre-commit|pre-merge-commit)$/, prefixes: ["#"] },
|
|
1535
|
+
{ test: /\.(sh|bash)$/, prefixes: ["#"] },
|
|
1536
|
+
{ test: /\.ya?ml$/, prefixes: ["#"] },
|
|
1537
|
+
{ test: /\.py$/, prefixes: ["#"] }
|
|
1538
|
+
];
|
|
1539
|
+
function commentPrefixesForPath(path) {
|
|
1540
|
+
for (const { test, prefixes } of LANGUAGE_COMMENT_PREFIXES) {
|
|
1541
|
+
if (test.test(path))
|
|
1542
|
+
return prefixes;
|
|
1523
1543
|
}
|
|
1524
|
-
return
|
|
1544
|
+
return [];
|
|
1525
1545
|
}
|
|
1526
|
-
function
|
|
1546
|
+
function isNeutralDiffContentLine(line, prefixes) {
|
|
1547
|
+
const t = line.trim();
|
|
1548
|
+
if (t === "")
|
|
1549
|
+
return true;
|
|
1550
|
+
if (t.startsWith("#!"))
|
|
1551
|
+
return false;
|
|
1552
|
+
return prefixes.some((p) => t.startsWith(p));
|
|
1553
|
+
}
|
|
1554
|
+
function isMechanicallyNeutralDiff(diffText, path) {
|
|
1555
|
+
const prefixes = commentPrefixesForPath(path);
|
|
1556
|
+
let sawChange = false;
|
|
1557
|
+
for (const raw of diffText.split(`
|
|
1558
|
+
`)) {
|
|
1559
|
+
if (raw.startsWith("+++ ") || raw.startsWith("--- ") || raw.startsWith("@@"))
|
|
1560
|
+
continue;
|
|
1561
|
+
if (raw.startsWith("+") || raw.startsWith("-")) {
|
|
1562
|
+
if (!isNeutralDiffContentLine(raw.slice(1), prefixes))
|
|
1563
|
+
return false;
|
|
1564
|
+
sawChange = true;
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
return sawChange;
|
|
1568
|
+
}
|
|
1569
|
+
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive, getDiff) {
|
|
1527
1570
|
const out = { errors: [], notes: [] };
|
|
1528
1571
|
if (docOwnersContent === null)
|
|
1529
1572
|
return out;
|
|
@@ -1536,13 +1579,15 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1536
1579
|
const fired = [];
|
|
1537
1580
|
for (const b of bindings) {
|
|
1538
1581
|
const re = globToRegex(b.glob);
|
|
1539
|
-
|
|
1540
|
-
|
|
1582
|
+
const matchedFiles = codeFiles.filter((f) => re.test(f));
|
|
1583
|
+
if (matchedFiles.length > 0)
|
|
1584
|
+
fired.push({ binding: b, matchedFiles });
|
|
1541
1585
|
}
|
|
1542
1586
|
if (fired.length === 0)
|
|
1543
1587
|
return out;
|
|
1544
1588
|
const acks = readDocAcks(prBody);
|
|
1545
|
-
|
|
1589
|
+
const neutrals = readDocNeutrals(prBody);
|
|
1590
|
+
for (const { binding: b, matchedFiles } of fired) {
|
|
1546
1591
|
if (waiverActive) {
|
|
1547
1592
|
out.notes.push(`C5 doc-waiver active for ${b.pointer} (binding ${DOC_OWNERS_PATH}:${b.lineNum})`);
|
|
1548
1593
|
continue;
|
|
@@ -1559,9 +1604,22 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1559
1604
|
out.errors.push(`C5 doc-owners-dangling: ${DOC_OWNERS_PATH}:${b.lineNum} points to ${b.pointer}, which does not exist on disk. Fix the binding or add the doc.`);
|
|
1560
1605
|
continue;
|
|
1561
1606
|
}
|
|
1562
|
-
if (
|
|
1563
|
-
|
|
1607
|
+
if (changed.includes(pointerPath))
|
|
1608
|
+
continue;
|
|
1609
|
+
const declared = neutrals.find((n) => n.surface === b.pointer);
|
|
1610
|
+
if (declared) {
|
|
1611
|
+
const evidenced = getDiff !== undefined && matchedFiles.every((f) => {
|
|
1612
|
+
const diff = getDiff(f);
|
|
1613
|
+
return diff !== null && isMechanicallyNeutralDiff(diff, f);
|
|
1614
|
+
});
|
|
1615
|
+
if (evidenced) {
|
|
1616
|
+
out.notes.push(`C5 doc-neutral: ${b.pointer} not required for ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\`) — declared neutral ("${declared.note}"), diff of ${matchedFiles.join(", ")} confirmed comment/whitespace-only.`);
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
out.errors.push(`C5 doc-neutral-unverified: ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}) declared \`Doc-neutral: ${b.pointer} — ${declared.note}\`, but the diff of ${matchedFiles.join(", ")} contains changes beyond comments/whitespace. Update ${pointerPath}, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1620
|
+
continue;
|
|
1564
1621
|
}
|
|
1622
|
+
out.errors.push(`C5 doc-coverage: code change matched ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}), but ${pointerPath} is not in the PR diff. Update it, declare \`Doc-neutral: ${b.pointer} — <why>\` if the change is genuinely mechanically-neutral, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1565
1623
|
}
|
|
1566
1624
|
return out;
|
|
1567
1625
|
}
|
|
@@ -1515,15 +1515,58 @@ function pointerToPath(p) {
|
|
|
1515
1515
|
return idx === -1 ? p : p.slice(0, idx);
|
|
1516
1516
|
}
|
|
1517
1517
|
var SEPARATOR = /(?:[ \t]*[—–][ \t]*|[ \t]+-[ \t]+)/.source;
|
|
1518
|
-
function
|
|
1519
|
-
const
|
|
1520
|
-
const re = new RegExp(`^[ \\t]
|
|
1518
|
+
function readPointerNoteField(body, field) {
|
|
1519
|
+
const out = [];
|
|
1520
|
+
const re = new RegExp(`^[ \\t]*${field}[ \\t]*:[ \\t]*(.+?)${SEPARATOR}(.+?)[ \\t]*$`, "gim");
|
|
1521
1521
|
for (const m of body.matchAll(re)) {
|
|
1522
|
-
|
|
1522
|
+
out.push({ surface: (m[1] ?? "").trim(), note: (m[2] ?? "").trim() });
|
|
1523
|
+
}
|
|
1524
|
+
return out;
|
|
1525
|
+
}
|
|
1526
|
+
function readDocAcks(body) {
|
|
1527
|
+
return readPointerNoteField(body, "Doc-ack");
|
|
1528
|
+
}
|
|
1529
|
+
function readDocNeutrals(body) {
|
|
1530
|
+
return readPointerNoteField(body, "Doc-neutral");
|
|
1531
|
+
}
|
|
1532
|
+
var LANGUAGE_COMMENT_PREFIXES = [
|
|
1533
|
+
{ test: /\.(ts|tsx|js|jsx|mjs|cjs)$/, prefixes: ["//"] },
|
|
1534
|
+
{ test: /(^|\/)(pre-push|pre-commit|pre-merge-commit)$/, prefixes: ["#"] },
|
|
1535
|
+
{ test: /\.(sh|bash)$/, prefixes: ["#"] },
|
|
1536
|
+
{ test: /\.ya?ml$/, prefixes: ["#"] },
|
|
1537
|
+
{ test: /\.py$/, prefixes: ["#"] }
|
|
1538
|
+
];
|
|
1539
|
+
function commentPrefixesForPath(path) {
|
|
1540
|
+
for (const { test, prefixes } of LANGUAGE_COMMENT_PREFIXES) {
|
|
1541
|
+
if (test.test(path))
|
|
1542
|
+
return prefixes;
|
|
1543
|
+
}
|
|
1544
|
+
return [];
|
|
1545
|
+
}
|
|
1546
|
+
function isNeutralDiffContentLine(line, prefixes) {
|
|
1547
|
+
const t = line.trim();
|
|
1548
|
+
if (t === "")
|
|
1549
|
+
return true;
|
|
1550
|
+
if (t.startsWith("#!"))
|
|
1551
|
+
return false;
|
|
1552
|
+
return prefixes.some((p) => t.startsWith(p));
|
|
1553
|
+
}
|
|
1554
|
+
function isMechanicallyNeutralDiff(diffText, path) {
|
|
1555
|
+
const prefixes = commentPrefixesForPath(path);
|
|
1556
|
+
let sawChange = false;
|
|
1557
|
+
for (const raw of diffText.split(`
|
|
1558
|
+
`)) {
|
|
1559
|
+
if (raw.startsWith("+++ ") || raw.startsWith("--- ") || raw.startsWith("@@"))
|
|
1560
|
+
continue;
|
|
1561
|
+
if (raw.startsWith("+") || raw.startsWith("-")) {
|
|
1562
|
+
if (!isNeutralDiffContentLine(raw.slice(1), prefixes))
|
|
1563
|
+
return false;
|
|
1564
|
+
sawChange = true;
|
|
1565
|
+
}
|
|
1523
1566
|
}
|
|
1524
|
-
return
|
|
1567
|
+
return sawChange;
|
|
1525
1568
|
}
|
|
1526
|
-
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive) {
|
|
1569
|
+
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive, getDiff) {
|
|
1527
1570
|
const out = { errors: [], notes: [] };
|
|
1528
1571
|
if (docOwnersContent === null)
|
|
1529
1572
|
return out;
|
|
@@ -1536,13 +1579,15 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1536
1579
|
const fired = [];
|
|
1537
1580
|
for (const b of bindings) {
|
|
1538
1581
|
const re = globToRegex(b.glob);
|
|
1539
|
-
|
|
1540
|
-
|
|
1582
|
+
const matchedFiles = codeFiles.filter((f) => re.test(f));
|
|
1583
|
+
if (matchedFiles.length > 0)
|
|
1584
|
+
fired.push({ binding: b, matchedFiles });
|
|
1541
1585
|
}
|
|
1542
1586
|
if (fired.length === 0)
|
|
1543
1587
|
return out;
|
|
1544
1588
|
const acks = readDocAcks(prBody);
|
|
1545
|
-
|
|
1589
|
+
const neutrals = readDocNeutrals(prBody);
|
|
1590
|
+
for (const { binding: b, matchedFiles } of fired) {
|
|
1546
1591
|
if (waiverActive) {
|
|
1547
1592
|
out.notes.push(`C5 doc-waiver active for ${b.pointer} (binding ${DOC_OWNERS_PATH}:${b.lineNum})`);
|
|
1548
1593
|
continue;
|
|
@@ -1559,9 +1604,22 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1559
1604
|
out.errors.push(`C5 doc-owners-dangling: ${DOC_OWNERS_PATH}:${b.lineNum} points to ${b.pointer}, which does not exist on disk. Fix the binding or add the doc.`);
|
|
1560
1605
|
continue;
|
|
1561
1606
|
}
|
|
1562
|
-
if (
|
|
1563
|
-
|
|
1607
|
+
if (changed.includes(pointerPath))
|
|
1608
|
+
continue;
|
|
1609
|
+
const declared = neutrals.find((n) => n.surface === b.pointer);
|
|
1610
|
+
if (declared) {
|
|
1611
|
+
const evidenced = getDiff !== undefined && matchedFiles.every((f) => {
|
|
1612
|
+
const diff = getDiff(f);
|
|
1613
|
+
return diff !== null && isMechanicallyNeutralDiff(diff, f);
|
|
1614
|
+
});
|
|
1615
|
+
if (evidenced) {
|
|
1616
|
+
out.notes.push(`C5 doc-neutral: ${b.pointer} not required for ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\`) — declared neutral ("${declared.note}"), diff of ${matchedFiles.join(", ")} confirmed comment/whitespace-only.`);
|
|
1617
|
+
continue;
|
|
1618
|
+
}
|
|
1619
|
+
out.errors.push(`C5 doc-neutral-unverified: ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}) declared \`Doc-neutral: ${b.pointer} — ${declared.note}\`, but the diff of ${matchedFiles.join(", ")} contains changes beyond comments/whitespace. Update ${pointerPath}, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1620
|
+
continue;
|
|
1564
1621
|
}
|
|
1622
|
+
out.errors.push(`C5 doc-coverage: code change matched ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}), but ${pointerPath} is not in the PR diff. Update it, declare \`Doc-neutral: ${b.pointer} — <why>\` if the change is genuinely mechanically-neutral, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1565
1623
|
}
|
|
1566
1624
|
return out;
|
|
1567
1625
|
}
|
|
@@ -1513,15 +1513,58 @@ function pointerToPath(p) {
|
|
|
1513
1513
|
return idx === -1 ? p : p.slice(0, idx);
|
|
1514
1514
|
}
|
|
1515
1515
|
var SEPARATOR = /(?:[ \t]*[—–][ \t]*|[ \t]+-[ \t]+)/.source;
|
|
1516
|
-
function
|
|
1517
|
-
const
|
|
1518
|
-
const re = new RegExp(`^[ \\t]
|
|
1516
|
+
function readPointerNoteField(body, field) {
|
|
1517
|
+
const out = [];
|
|
1518
|
+
const re = new RegExp(`^[ \\t]*${field}[ \\t]*:[ \\t]*(.+?)${SEPARATOR}(.+?)[ \\t]*$`, "gim");
|
|
1519
1519
|
for (const m of body.matchAll(re)) {
|
|
1520
|
-
|
|
1520
|
+
out.push({ surface: (m[1] ?? "").trim(), note: (m[2] ?? "").trim() });
|
|
1521
|
+
}
|
|
1522
|
+
return out;
|
|
1523
|
+
}
|
|
1524
|
+
function readDocAcks(body) {
|
|
1525
|
+
return readPointerNoteField(body, "Doc-ack");
|
|
1526
|
+
}
|
|
1527
|
+
function readDocNeutrals(body) {
|
|
1528
|
+
return readPointerNoteField(body, "Doc-neutral");
|
|
1529
|
+
}
|
|
1530
|
+
var LANGUAGE_COMMENT_PREFIXES = [
|
|
1531
|
+
{ test: /\.(ts|tsx|js|jsx|mjs|cjs)$/, prefixes: ["//"] },
|
|
1532
|
+
{ test: /(^|\/)(pre-push|pre-commit|pre-merge-commit)$/, prefixes: ["#"] },
|
|
1533
|
+
{ test: /\.(sh|bash)$/, prefixes: ["#"] },
|
|
1534
|
+
{ test: /\.ya?ml$/, prefixes: ["#"] },
|
|
1535
|
+
{ test: /\.py$/, prefixes: ["#"] }
|
|
1536
|
+
];
|
|
1537
|
+
function commentPrefixesForPath(path) {
|
|
1538
|
+
for (const { test, prefixes } of LANGUAGE_COMMENT_PREFIXES) {
|
|
1539
|
+
if (test.test(path))
|
|
1540
|
+
return prefixes;
|
|
1521
1541
|
}
|
|
1522
|
-
return
|
|
1542
|
+
return [];
|
|
1523
1543
|
}
|
|
1524
|
-
function
|
|
1544
|
+
function isNeutralDiffContentLine(line, prefixes) {
|
|
1545
|
+
const t = line.trim();
|
|
1546
|
+
if (t === "")
|
|
1547
|
+
return true;
|
|
1548
|
+
if (t.startsWith("#!"))
|
|
1549
|
+
return false;
|
|
1550
|
+
return prefixes.some((p) => t.startsWith(p));
|
|
1551
|
+
}
|
|
1552
|
+
function isMechanicallyNeutralDiff(diffText, path) {
|
|
1553
|
+
const prefixes = commentPrefixesForPath(path);
|
|
1554
|
+
let sawChange = false;
|
|
1555
|
+
for (const raw of diffText.split(`
|
|
1556
|
+
`)) {
|
|
1557
|
+
if (raw.startsWith("+++ ") || raw.startsWith("--- ") || raw.startsWith("@@"))
|
|
1558
|
+
continue;
|
|
1559
|
+
if (raw.startsWith("+") || raw.startsWith("-")) {
|
|
1560
|
+
if (!isNeutralDiffContentLine(raw.slice(1), prefixes))
|
|
1561
|
+
return false;
|
|
1562
|
+
sawChange = true;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return sawChange;
|
|
1566
|
+
}
|
|
1567
|
+
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive, getDiff) {
|
|
1525
1568
|
const out = { errors: [], notes: [] };
|
|
1526
1569
|
if (docOwnersContent === null)
|
|
1527
1570
|
return out;
|
|
@@ -1534,13 +1577,15 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1534
1577
|
const fired = [];
|
|
1535
1578
|
for (const b of bindings) {
|
|
1536
1579
|
const re = globToRegex(b.glob);
|
|
1537
|
-
|
|
1538
|
-
|
|
1580
|
+
const matchedFiles = codeFiles.filter((f) => re.test(f));
|
|
1581
|
+
if (matchedFiles.length > 0)
|
|
1582
|
+
fired.push({ binding: b, matchedFiles });
|
|
1539
1583
|
}
|
|
1540
1584
|
if (fired.length === 0)
|
|
1541
1585
|
return out;
|
|
1542
1586
|
const acks = readDocAcks(prBody);
|
|
1543
|
-
|
|
1587
|
+
const neutrals = readDocNeutrals(prBody);
|
|
1588
|
+
for (const { binding: b, matchedFiles } of fired) {
|
|
1544
1589
|
if (waiverActive) {
|
|
1545
1590
|
out.notes.push(`C5 doc-waiver active for ${b.pointer} (binding ${DOC_OWNERS_PATH}:${b.lineNum})`);
|
|
1546
1591
|
continue;
|
|
@@ -1557,9 +1602,22 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1557
1602
|
out.errors.push(`C5 doc-owners-dangling: ${DOC_OWNERS_PATH}:${b.lineNum} points to ${b.pointer}, which does not exist on disk. Fix the binding or add the doc.`);
|
|
1558
1603
|
continue;
|
|
1559
1604
|
}
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1605
|
+
if (changed.includes(pointerPath))
|
|
1606
|
+
continue;
|
|
1607
|
+
const declared = neutrals.find((n) => n.surface === b.pointer);
|
|
1608
|
+
if (declared) {
|
|
1609
|
+
const evidenced = getDiff !== undefined && matchedFiles.every((f) => {
|
|
1610
|
+
const diff = getDiff(f);
|
|
1611
|
+
return diff !== null && isMechanicallyNeutralDiff(diff, f);
|
|
1612
|
+
});
|
|
1613
|
+
if (evidenced) {
|
|
1614
|
+
out.notes.push(`C5 doc-neutral: ${b.pointer} not required for ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\`) — declared neutral ("${declared.note}"), diff of ${matchedFiles.join(", ")} confirmed comment/whitespace-only.`);
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
out.errors.push(`C5 doc-neutral-unverified: ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}) declared \`Doc-neutral: ${b.pointer} — ${declared.note}\`, but the diff of ${matchedFiles.join(", ")} contains changes beyond comments/whitespace. Update ${pointerPath}, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1618
|
+
continue;
|
|
1562
1619
|
}
|
|
1620
|
+
out.errors.push(`C5 doc-coverage: code change matched ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}), but ${pointerPath} is not in the PR diff. Update it, declare \`Doc-neutral: ${b.pointer} — <why>\` if the change is genuinely mechanically-neutral, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1563
1621
|
}
|
|
1564
1622
|
return out;
|
|
1565
1623
|
}
|
|
@@ -1513,15 +1513,58 @@ function pointerToPath(p) {
|
|
|
1513
1513
|
return idx === -1 ? p : p.slice(0, idx);
|
|
1514
1514
|
}
|
|
1515
1515
|
var SEPARATOR = /(?:[ \t]*[—–][ \t]*|[ \t]+-[ \t]+)/.source;
|
|
1516
|
-
function
|
|
1517
|
-
const
|
|
1518
|
-
const re = new RegExp(`^[ \\t]
|
|
1516
|
+
function readPointerNoteField(body, field) {
|
|
1517
|
+
const out = [];
|
|
1518
|
+
const re = new RegExp(`^[ \\t]*${field}[ \\t]*:[ \\t]*(.+?)${SEPARATOR}(.+?)[ \\t]*$`, "gim");
|
|
1519
1519
|
for (const m of body.matchAll(re)) {
|
|
1520
|
-
|
|
1520
|
+
out.push({ surface: (m[1] ?? "").trim(), note: (m[2] ?? "").trim() });
|
|
1521
|
+
}
|
|
1522
|
+
return out;
|
|
1523
|
+
}
|
|
1524
|
+
function readDocAcks(body) {
|
|
1525
|
+
return readPointerNoteField(body, "Doc-ack");
|
|
1526
|
+
}
|
|
1527
|
+
function readDocNeutrals(body) {
|
|
1528
|
+
return readPointerNoteField(body, "Doc-neutral");
|
|
1529
|
+
}
|
|
1530
|
+
var LANGUAGE_COMMENT_PREFIXES = [
|
|
1531
|
+
{ test: /\.(ts|tsx|js|jsx|mjs|cjs)$/, prefixes: ["//"] },
|
|
1532
|
+
{ test: /(^|\/)(pre-push|pre-commit|pre-merge-commit)$/, prefixes: ["#"] },
|
|
1533
|
+
{ test: /\.(sh|bash)$/, prefixes: ["#"] },
|
|
1534
|
+
{ test: /\.ya?ml$/, prefixes: ["#"] },
|
|
1535
|
+
{ test: /\.py$/, prefixes: ["#"] }
|
|
1536
|
+
];
|
|
1537
|
+
function commentPrefixesForPath(path) {
|
|
1538
|
+
for (const { test, prefixes } of LANGUAGE_COMMENT_PREFIXES) {
|
|
1539
|
+
if (test.test(path))
|
|
1540
|
+
return prefixes;
|
|
1521
1541
|
}
|
|
1522
|
-
return
|
|
1542
|
+
return [];
|
|
1523
1543
|
}
|
|
1524
|
-
function
|
|
1544
|
+
function isNeutralDiffContentLine(line, prefixes) {
|
|
1545
|
+
const t = line.trim();
|
|
1546
|
+
if (t === "")
|
|
1547
|
+
return true;
|
|
1548
|
+
if (t.startsWith("#!"))
|
|
1549
|
+
return false;
|
|
1550
|
+
return prefixes.some((p) => t.startsWith(p));
|
|
1551
|
+
}
|
|
1552
|
+
function isMechanicallyNeutralDiff(diffText, path) {
|
|
1553
|
+
const prefixes = commentPrefixesForPath(path);
|
|
1554
|
+
let sawChange = false;
|
|
1555
|
+
for (const raw of diffText.split(`
|
|
1556
|
+
`)) {
|
|
1557
|
+
if (raw.startsWith("+++ ") || raw.startsWith("--- ") || raw.startsWith("@@"))
|
|
1558
|
+
continue;
|
|
1559
|
+
if (raw.startsWith("+") || raw.startsWith("-")) {
|
|
1560
|
+
if (!isNeutralDiffContentLine(raw.slice(1), prefixes))
|
|
1561
|
+
return false;
|
|
1562
|
+
sawChange = true;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
return sawChange;
|
|
1566
|
+
}
|
|
1567
|
+
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive, getDiff) {
|
|
1525
1568
|
const out = { errors: [], notes: [] };
|
|
1526
1569
|
if (docOwnersContent === null)
|
|
1527
1570
|
return out;
|
|
@@ -1534,13 +1577,15 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1534
1577
|
const fired = [];
|
|
1535
1578
|
for (const b of bindings) {
|
|
1536
1579
|
const re = globToRegex(b.glob);
|
|
1537
|
-
|
|
1538
|
-
|
|
1580
|
+
const matchedFiles = codeFiles.filter((f) => re.test(f));
|
|
1581
|
+
if (matchedFiles.length > 0)
|
|
1582
|
+
fired.push({ binding: b, matchedFiles });
|
|
1539
1583
|
}
|
|
1540
1584
|
if (fired.length === 0)
|
|
1541
1585
|
return out;
|
|
1542
1586
|
const acks = readDocAcks(prBody);
|
|
1543
|
-
|
|
1587
|
+
const neutrals = readDocNeutrals(prBody);
|
|
1588
|
+
for (const { binding: b, matchedFiles } of fired) {
|
|
1544
1589
|
if (waiverActive) {
|
|
1545
1590
|
out.notes.push(`C5 doc-waiver active for ${b.pointer} (binding ${DOC_OWNERS_PATH}:${b.lineNum})`);
|
|
1546
1591
|
continue;
|
|
@@ -1557,9 +1602,22 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1557
1602
|
out.errors.push(`C5 doc-owners-dangling: ${DOC_OWNERS_PATH}:${b.lineNum} points to ${b.pointer}, which does not exist on disk. Fix the binding or add the doc.`);
|
|
1558
1603
|
continue;
|
|
1559
1604
|
}
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1605
|
+
if (changed.includes(pointerPath))
|
|
1606
|
+
continue;
|
|
1607
|
+
const declared = neutrals.find((n) => n.surface === b.pointer);
|
|
1608
|
+
if (declared) {
|
|
1609
|
+
const evidenced = getDiff !== undefined && matchedFiles.every((f) => {
|
|
1610
|
+
const diff = getDiff(f);
|
|
1611
|
+
return diff !== null && isMechanicallyNeutralDiff(diff, f);
|
|
1612
|
+
});
|
|
1613
|
+
if (evidenced) {
|
|
1614
|
+
out.notes.push(`C5 doc-neutral: ${b.pointer} not required for ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\`) — declared neutral ("${declared.note}"), diff of ${matchedFiles.join(", ")} confirmed comment/whitespace-only.`);
|
|
1615
|
+
continue;
|
|
1616
|
+
}
|
|
1617
|
+
out.errors.push(`C5 doc-neutral-unverified: ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}) declared \`Doc-neutral: ${b.pointer} — ${declared.note}\`, but the diff of ${matchedFiles.join(", ")} contains changes beyond comments/whitespace. Update ${pointerPath}, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1618
|
+
continue;
|
|
1562
1619
|
}
|
|
1620
|
+
out.errors.push(`C5 doc-coverage: code change matched ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}), but ${pointerPath} is not in the PR diff. Update it, declare \`Doc-neutral: ${b.pointer} — <why>\` if the change is genuinely mechanically-neutral, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1563
1621
|
}
|
|
1564
1622
|
return out;
|
|
1565
1623
|
}
|
|
@@ -1510,15 +1510,58 @@ function pointerToPath(p) {
|
|
|
1510
1510
|
return idx === -1 ? p : p.slice(0, idx);
|
|
1511
1511
|
}
|
|
1512
1512
|
var SEPARATOR = /(?:[ \t]*[—–][ \t]*|[ \t]+-[ \t]+)/.source;
|
|
1513
|
-
function
|
|
1514
|
-
const
|
|
1515
|
-
const re = new RegExp(`^[ \\t]
|
|
1513
|
+
function readPointerNoteField(body, field) {
|
|
1514
|
+
const out = [];
|
|
1515
|
+
const re = new RegExp(`^[ \\t]*${field}[ \\t]*:[ \\t]*(.+?)${SEPARATOR}(.+?)[ \\t]*$`, "gim");
|
|
1516
1516
|
for (const m of body.matchAll(re)) {
|
|
1517
|
-
|
|
1517
|
+
out.push({ surface: (m[1] ?? "").trim(), note: (m[2] ?? "").trim() });
|
|
1518
|
+
}
|
|
1519
|
+
return out;
|
|
1520
|
+
}
|
|
1521
|
+
function readDocAcks(body) {
|
|
1522
|
+
return readPointerNoteField(body, "Doc-ack");
|
|
1523
|
+
}
|
|
1524
|
+
function readDocNeutrals(body) {
|
|
1525
|
+
return readPointerNoteField(body, "Doc-neutral");
|
|
1526
|
+
}
|
|
1527
|
+
var LANGUAGE_COMMENT_PREFIXES = [
|
|
1528
|
+
{ test: /\.(ts|tsx|js|jsx|mjs|cjs)$/, prefixes: ["//"] },
|
|
1529
|
+
{ test: /(^|\/)(pre-push|pre-commit|pre-merge-commit)$/, prefixes: ["#"] },
|
|
1530
|
+
{ test: /\.(sh|bash)$/, prefixes: ["#"] },
|
|
1531
|
+
{ test: /\.ya?ml$/, prefixes: ["#"] },
|
|
1532
|
+
{ test: /\.py$/, prefixes: ["#"] }
|
|
1533
|
+
];
|
|
1534
|
+
function commentPrefixesForPath(path) {
|
|
1535
|
+
for (const { test, prefixes } of LANGUAGE_COMMENT_PREFIXES) {
|
|
1536
|
+
if (test.test(path))
|
|
1537
|
+
return prefixes;
|
|
1518
1538
|
}
|
|
1519
|
-
return
|
|
1539
|
+
return [];
|
|
1520
1540
|
}
|
|
1521
|
-
function
|
|
1541
|
+
function isNeutralDiffContentLine(line, prefixes) {
|
|
1542
|
+
const t = line.trim();
|
|
1543
|
+
if (t === "")
|
|
1544
|
+
return true;
|
|
1545
|
+
if (t.startsWith("#!"))
|
|
1546
|
+
return false;
|
|
1547
|
+
return prefixes.some((p) => t.startsWith(p));
|
|
1548
|
+
}
|
|
1549
|
+
function isMechanicallyNeutralDiff(diffText, path) {
|
|
1550
|
+
const prefixes = commentPrefixesForPath(path);
|
|
1551
|
+
let sawChange = false;
|
|
1552
|
+
for (const raw of diffText.split(`
|
|
1553
|
+
`)) {
|
|
1554
|
+
if (raw.startsWith("+++ ") || raw.startsWith("--- ") || raw.startsWith("@@"))
|
|
1555
|
+
continue;
|
|
1556
|
+
if (raw.startsWith("+") || raw.startsWith("-")) {
|
|
1557
|
+
if (!isNeutralDiffContentLine(raw.slice(1), prefixes))
|
|
1558
|
+
return false;
|
|
1559
|
+
sawChange = true;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
return sawChange;
|
|
1563
|
+
}
|
|
1564
|
+
function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive, getDiff) {
|
|
1522
1565
|
const out = { errors: [], notes: [] };
|
|
1523
1566
|
if (docOwnersContent === null)
|
|
1524
1567
|
return out;
|
|
@@ -1531,13 +1574,15 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1531
1574
|
const fired = [];
|
|
1532
1575
|
for (const b of bindings) {
|
|
1533
1576
|
const re = globToRegex(b.glob);
|
|
1534
|
-
|
|
1535
|
-
|
|
1577
|
+
const matchedFiles = codeFiles.filter((f) => re.test(f));
|
|
1578
|
+
if (matchedFiles.length > 0)
|
|
1579
|
+
fired.push({ binding: b, matchedFiles });
|
|
1536
1580
|
}
|
|
1537
1581
|
if (fired.length === 0)
|
|
1538
1582
|
return out;
|
|
1539
1583
|
const acks = readDocAcks(prBody);
|
|
1540
|
-
|
|
1584
|
+
const neutrals = readDocNeutrals(prBody);
|
|
1585
|
+
for (const { binding: b, matchedFiles } of fired) {
|
|
1541
1586
|
if (waiverActive) {
|
|
1542
1587
|
out.notes.push(`C5 doc-waiver active for ${b.pointer} (binding ${DOC_OWNERS_PATH}:${b.lineNum})`);
|
|
1543
1588
|
continue;
|
|
@@ -1554,9 +1599,22 @@ function evaluateC5(changed, docOwnersContent, prBody, fileExists, waiverActive)
|
|
|
1554
1599
|
out.errors.push(`C5 doc-owners-dangling: ${DOC_OWNERS_PATH}:${b.lineNum} points to ${b.pointer}, which does not exist on disk. Fix the binding or add the doc.`);
|
|
1555
1600
|
continue;
|
|
1556
1601
|
}
|
|
1557
|
-
if (
|
|
1558
|
-
|
|
1602
|
+
if (changed.includes(pointerPath))
|
|
1603
|
+
continue;
|
|
1604
|
+
const declared = neutrals.find((n) => n.surface === b.pointer);
|
|
1605
|
+
if (declared) {
|
|
1606
|
+
const evidenced = getDiff !== undefined && matchedFiles.every((f) => {
|
|
1607
|
+
const diff = getDiff(f);
|
|
1608
|
+
return diff !== null && isMechanicallyNeutralDiff(diff, f);
|
|
1609
|
+
});
|
|
1610
|
+
if (evidenced) {
|
|
1611
|
+
out.notes.push(`C5 doc-neutral: ${b.pointer} not required for ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\`) — declared neutral ("${declared.note}"), diff of ${matchedFiles.join(", ")} confirmed comment/whitespace-only.`);
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
out.errors.push(`C5 doc-neutral-unverified: ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}) declared \`Doc-neutral: ${b.pointer} — ${declared.note}\`, but the diff of ${matchedFiles.join(", ")} contains changes beyond comments/whitespace. Update ${pointerPath}, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1615
|
+
continue;
|
|
1559
1616
|
}
|
|
1617
|
+
out.errors.push(`C5 doc-coverage: code change matched ${DOC_OWNERS_PATH}:${b.lineNum} (glob \`${b.glob}\` → ${b.pointer}), but ${pointerPath} is not in the PR diff. Update it, declare \`Doc-neutral: ${b.pointer} — <why>\` if the change is genuinely mechanically-neutral, or have a principal apply the \`${WAIVER_LABEL}\` label.`);
|
|
1560
1618
|
}
|
|
1561
1619
|
return out;
|
|
1562
1620
|
}
|