@attalabs/vinaya 0.9.0 → 0.16.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/README.md +33 -7
- package/aeg-root/contracts/planner-brief.md +1 -1
- package/aeg-root/enforcement.md +7 -7
- package/aeg-root/glossary.md +4 -0
- package/aeg-root/roles/archivist.md +2 -2
- package/aeg-root/roles/developer.md +29 -5
- package/aeg-root/roles/planner.md +3 -3
- package/aeg-root/roles/reviewer.md +17 -4
- package/aeg-root/roles/security.md +16 -5
- package/aeg-root/roles/tranche-archivist.md +1 -1
- package/aeg-root/skills/aeg/SKILL.md +1 -1
- package/aeg-root/state-machine.md +8 -8
- package/aeg-root/templates/pr-report-template.md +8 -4
- package/aeg-root/tranche-model.md +34 -12
- package/dist/checks/bin/check-branch-topology.js +232 -120
- package/dist/checks/bin/check-brief-shape.js +232 -120
- package/dist/checks/bin/check-closes-n.js +232 -120
- package/dist/checks/bin/check-coherence.js +234 -121
- package/dist/checks/bin/check-dead-branch-push.js +232 -120
- package/dist/checks/bin/check-dispatch-readiness.js +234 -121
- package/dist/checks/bin/check-doc-coverage-push.js +261 -130
- package/dist/checks/bin/check-doc-coverage.js +263 -132
- package/dist/checks/bin/check-evidence-fresh.js +3196 -0
- package/dist/checks/bin/check-first-push-dispatch.js +234 -121
- package/dist/checks/bin/check-issue-assignment.js +232 -120
- package/dist/checks/bin/check-no-disk-state.js +232 -120
- package/dist/checks/bin/check-reader-resolvable-prose.js +232 -120
- package/dist/checks/bin/check-registry-gates.js +232 -120
- package/dist/checks/bin/check-review-gate.js +237 -123
- package/dist/checks/bin/check-single-plan-pr.js +232 -120
- package/dist/checks/bin/check-test-plan.js +232 -120
- package/dist/index.js +1232 -262
- package/package.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/required-server-files.json +3 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js +2 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__0e95dv5._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/1q96_modules_@clerk_nextjs_dist_esm_app-router_client_keyless-creator-reader_0lom2js.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__029-1ch._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__05t6482._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0gw1_g2._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0mdkphe._.js +3 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0plfbyn._.js +45 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0q56ohl._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0rr_lq7._.js +51 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0zfjc2o._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__19jyp3m._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1_ojzp1._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1d_8nm4._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1p19382._.js → [root-of-the-server]__1qpl4m_._.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1zkwtx8._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__210ehg9._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_00yjcv1._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0o54suy._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0rix02i._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_18-d2be._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +5 -4
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1gw6vc9._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/apps_vinaya-studio_web_src_app_studio_projects_[name]_tranches_[slug]_20yap_f._.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
- package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3antrj5z0uf_c.js → 0ek6qlqhokys0.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{1khy4oilrab6g.js → 0fm4p9os2lsom.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3zyedaek9nvr_.js → 0ywkq74falx-x.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/1s9ixp5nhnevp.js +1 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/27sqffqm2wbxx.css +1 -0
- package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
- package/studio-standalone/apps/vinaya-studio/web/server.js +1 -1
- package/templates/custom-check.template.ts +5 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1s7kj3s._.js +0 -3
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1tswrm4._.js +0 -9
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_01jl99k._.js +0 -45
- package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/apps_vinaya-studio_web_src_20c6-3m._.js +0 -45
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/0qu0xgvifbacu.css +0 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/2e68sbtdhi5dg.js +0 -1
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{pY8RKWuVAUZOjj2-bAYCY → R7xvB2iuNFp_jxENpL7xv}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{pY8RKWuVAUZOjj2-bAYCY → R7xvB2iuNFp_jxENpL7xv}/_clientMiddlewareManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{pY8RKWuVAUZOjj2-bAYCY → R7xvB2iuNFp_jxENpL7xv}/_ssgManifest.js +0 -0
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// @bun
|
|
3
3
|
|
|
4
4
|
// src/checks/bin/check-doc-coverage.ts
|
|
5
|
-
import { execFileSync as
|
|
5
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
6
6
|
import { existsSync, readFileSync } from "fs";
|
|
7
|
-
// ../../packages/aeg-
|
|
7
|
+
// ../../packages/aeg-forge-state/src/strip-code.ts
|
|
8
8
|
function maskCode(body) {
|
|
9
9
|
const fill = (line) => " ".repeat(line.length);
|
|
10
10
|
return maskIndentedCode(maskFencedCode(body, fill), fill).replace(/(`+)[^\n]*?\1/g, (m) => " ".repeat(m.length));
|
|
@@ -21,29 +21,38 @@ function stripCode(body, options = {}) {
|
|
|
21
21
|
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})([^\r\n]*)\r?$/;
|
|
22
22
|
var FENCE_CLOSE = /^ {0,3}(`+|~+)[ \t]*\r?$/;
|
|
23
23
|
function maskFencedCode(body, fill) {
|
|
24
|
+
const { lines } = scanFencedCode(body);
|
|
25
|
+
return lines.map(({ line, inCode }) => inCode ? fill(line) : line).join(`
|
|
26
|
+
`);
|
|
27
|
+
}
|
|
28
|
+
function scanFencedCode(body) {
|
|
24
29
|
let fenceChar = null;
|
|
25
30
|
let fenceLen = 0;
|
|
26
|
-
|
|
31
|
+
const lines = body.split(`
|
|
27
32
|
`).map((line) => {
|
|
28
33
|
if (fenceChar === null) {
|
|
29
34
|
const open = line.match(FENCE_OPEN);
|
|
30
35
|
if (!open)
|
|
31
|
-
return line;
|
|
36
|
+
return { line, inCode: false };
|
|
32
37
|
const marker = open[1];
|
|
33
38
|
if (marker[0] === "`" && open[2].includes("`"))
|
|
34
|
-
return line;
|
|
39
|
+
return { line, inCode: false };
|
|
35
40
|
fenceChar = marker[0];
|
|
36
41
|
fenceLen = marker.length;
|
|
37
|
-
return
|
|
42
|
+
return { line, inCode: true };
|
|
38
43
|
}
|
|
39
44
|
const close = line.match(FENCE_CLOSE);
|
|
40
45
|
if (close && close[1][0] === fenceChar && close[1].length >= fenceLen) {
|
|
41
46
|
fenceChar = null;
|
|
42
47
|
fenceLen = 0;
|
|
43
48
|
}
|
|
44
|
-
return
|
|
45
|
-
})
|
|
46
|
-
|
|
49
|
+
return { line, inCode: true };
|
|
50
|
+
});
|
|
51
|
+
return { lines, unterminated: fenceChar !== null };
|
|
52
|
+
}
|
|
53
|
+
function hasUnterminatedFence(body) {
|
|
54
|
+
return scanFencedCode(body.replace(/\r\n?/g, `
|
|
55
|
+
`)).unterminated;
|
|
47
56
|
}
|
|
48
57
|
var LIST_MARKER = /^ {0,3}(?:[-*+]|\d{1,9}[.)])(?:\s|$)/;
|
|
49
58
|
function indentWidth(line) {
|
|
@@ -86,88 +95,7 @@ function maskIndentedCode(body, fill) {
|
|
|
86
95
|
return out.join(`
|
|
87
96
|
`);
|
|
88
97
|
}
|
|
89
|
-
|
|
90
|
-
const masked = maskCode(body);
|
|
91
|
-
const start = new RegExp(`<!--\\s*AEG:${field}:START\\s*-->`).exec(masked);
|
|
92
|
-
if (!start)
|
|
93
|
-
return null;
|
|
94
|
-
const afterStart = start.index + start[0].length;
|
|
95
|
-
const end = new RegExp(`<!--\\s*AEG:${field}:END\\s*-->`).exec(masked.slice(afterStart));
|
|
96
|
-
if (!end)
|
|
97
|
-
return null;
|
|
98
|
-
return body.slice(afterStart, afterStart + end.index);
|
|
99
|
-
}
|
|
100
|
-
// ../../packages/aeg-core/src/parse-registry.ts
|
|
101
|
-
function parseRegistry(md) {
|
|
102
|
-
const rows = readMarkdownTable(md, /^##\s+Registry\b/i);
|
|
103
|
-
const projects = [];
|
|
104
|
-
for (const row of rows) {
|
|
105
|
-
if (row.length < 4)
|
|
106
|
-
continue;
|
|
107
|
-
const name = stripBackticks(row[0] ?? "").trim();
|
|
108
|
-
const path = stripBackticks(row[1] ?? "").trim();
|
|
109
|
-
const specsPath = stripBackticks(row[2] ?? "").trim();
|
|
110
|
-
const stateCell = (row[3] ?? "").trim();
|
|
111
|
-
if (!name || !path || !specsPath)
|
|
112
|
-
continue;
|
|
113
|
-
projects.push({
|
|
114
|
-
name,
|
|
115
|
-
path,
|
|
116
|
-
specsPath,
|
|
117
|
-
statePath: parseStateCell(stateCell)
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
return projects;
|
|
121
|
-
}
|
|
122
|
-
function parseStateCell(cell) {
|
|
123
|
-
if (!cell)
|
|
124
|
-
return null;
|
|
125
|
-
const stripped = stripBackticks(cell).trim();
|
|
126
|
-
if (/^[\w./@-]+\/?$/.test(stripped) && cell.includes("`")) {
|
|
127
|
-
return stripped;
|
|
128
|
-
}
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
function stripBackticks(s) {
|
|
132
|
-
return s.replace(/`/g, "");
|
|
133
|
-
}
|
|
134
|
-
function readMarkdownTable(md, headingMatcher) {
|
|
135
|
-
const lines = md.split(/\r?\n/);
|
|
136
|
-
let i = 0;
|
|
137
|
-
for (;i < lines.length; i++) {
|
|
138
|
-
if (headingMatcher.test(lines[i] ?? "")) {
|
|
139
|
-
i++;
|
|
140
|
-
break;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
if (i >= lines.length)
|
|
144
|
-
return [];
|
|
145
|
-
for (;i < lines.length; i++) {
|
|
146
|
-
if ((lines[i] ?? "").trim().startsWith("|"))
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
if (i + 1 >= lines.length)
|
|
150
|
-
return [];
|
|
151
|
-
i += 2;
|
|
152
|
-
const out = [];
|
|
153
|
-
for (;i < lines.length; i++) {
|
|
154
|
-
const trimmed = (lines[i] ?? "").trim();
|
|
155
|
-
if (!trimmed.startsWith("|"))
|
|
156
|
-
break;
|
|
157
|
-
out.push(splitTableRow(trimmed));
|
|
158
|
-
}
|
|
159
|
-
return out;
|
|
160
|
-
}
|
|
161
|
-
function splitTableRow(row) {
|
|
162
|
-
const PLACEHOLDER = "\x00PIPE\x00";
|
|
163
|
-
const escaped = row.replace(/\\\|/g, PLACEHOLDER);
|
|
164
|
-
const parts = escaped.split("|").map((s) => s.trim());
|
|
165
|
-
if (parts.length && parts[0] === "")
|
|
166
|
-
parts.shift();
|
|
167
|
-
if (parts.length && parts[parts.length - 1] === "")
|
|
168
|
-
parts.pop();
|
|
169
|
-
return parts.map((p) => p.split(PLACEHOLDER).join("|"));
|
|
170
|
-
}
|
|
98
|
+
|
|
171
99
|
// ../../packages/aeg-forge-state/src/parse-rationale-deps.ts
|
|
172
100
|
var SECTION_HEADER = /\*\*Dependency rationale\*\*/i;
|
|
173
101
|
var NEXT_HEADER = /\*\*[A-Z][^*]*\*\*/;
|
|
@@ -465,11 +393,37 @@ function findTrancheSlug(names) {
|
|
|
465
393
|
var TITLE_PATTERN = /^\[([^\]]+)]\s*(\S+)\s*—\s*(.+)$/;
|
|
466
394
|
var PROJECT_SLUG = /^[a-z0-9][a-z0-9-]*$/i;
|
|
467
395
|
var PROJECT_FIELD = /^\s*(?:\*\*)?Project(?:\(s\))?(?:\*\*)?\s*:\s*(?:\*\*)?\s*(.+)$/im;
|
|
396
|
+
var VALUE_EDGE_MARKUP = /^[`.;\s]+|[`.;\s]+$/g;
|
|
397
|
+
function unwrapValue(raw) {
|
|
398
|
+
return raw.replace(/\*\*/g, "").replace(VALUE_EDGE_MARKUP, "");
|
|
399
|
+
}
|
|
400
|
+
function projectFieldFromBody(body) {
|
|
401
|
+
const m = stripCode(body, { inlineSpans: "keep" }).match(PROJECT_FIELD);
|
|
402
|
+
if (m)
|
|
403
|
+
return parseFieldValue(m[1] ?? "");
|
|
404
|
+
if (hasUnterminatedFence(body)) {
|
|
405
|
+
const raw = body.match(PROJECT_FIELD);
|
|
406
|
+
if (raw)
|
|
407
|
+
return { declared: true, names: [], unparsed: [(raw[1] ?? "").trim()], unreadable: true };
|
|
408
|
+
}
|
|
409
|
+
return { declared: false, names: [], unparsed: [], unreadable: false };
|
|
410
|
+
}
|
|
411
|
+
function parseFieldValue(value) {
|
|
412
|
+
const names = [];
|
|
413
|
+
const unparsed = [];
|
|
414
|
+
for (const raw of value.split(",")) {
|
|
415
|
+
const name = unwrapValue(raw);
|
|
416
|
+
if (name.length === 0)
|
|
417
|
+
continue;
|
|
418
|
+
if (PROJECT_SLUG.test(name))
|
|
419
|
+
names.push(name);
|
|
420
|
+
else
|
|
421
|
+
unparsed.push(raw.trim());
|
|
422
|
+
}
|
|
423
|
+
return { declared: true, names: [...new Set(names)], unparsed, unreadable: false };
|
|
424
|
+
}
|
|
468
425
|
function projectsFromBody(body) {
|
|
469
|
-
|
|
470
|
-
if (!m)
|
|
471
|
-
return [];
|
|
472
|
-
return (m[1] ?? "").split(",").map((s) => s.trim()).filter((s) => PROJECT_SLUG.test(s));
|
|
426
|
+
return projectFieldFromBody(body).names;
|
|
473
427
|
}
|
|
474
428
|
function taskFromIssue(issue) {
|
|
475
429
|
const m = issue.title.match(TITLE_PATTERN);
|
|
@@ -511,8 +465,8 @@ function tasksFromIssues(issues) {
|
|
|
511
465
|
}
|
|
512
466
|
return tasks.sort((a, b) => compareTaskIds(a.id, b.id));
|
|
513
467
|
}
|
|
514
|
-
async function
|
|
515
|
-
return
|
|
468
|
+
async function fetchTrancheIssuesAsync(owner, repo, slug) {
|
|
469
|
+
return ghIssueListByAnyLabelAsync(owner, repo, [trancheLabel(slug)]);
|
|
516
470
|
}
|
|
517
471
|
function resolveTaskIssueRef(title, labels) {
|
|
518
472
|
const m = title.match(TITLE_PATTERN);
|
|
@@ -528,17 +482,20 @@ function resolveTaskIssueRef(title, labels) {
|
|
|
528
482
|
}
|
|
529
483
|
|
|
530
484
|
// ../../packages/aeg-forge-state/src/derive-from-forge.ts
|
|
531
|
-
|
|
532
|
-
const milestone = known ?? findMilestoneForSlug(owner, repo, slug);
|
|
533
|
-
const tasks = await listTasksForSlugAsync(owner, repo, slug);
|
|
485
|
+
function trancheFromIssues(slug, issues, known) {
|
|
534
486
|
return {
|
|
535
487
|
name: slug,
|
|
536
|
-
lifecycle:
|
|
537
|
-
goal:
|
|
538
|
-
tasks,
|
|
488
|
+
lifecycle: known?.lifecycle ?? "active",
|
|
489
|
+
goal: known?.goal ?? "",
|
|
490
|
+
tasks: tasksFromIssues(issues),
|
|
539
491
|
backlog: []
|
|
540
492
|
};
|
|
541
493
|
}
|
|
494
|
+
async function deriveTrancheFromForge(owner, repo, slug, known) {
|
|
495
|
+
const milestone = known ?? findMilestoneForSlug(owner, repo, slug);
|
|
496
|
+
const issues = await fetchTrancheIssuesAsync(owner, repo, slug);
|
|
497
|
+
return trancheFromIssues(slug, issues, milestone);
|
|
498
|
+
}
|
|
542
499
|
// ../../node_modules/universal-user-agent/index.js
|
|
543
500
|
function getUserAgent() {
|
|
544
501
|
if (typeof navigator === "object" && "userAgent" in navigator) {
|
|
@@ -1458,6 +1415,96 @@ var systemEnv3 = {
|
|
|
1458
1415
|
...process.env,
|
|
1459
1416
|
PATH: [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].filter(Boolean).join(":")
|
|
1460
1417
|
};
|
|
1418
|
+
// ../../packages/aeg-core/src/anchored-region.ts
|
|
1419
|
+
function anchoredRegionBounds(body, field) {
|
|
1420
|
+
const masked = maskCode(body);
|
|
1421
|
+
const start = new RegExp(`<!--\\s*AEG:${field}:START\\s*-->`).exec(masked);
|
|
1422
|
+
if (!start)
|
|
1423
|
+
return null;
|
|
1424
|
+
const innerStart = start.index + start[0].length;
|
|
1425
|
+
const end = new RegExp(`<!--\\s*AEG:${field}:END\\s*-->`).exec(masked.slice(innerStart));
|
|
1426
|
+
if (!end)
|
|
1427
|
+
return null;
|
|
1428
|
+
const innerEnd = innerStart + end.index;
|
|
1429
|
+
return { outerStart: start.index, outerEnd: innerEnd + end[0].length, innerStart, innerEnd };
|
|
1430
|
+
}
|
|
1431
|
+
function anchoredRegion(body, field) {
|
|
1432
|
+
const bounds = anchoredRegionBounds(body, field);
|
|
1433
|
+
if (!bounds)
|
|
1434
|
+
return null;
|
|
1435
|
+
return body.slice(bounds.innerStart, bounds.innerEnd);
|
|
1436
|
+
}
|
|
1437
|
+
// ../../packages/aeg-core/src/parse-registry.ts
|
|
1438
|
+
function parseRegistry(md) {
|
|
1439
|
+
const rows = readMarkdownTable(md, /^##\s+Registry\b/i);
|
|
1440
|
+
const projects = [];
|
|
1441
|
+
for (const row of rows) {
|
|
1442
|
+
if (row.length < 4)
|
|
1443
|
+
continue;
|
|
1444
|
+
const name = stripBackticks(row[0] ?? "").trim();
|
|
1445
|
+
const path = stripBackticks(row[1] ?? "").trim();
|
|
1446
|
+
const specsPath = stripBackticks(row[2] ?? "").trim();
|
|
1447
|
+
const stateCell = (row[3] ?? "").trim();
|
|
1448
|
+
if (!name || !path || !specsPath)
|
|
1449
|
+
continue;
|
|
1450
|
+
projects.push({
|
|
1451
|
+
name,
|
|
1452
|
+
path,
|
|
1453
|
+
specsPath,
|
|
1454
|
+
statePath: parseStateCell(stateCell)
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1457
|
+
return projects;
|
|
1458
|
+
}
|
|
1459
|
+
function parseStateCell(cell) {
|
|
1460
|
+
if (!cell)
|
|
1461
|
+
return null;
|
|
1462
|
+
const stripped = stripBackticks(cell).trim();
|
|
1463
|
+
if (/^[\w./@-]+\/?$/.test(stripped) && cell.includes("`")) {
|
|
1464
|
+
return stripped;
|
|
1465
|
+
}
|
|
1466
|
+
return null;
|
|
1467
|
+
}
|
|
1468
|
+
function stripBackticks(s) {
|
|
1469
|
+
return s.replace(/`/g, "");
|
|
1470
|
+
}
|
|
1471
|
+
function readMarkdownTable(md, headingMatcher) {
|
|
1472
|
+
const lines = md.split(/\r?\n/);
|
|
1473
|
+
let i = 0;
|
|
1474
|
+
for (;i < lines.length; i++) {
|
|
1475
|
+
if (headingMatcher.test(lines[i] ?? "")) {
|
|
1476
|
+
i++;
|
|
1477
|
+
break;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
if (i >= lines.length)
|
|
1481
|
+
return [];
|
|
1482
|
+
for (;i < lines.length; i++) {
|
|
1483
|
+
if ((lines[i] ?? "").trim().startsWith("|"))
|
|
1484
|
+
break;
|
|
1485
|
+
}
|
|
1486
|
+
if (i + 1 >= lines.length)
|
|
1487
|
+
return [];
|
|
1488
|
+
i += 2;
|
|
1489
|
+
const out = [];
|
|
1490
|
+
for (;i < lines.length; i++) {
|
|
1491
|
+
const trimmed = (lines[i] ?? "").trim();
|
|
1492
|
+
if (!trimmed.startsWith("|"))
|
|
1493
|
+
break;
|
|
1494
|
+
out.push(splitTableRow(trimmed));
|
|
1495
|
+
}
|
|
1496
|
+
return out;
|
|
1497
|
+
}
|
|
1498
|
+
function splitTableRow(row) {
|
|
1499
|
+
const PLACEHOLDER = "\x00PIPE\x00";
|
|
1500
|
+
const escaped = row.replace(/\\\|/g, PLACEHOLDER);
|
|
1501
|
+
const parts = escaped.split("|").map((s) => s.trim());
|
|
1502
|
+
if (parts.length && parts[0] === "")
|
|
1503
|
+
parts.shift();
|
|
1504
|
+
if (parts.length && parts[parts.length - 1] === "")
|
|
1505
|
+
parts.pop();
|
|
1506
|
+
return parts.map((p) => p.split(PLACEHOLDER).join("|"));
|
|
1507
|
+
}
|
|
1461
1508
|
// ../../packages/aeg-core/src/state-machine-model.ts
|
|
1462
1509
|
var FORGE_FACT_INPUTS = [
|
|
1463
1510
|
{
|
|
@@ -1961,23 +2008,65 @@ function checkIssueRationale(body) {
|
|
|
1961
2008
|
function isTaskIssueLabelSet(labels) {
|
|
1962
2009
|
return hasLabel("tranche", labels);
|
|
1963
2010
|
}
|
|
2011
|
+
function isControlCodePoint(codePoint) {
|
|
2012
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
2013
|
+
}
|
|
2014
|
+
function isDisplayControlCodePoint(codePoint) {
|
|
2015
|
+
return codePoint >= 8203 && codePoint <= 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8294 && codePoint <= 8297 || codePoint === 65279;
|
|
2016
|
+
}
|
|
2017
|
+
var MESSAGE_VALUE_COUNT_MAX = 5;
|
|
2018
|
+
function residueForMessage(values) {
|
|
2019
|
+
const shown = values.slice(0, MESSAGE_VALUE_COUNT_MAX).map((v) => `\`${forMessage(v)}\``);
|
|
2020
|
+
const rest = values.length - shown.length;
|
|
2021
|
+
return rest > 0 ? `${shown.join(", ")} and ${rest} more` : shown.join(", ");
|
|
2022
|
+
}
|
|
2023
|
+
var MESSAGE_VALUE_MAX = 64;
|
|
2024
|
+
function forMessage(value) {
|
|
2025
|
+
const kept = [];
|
|
2026
|
+
for (const ch of value.replace(/\s+/g, " ")) {
|
|
2027
|
+
const codePoint = ch.codePointAt(0) ?? 0;
|
|
2028
|
+
if (isControlCodePoint(codePoint) || isDisplayControlCodePoint(codePoint))
|
|
2029
|
+
continue;
|
|
2030
|
+
kept.push(ch);
|
|
2031
|
+
}
|
|
2032
|
+
const cleaned = kept.join("").trim();
|
|
2033
|
+
if (cleaned.length === 0)
|
|
2034
|
+
return "(unprintable)";
|
|
2035
|
+
const points = Array.from(cleaned);
|
|
2036
|
+
return points.length > MESSAGE_VALUE_MAX ? `${points.slice(0, MESSAGE_VALUE_MAX).join("")}…` : cleaned;
|
|
2037
|
+
}
|
|
1964
2038
|
function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
1965
2039
|
if (registeredNames.length === 0)
|
|
1966
2040
|
return { status: "pass", errors: [] };
|
|
1967
|
-
const
|
|
1968
|
-
|
|
1969
|
-
if (unregistered.length === 0)
|
|
2041
|
+
const field = projectFieldFromBody(body);
|
|
2042
|
+
if (!field.declared)
|
|
1970
2043
|
return { status: "pass", errors: [] };
|
|
1971
|
-
const
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2044
|
+
const known = new Set(registeredNames.map((n) => n.trim()));
|
|
2045
|
+
const registeredList = [...registeredNames].map(forMessage).join(", ");
|
|
2046
|
+
const errors = [];
|
|
2047
|
+
if (field.unreadable) {
|
|
2048
|
+
return {
|
|
2049
|
+
status: "fail",
|
|
2050
|
+
errors: [
|
|
2051
|
+
`issue-validation project registry: the body has an unterminated code fence, so its \`**Project:**\` field could not be read reliably — the fence runs to end of body (CommonMark, and how the forge renders it), swallowing everything after it including the foot field where the declaration lives by convention. Read from the raw body the field says ${residueForMessage(field.unparsed) || "(nothing)"}, but a line inside a swallowed region may be a quoted example rather than a real declaration, so this gate refuses rather than guessing which. Balance the fences — every opening run of backticks or tildes needs a closing run at least as long — and the field reads normally.`
|
|
2052
|
+
]
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
const unregistered = field.names.filter((p) => !known.has(p));
|
|
2056
|
+
if (unregistered.length > 0) {
|
|
2057
|
+
const caseHints = unregistered.map((p) => {
|
|
2058
|
+
const row = [...known].find((k) => k.toLowerCase() === p.toLowerCase());
|
|
2059
|
+
return row ? `\`${forMessage(p)}\` differs from the registered \`${forMessage(row)}\` only in case` : null;
|
|
2060
|
+
}).filter((h) => h !== null);
|
|
2061
|
+
errors.push(`issue-validation project registry: the \`**Project:**\` field declares ${unregistered.map(forMessage).join(", ")} — no such row in \`.vinaya/projects.md\`, which is the authority for valid project names (registered: ${registeredList}). Fix the name, or register the project with \`vinaya init product <name> --path <folder>\` first; an unregistered project has no specs to read and no per-project state to update.${caseHints.length > 0 ? ` Note: ${caseHints.join("; ")} — project names are matched exactly, because every downstream consumer compares them literally.` : ""} This reads the same field \`projectsFromBody\` derives the task's project from, so a name here that is not a row is a task that resolves to a project that does not exist.`);
|
|
2062
|
+
}
|
|
2063
|
+
if (field.names.length === 0) {
|
|
2064
|
+
const residue = field.unparsed.length > 0 ? ` — ${residueForMessage(field.unparsed)}` : " — the field is empty";
|
|
2065
|
+
errors.push(`issue-validation project registry: the \`**Project:**\` field is present but resolves to no project name${residue} (registered: ${registeredList}). A project name is a slug (\`[a-z0-9][a-z0-9-]*\`, matched exactly); prose, a parenthetical, or a sentence in this field resolves to no project at all, and this gate cannot check a name it never receives — which is how a declaration like this used to pass. Write the registered name on its own, or register the project with \`vinaya init product <name> --path <folder>\` first. If the task genuinely touches no registered project, omit the field rather than explaining its absence inside it — \`checkIssueRationale\` already requires the \`Project(s) + blast radius\` narrative field for that.`);
|
|
2066
|
+
} else if (field.unparsed.length > 0) {
|
|
2067
|
+
errors.push(`issue-validation project registry: the \`**Project:**\` field declares a value that is not a project name — ${residueForMessage(field.unparsed)} (registered: ${registeredList}). The rest of the field parsed, so this is a name the gate silently could not check rather than a field it could not read at all. A project name is a slug (\`[a-z0-9][a-z0-9-]*\`, matched exactly); write it on its own, or drop it if it names no project.`);
|
|
2068
|
+
}
|
|
2069
|
+
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
1981
2070
|
}
|
|
1982
2071
|
|
|
1983
2072
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
@@ -2281,15 +2370,25 @@ function checkClosesN2(branch, prBody, trancheFiles, taskIssueRefs) {
|
|
|
2281
2370
|
return { ok: true, expectedIssue };
|
|
2282
2371
|
}
|
|
2283
2372
|
// ../../packages/aeg-core/src/verdict-extraction.ts
|
|
2373
|
+
var HEAD_SHA_PATTERN = /^[ \t]*(?:\*{1,3}|_{1,3})?Judged head:\s*([0-9a-f]{7,40})(?![A-Za-z0-9])/im;
|
|
2374
|
+
function extractHeadSha(comment) {
|
|
2375
|
+
const m = comment.match(HEAD_SHA_PATTERN);
|
|
2376
|
+
return m ? m[1].toLowerCase() : null;
|
|
2377
|
+
}
|
|
2284
2378
|
function extractVerdict(comments, valuePattern, missingLabel) {
|
|
2285
2379
|
const clearHits = comments.filter((c) => valuePattern.test(c));
|
|
2286
2380
|
if (clearHits.length > 0) {
|
|
2287
2381
|
const latest = clearHits[clearHits.length - 1];
|
|
2288
2382
|
const m = latest.match(valuePattern);
|
|
2289
|
-
return {
|
|
2383
|
+
return {
|
|
2384
|
+
value: m[1].toUpperCase().replace(/[_-]/g, " "),
|
|
2385
|
+
headSha: extractHeadSha(latest),
|
|
2386
|
+
danglingNote: null
|
|
2387
|
+
};
|
|
2290
2388
|
}
|
|
2291
2389
|
return {
|
|
2292
2390
|
value: `no ${missingLabel} pass was run before merge — DANGLING, see below`,
|
|
2391
|
+
headSha: null,
|
|
2293
2392
|
danglingNote: `no ${missingLabel} verdict comment found on this PR`
|
|
2294
2393
|
};
|
|
2295
2394
|
}
|
|
@@ -2303,6 +2402,11 @@ function extractSecurityReviewVerdict(comments) {
|
|
|
2303
2402
|
function isReviewGateExemptBranch(branch) {
|
|
2304
2403
|
return branch.startsWith("plan/");
|
|
2305
2404
|
}
|
|
2405
|
+
function isBoundToHead(extraction, headSha) {
|
|
2406
|
+
if (!extraction.headSha)
|
|
2407
|
+
return false;
|
|
2408
|
+
return headSha.toLowerCase().startsWith(extraction.headSha.toLowerCase());
|
|
2409
|
+
}
|
|
2306
2410
|
function checkReviewGate(input) {
|
|
2307
2411
|
const principalAllowlist = input.principalAllowlist ?? PRINCIPAL_ALLOWLIST;
|
|
2308
2412
|
const waived = isWaiverLabelActorVerified({
|
|
@@ -2325,18 +2429,26 @@ function checkReviewGate(input) {
|
|
|
2325
2429
|
const security = extractSecurityReviewVerdict(verifiedBodies);
|
|
2326
2430
|
const codeReviewClean = codeReview.value === "APPROVE";
|
|
2327
2431
|
const securityClean = security.value === "PASS";
|
|
2328
|
-
|
|
2432
|
+
const codeReviewBound = isBoundToHead(codeReview, input.headSha);
|
|
2433
|
+
const securityBound = isBoundToHead(security, input.headSha);
|
|
2434
|
+
if (codeReviewClean && codeReviewBound && securityClean && securityBound) {
|
|
2329
2435
|
return {
|
|
2330
2436
|
verdict: "pass",
|
|
2331
|
-
reason:
|
|
2437
|
+
reason: `code-reviewer verdict is a clean APPROVE and security-review verdict is a clean PASS, both covering head ${input.headSha}.`,
|
|
2332
2438
|
waived: false
|
|
2333
2439
|
};
|
|
2334
2440
|
}
|
|
2335
2441
|
const problems = [];
|
|
2336
|
-
if (!codeReviewClean)
|
|
2442
|
+
if (!codeReviewClean) {
|
|
2337
2443
|
problems.push(`code-reviewer verdict is not a clean APPROVE (found: ${codeReview.value})`);
|
|
2338
|
-
if (!
|
|
2444
|
+
} else if (!codeReviewBound) {
|
|
2445
|
+
problems.push(`the newest code-review verdict covers ${codeReview.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2446
|
+
}
|
|
2447
|
+
if (!securityClean) {
|
|
2339
2448
|
problems.push(`security-review verdict is not a clean PASS (found: ${security.value})`);
|
|
2449
|
+
} else if (!securityBound) {
|
|
2450
|
+
problems.push(`the newest security-review verdict covers ${security.headSha ?? "no recorded commit"}, head is ${input.headSha}`);
|
|
2451
|
+
}
|
|
2340
2452
|
const ignoredNote = ignoredCount > 0 ? ` ${ignoredCount} verdict-shaped comment(s) from authors outside the principal allowlist were ignored.` : "";
|
|
2341
2453
|
return {
|
|
2342
2454
|
verdict: "fail",
|
|
@@ -2940,6 +3052,21 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
2940
3052
|
messages.push("All Test Plan items ticked — runtime verification complete.", "PASS.");
|
|
2941
3053
|
return { verdict: "pass", messages };
|
|
2942
3054
|
}
|
|
3055
|
+
// src/lib/diff-evidence.ts
|
|
3056
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
3057
|
+
function fileDiffAgainst(ref, path) {
|
|
3058
|
+
let out;
|
|
3059
|
+
try {
|
|
3060
|
+
out = execFileSync2("git", ["diff", `${ref}...HEAD`, "--", path], {
|
|
3061
|
+
encoding: "utf8",
|
|
3062
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3063
|
+
}).trim();
|
|
3064
|
+
} catch {
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
3067
|
+
return out === "" ? null : out;
|
|
3068
|
+
}
|
|
3069
|
+
|
|
2943
3070
|
// src/checks/contract.ts
|
|
2944
3071
|
var CHECK_SCHEMA_VERSION = 1;
|
|
2945
3072
|
function emitCheckError(error) {
|
|
@@ -2948,7 +3075,7 @@ function emitCheckError(error) {
|
|
|
2948
3075
|
}
|
|
2949
3076
|
|
|
2950
3077
|
// src/lib/config.ts
|
|
2951
|
-
import { execFileSync as
|
|
3078
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
2952
3079
|
import { homedir } from "node:os";
|
|
2953
3080
|
import { dirname, join } from "node:path";
|
|
2954
3081
|
import { z } from "zod";
|
|
@@ -3046,7 +3173,8 @@ var VinayaConfigSchema = z.object({
|
|
|
3046
3173
|
briefSchema: BriefSchemaSchema.optional(),
|
|
3047
3174
|
managed: ManagedManifestSchema.optional(),
|
|
3048
3175
|
principals: z.array(z.string()).min(1).optional(),
|
|
3049
|
-
ci: z.object({ setup: z.string().min(1) }).optional()
|
|
3176
|
+
ci: z.object({ setup: z.string().min(1) }).optional(),
|
|
3177
|
+
blastRadius: z.object({ extraDomains: z.array(z.string()).optional() }).optional()
|
|
3050
3178
|
});
|
|
3051
3179
|
var GLOBAL_VINAYA_HOME = join(homedir(), ".vinaya");
|
|
3052
3180
|
var GLOBAL_CONFIG_PATH = join(GLOBAL_VINAYA_HOME, "config.json");
|
|
@@ -3063,7 +3191,7 @@ function trustAnchorRepo() {
|
|
|
3063
3191
|
return validated;
|
|
3064
3192
|
}
|
|
3065
3193
|
try {
|
|
3066
|
-
const url =
|
|
3194
|
+
const url = execFileSync3("git", ["remote", "get-url", "origin"], {
|
|
3067
3195
|
encoding: "utf-8",
|
|
3068
3196
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3069
3197
|
}).trim();
|
|
@@ -3077,7 +3205,7 @@ function ghFetchTrustAnchorConfig() {
|
|
|
3077
3205
|
const repo = trustAnchorRepo();
|
|
3078
3206
|
if (!repo)
|
|
3079
3207
|
throw new Error("could not resolve repo identity for the trust-anchor read");
|
|
3080
|
-
return
|
|
3208
|
+
return execFileSync3("gh", ["api", `repos/${repo}/contents/${LOCAL_CONFIG_FILENAME}`, "--jq", ".content"], {
|
|
3081
3209
|
encoding: "utf-8",
|
|
3082
3210
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3083
3211
|
});
|
|
@@ -3125,7 +3253,7 @@ function loadTrustAnchorConfig(fetcher = ghFetchTrustAnchorConfig) {
|
|
|
3125
3253
|
var CHECK_NAME = "doc-coverage";
|
|
3126
3254
|
function git(args) {
|
|
3127
3255
|
try {
|
|
3128
|
-
return
|
|
3256
|
+
return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
3129
3257
|
} catch {
|
|
3130
3258
|
return "";
|
|
3131
3259
|
}
|
|
@@ -3148,7 +3276,7 @@ function resolvePrBody() {
|
|
|
3148
3276
|
}
|
|
3149
3277
|
function fetchPrLabels(prNumber) {
|
|
3150
3278
|
try {
|
|
3151
|
-
const out =
|
|
3279
|
+
const out = execFileSync4("gh", ["pr", "view", String(prNumber), "--json", "labels"], {
|
|
3152
3280
|
encoding: "utf8",
|
|
3153
3281
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3154
3282
|
});
|
|
@@ -3159,7 +3287,7 @@ function fetchPrLabels(prNumber) {
|
|
|
3159
3287
|
}
|
|
3160
3288
|
function fetchWaiverLabelActor(prNumber, label2) {
|
|
3161
3289
|
try {
|
|
3162
|
-
const out =
|
|
3290
|
+
const out = execFileSync4("gh", ["api", `repos/{owner}/{repo}/issues/${prNumber}/timeline`, "--paginate"], {
|
|
3163
3291
|
encoding: "utf8",
|
|
3164
3292
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3165
3293
|
});
|
|
@@ -3188,14 +3316,17 @@ function waiverActive() {
|
|
|
3188
3316
|
}
|
|
3189
3317
|
function main() {
|
|
3190
3318
|
const base = process.env.BASE_SHA || "origin/main";
|
|
3191
|
-
let
|
|
3192
|
-
|
|
3193
|
-
|
|
3319
|
+
let ref = base;
|
|
3320
|
+
let changed = changedFiles(ref);
|
|
3321
|
+
if (changed.length === 0) {
|
|
3322
|
+
ref = "main";
|
|
3323
|
+
changed = changedFiles(ref);
|
|
3324
|
+
}
|
|
3194
3325
|
if (changed.length === 0) {
|
|
3195
3326
|
process.exit(0);
|
|
3196
3327
|
}
|
|
3197
3328
|
const content = existsSync(DOC_OWNERS_PATH) ? readFileSync(DOC_OWNERS_PATH, "utf8") : null;
|
|
3198
|
-
const result = evaluateC5(changed, content, resolvePrBody(), existsSync, waiverActive());
|
|
3329
|
+
const result = evaluateC5(changed, content, resolvePrBody(), existsSync, waiverActive(), (p) => fileDiffAgainst(ref, p));
|
|
3199
3330
|
if (result.errors.length > 0) {
|
|
3200
3331
|
for (const message of result.errors) {
|
|
3201
3332
|
emitCheckError({
|