@attalabs/vinaya 0.9.0 → 0.12.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 +7 -5
- package/aeg-root/enforcement.md +3 -3
- 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 +1 -1
- package/aeg-root/roles/reviewer.md +1 -1
- package/aeg-root/roles/security.md +1 -1
- package/aeg-root/roles/tranche-archivist.md +1 -1
- package/aeg-root/state-machine.md +6 -6
- package/aeg-root/templates/pr-report-template.md +8 -4
- package/aeg-root/tranche-model.md +33 -11
- package/dist/checks/bin/check-branch-topology.js +204 -115
- package/dist/checks/bin/check-brief-shape.js +204 -115
- package/dist/checks/bin/check-closes-n.js +204 -115
- package/dist/checks/bin/check-coherence.js +204 -115
- package/dist/checks/bin/check-dead-branch-push.js +204 -115
- package/dist/checks/bin/check-dispatch-readiness.js +204 -115
- package/dist/checks/bin/check-doc-coverage-push.js +231 -124
- package/dist/checks/bin/check-doc-coverage.js +233 -126
- package/dist/checks/bin/check-evidence-fresh.js +3173 -0
- package/dist/checks/bin/check-first-push-dispatch.js +204 -115
- package/dist/checks/bin/check-issue-assignment.js +204 -115
- package/dist/checks/bin/check-no-disk-state.js +204 -115
- package/dist/checks/bin/check-reader-resolvable-prose.js +204 -115
- package/dist/checks/bin/check-registry-gates.js +204 -115
- package/dist/checks/bin/check-review-gate.js +204 -115
- package/dist/checks/bin/check-single-plan-pr.js +204 -115
- package/dist/checks/bin/check-test-plan.js +204 -115
- package/dist/index.js +487 -221
- 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/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/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]__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]__1wsl6ro._.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/_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/{3zyedaek9nvr_.js → 1eo32v1tbjy7c.js} +1 -1
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/1t7yy_s34022p.js +1 -0
- package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3antrj5z0uf_c.js → 2ieu4oi-pp_bz.js} +1 -1
- 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/2e68sbtdhi5dg.js +0 -1
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{pY8RKWuVAUZOjj2-bAYCY → ZHVE9Y9G8XKR91g27SgVX}/_buildManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{pY8RKWuVAUZOjj2-bAYCY → ZHVE9Y9G8XKR91g27SgVX}/_clientMiddlewareManifest.js +0 -0
- /package/studio-standalone/apps/vinaya-studio/web/.next/static/{pY8RKWuVAUZOjj2-bAYCY → ZHVE9Y9G8XKR91g27SgVX}/_ssgManifest.js +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/checks/bin/check-doc-coverage-push.ts
|
|
4
|
-
import { execFileSync as
|
|
4
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
5
5
|
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
-
// ../../packages/aeg-
|
|
6
|
+
// ../../packages/aeg-forge-state/src/strip-code.ts
|
|
7
7
|
function maskCode(body) {
|
|
8
8
|
const fill = (line) => " ".repeat(line.length);
|
|
9
9
|
return maskIndentedCode(maskFencedCode(body, fill), fill).replace(/(`+)[^\n]*?\1/g, (m) => " ".repeat(m.length));
|
|
@@ -20,29 +20,38 @@ function stripCode(body, options = {}) {
|
|
|
20
20
|
var FENCE_OPEN = /^ {0,3}(`{3,}|~{3,})([^\r\n]*)\r?$/;
|
|
21
21
|
var FENCE_CLOSE = /^ {0,3}(`+|~+)[ \t]*\r?$/;
|
|
22
22
|
function maskFencedCode(body, fill) {
|
|
23
|
+
const { lines } = scanFencedCode(body);
|
|
24
|
+
return lines.map(({ line, inCode }) => inCode ? fill(line) : line).join(`
|
|
25
|
+
`);
|
|
26
|
+
}
|
|
27
|
+
function scanFencedCode(body) {
|
|
23
28
|
let fenceChar = null;
|
|
24
29
|
let fenceLen = 0;
|
|
25
|
-
|
|
30
|
+
const lines = body.split(`
|
|
26
31
|
`).map((line) => {
|
|
27
32
|
if (fenceChar === null) {
|
|
28
33
|
const open = line.match(FENCE_OPEN);
|
|
29
34
|
if (!open)
|
|
30
|
-
return line;
|
|
35
|
+
return { line, inCode: false };
|
|
31
36
|
const marker = open[1];
|
|
32
37
|
if (marker[0] === "`" && open[2].includes("`"))
|
|
33
|
-
return line;
|
|
38
|
+
return { line, inCode: false };
|
|
34
39
|
fenceChar = marker[0];
|
|
35
40
|
fenceLen = marker.length;
|
|
36
|
-
return
|
|
41
|
+
return { line, inCode: true };
|
|
37
42
|
}
|
|
38
43
|
const close = line.match(FENCE_CLOSE);
|
|
39
44
|
if (close && close[1][0] === fenceChar && close[1].length >= fenceLen) {
|
|
40
45
|
fenceChar = null;
|
|
41
46
|
fenceLen = 0;
|
|
42
47
|
}
|
|
43
|
-
return
|
|
44
|
-
})
|
|
45
|
-
|
|
48
|
+
return { line, inCode: true };
|
|
49
|
+
});
|
|
50
|
+
return { lines, unterminated: fenceChar !== null };
|
|
51
|
+
}
|
|
52
|
+
function hasUnterminatedFence(body) {
|
|
53
|
+
return scanFencedCode(body.replace(/\r\n?/g, `
|
|
54
|
+
`)).unterminated;
|
|
46
55
|
}
|
|
47
56
|
var LIST_MARKER = /^ {0,3}(?:[-*+]|\d{1,9}[.)])(?:\s|$)/;
|
|
48
57
|
function indentWidth(line) {
|
|
@@ -85,88 +94,7 @@ function maskIndentedCode(body, fill) {
|
|
|
85
94
|
return out.join(`
|
|
86
95
|
`);
|
|
87
96
|
}
|
|
88
|
-
|
|
89
|
-
const masked = maskCode(body);
|
|
90
|
-
const start = new RegExp(`<!--\\s*AEG:${field}:START\\s*-->`).exec(masked);
|
|
91
|
-
if (!start)
|
|
92
|
-
return null;
|
|
93
|
-
const afterStart = start.index + start[0].length;
|
|
94
|
-
const end = new RegExp(`<!--\\s*AEG:${field}:END\\s*-->`).exec(masked.slice(afterStart));
|
|
95
|
-
if (!end)
|
|
96
|
-
return null;
|
|
97
|
-
return body.slice(afterStart, afterStart + end.index);
|
|
98
|
-
}
|
|
99
|
-
// ../../packages/aeg-core/src/parse-registry.ts
|
|
100
|
-
function parseRegistry(md) {
|
|
101
|
-
const rows = readMarkdownTable(md, /^##\s+Registry\b/i);
|
|
102
|
-
const projects = [];
|
|
103
|
-
for (const row of rows) {
|
|
104
|
-
if (row.length < 4)
|
|
105
|
-
continue;
|
|
106
|
-
const name = stripBackticks(row[0] ?? "").trim();
|
|
107
|
-
const path = stripBackticks(row[1] ?? "").trim();
|
|
108
|
-
const specsPath = stripBackticks(row[2] ?? "").trim();
|
|
109
|
-
const stateCell = (row[3] ?? "").trim();
|
|
110
|
-
if (!name || !path || !specsPath)
|
|
111
|
-
continue;
|
|
112
|
-
projects.push({
|
|
113
|
-
name,
|
|
114
|
-
path,
|
|
115
|
-
specsPath,
|
|
116
|
-
statePath: parseStateCell(stateCell)
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return projects;
|
|
120
|
-
}
|
|
121
|
-
function parseStateCell(cell) {
|
|
122
|
-
if (!cell)
|
|
123
|
-
return null;
|
|
124
|
-
const stripped = stripBackticks(cell).trim();
|
|
125
|
-
if (/^[\w./@-]+\/?$/.test(stripped) && cell.includes("`")) {
|
|
126
|
-
return stripped;
|
|
127
|
-
}
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
function stripBackticks(s) {
|
|
131
|
-
return s.replace(/`/g, "");
|
|
132
|
-
}
|
|
133
|
-
function readMarkdownTable(md, headingMatcher) {
|
|
134
|
-
const lines = md.split(/\r?\n/);
|
|
135
|
-
let i = 0;
|
|
136
|
-
for (;i < lines.length; i++) {
|
|
137
|
-
if (headingMatcher.test(lines[i] ?? "")) {
|
|
138
|
-
i++;
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (i >= lines.length)
|
|
143
|
-
return [];
|
|
144
|
-
for (;i < lines.length; i++) {
|
|
145
|
-
if ((lines[i] ?? "").trim().startsWith("|"))
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
if (i + 1 >= lines.length)
|
|
149
|
-
return [];
|
|
150
|
-
i += 2;
|
|
151
|
-
const out = [];
|
|
152
|
-
for (;i < lines.length; i++) {
|
|
153
|
-
const trimmed = (lines[i] ?? "").trim();
|
|
154
|
-
if (!trimmed.startsWith("|"))
|
|
155
|
-
break;
|
|
156
|
-
out.push(splitTableRow(trimmed));
|
|
157
|
-
}
|
|
158
|
-
return out;
|
|
159
|
-
}
|
|
160
|
-
function splitTableRow(row) {
|
|
161
|
-
const PLACEHOLDER = "\x00PIPE\x00";
|
|
162
|
-
const escaped = row.replace(/\\\|/g, PLACEHOLDER);
|
|
163
|
-
const parts = escaped.split("|").map((s) => s.trim());
|
|
164
|
-
if (parts.length && parts[0] === "")
|
|
165
|
-
parts.shift();
|
|
166
|
-
if (parts.length && parts[parts.length - 1] === "")
|
|
167
|
-
parts.pop();
|
|
168
|
-
return parts.map((p) => p.split(PLACEHOLDER).join("|"));
|
|
169
|
-
}
|
|
97
|
+
|
|
170
98
|
// ../../packages/aeg-forge-state/src/parse-rationale-deps.ts
|
|
171
99
|
var SECTION_HEADER = /\*\*Dependency rationale\*\*/i;
|
|
172
100
|
var NEXT_HEADER = /\*\*[A-Z][^*]*\*\*/;
|
|
@@ -464,11 +392,37 @@ function findTrancheSlug(names) {
|
|
|
464
392
|
var TITLE_PATTERN = /^\[([^\]]+)]\s*(\S+)\s*—\s*(.+)$/;
|
|
465
393
|
var PROJECT_SLUG = /^[a-z0-9][a-z0-9-]*$/i;
|
|
466
394
|
var PROJECT_FIELD = /^\s*(?:\*\*)?Project(?:\(s\))?(?:\*\*)?\s*:\s*(?:\*\*)?\s*(.+)$/im;
|
|
395
|
+
var VALUE_EDGE_MARKUP = /^[`.;\s]+|[`.;\s]+$/g;
|
|
396
|
+
function unwrapValue(raw) {
|
|
397
|
+
return raw.replace(/\*\*/g, "").replace(VALUE_EDGE_MARKUP, "");
|
|
398
|
+
}
|
|
399
|
+
function projectFieldFromBody(body) {
|
|
400
|
+
const m = stripCode(body, { inlineSpans: "keep" }).match(PROJECT_FIELD);
|
|
401
|
+
if (m)
|
|
402
|
+
return parseFieldValue(m[1] ?? "");
|
|
403
|
+
if (hasUnterminatedFence(body)) {
|
|
404
|
+
const raw = body.match(PROJECT_FIELD);
|
|
405
|
+
if (raw)
|
|
406
|
+
return { declared: true, names: [], unparsed: [(raw[1] ?? "").trim()], unreadable: true };
|
|
407
|
+
}
|
|
408
|
+
return { declared: false, names: [], unparsed: [], unreadable: false };
|
|
409
|
+
}
|
|
410
|
+
function parseFieldValue(value) {
|
|
411
|
+
const names = [];
|
|
412
|
+
const unparsed = [];
|
|
413
|
+
for (const raw of value.split(",")) {
|
|
414
|
+
const name = unwrapValue(raw);
|
|
415
|
+
if (name.length === 0)
|
|
416
|
+
continue;
|
|
417
|
+
if (PROJECT_SLUG.test(name))
|
|
418
|
+
names.push(name);
|
|
419
|
+
else
|
|
420
|
+
unparsed.push(raw.trim());
|
|
421
|
+
}
|
|
422
|
+
return { declared: true, names: [...new Set(names)], unparsed, unreadable: false };
|
|
423
|
+
}
|
|
467
424
|
function projectsFromBody(body) {
|
|
468
|
-
|
|
469
|
-
if (!m)
|
|
470
|
-
return [];
|
|
471
|
-
return (m[1] ?? "").split(",").map((s) => s.trim()).filter((s) => PROJECT_SLUG.test(s));
|
|
425
|
+
return projectFieldFromBody(body).names;
|
|
472
426
|
}
|
|
473
427
|
function taskFromIssue(issue) {
|
|
474
428
|
const m = issue.title.match(TITLE_PATTERN);
|
|
@@ -510,8 +464,8 @@ function tasksFromIssues(issues) {
|
|
|
510
464
|
}
|
|
511
465
|
return tasks.sort((a, b) => compareTaskIds(a.id, b.id));
|
|
512
466
|
}
|
|
513
|
-
async function
|
|
514
|
-
return
|
|
467
|
+
async function fetchTrancheIssuesAsync(owner, repo, slug) {
|
|
468
|
+
return ghIssueListByAnyLabelAsync(owner, repo, [trancheLabel(slug)]);
|
|
515
469
|
}
|
|
516
470
|
function resolveTaskIssueRef(title, labels) {
|
|
517
471
|
const m = title.match(TITLE_PATTERN);
|
|
@@ -527,17 +481,20 @@ function resolveTaskIssueRef(title, labels) {
|
|
|
527
481
|
}
|
|
528
482
|
|
|
529
483
|
// ../../packages/aeg-forge-state/src/derive-from-forge.ts
|
|
530
|
-
|
|
531
|
-
const milestone = known ?? findMilestoneForSlug(owner, repo, slug);
|
|
532
|
-
const tasks = await listTasksForSlugAsync(owner, repo, slug);
|
|
484
|
+
function trancheFromIssues(slug, issues, known) {
|
|
533
485
|
return {
|
|
534
486
|
name: slug,
|
|
535
|
-
lifecycle:
|
|
536
|
-
goal:
|
|
537
|
-
tasks,
|
|
487
|
+
lifecycle: known?.lifecycle ?? "active",
|
|
488
|
+
goal: known?.goal ?? "",
|
|
489
|
+
tasks: tasksFromIssues(issues),
|
|
538
490
|
backlog: []
|
|
539
491
|
};
|
|
540
492
|
}
|
|
493
|
+
async function deriveTrancheFromForge(owner, repo, slug, known) {
|
|
494
|
+
const milestone = known ?? findMilestoneForSlug(owner, repo, slug);
|
|
495
|
+
const issues = await fetchTrancheIssuesAsync(owner, repo, slug);
|
|
496
|
+
return trancheFromIssues(slug, issues, milestone);
|
|
497
|
+
}
|
|
541
498
|
// ../../node_modules/universal-user-agent/index.js
|
|
542
499
|
function getUserAgent() {
|
|
543
500
|
if (typeof navigator === "object" && "userAgent" in navigator) {
|
|
@@ -1457,6 +1414,96 @@ var systemEnv3 = {
|
|
|
1457
1414
|
...process.env,
|
|
1458
1415
|
PATH: [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin", "/usr/bin", "/bin"].filter(Boolean).join(":")
|
|
1459
1416
|
};
|
|
1417
|
+
// ../../packages/aeg-core/src/anchored-region.ts
|
|
1418
|
+
function anchoredRegionBounds(body, field) {
|
|
1419
|
+
const masked = maskCode(body);
|
|
1420
|
+
const start = new RegExp(`<!--\\s*AEG:${field}:START\\s*-->`).exec(masked);
|
|
1421
|
+
if (!start)
|
|
1422
|
+
return null;
|
|
1423
|
+
const innerStart = start.index + start[0].length;
|
|
1424
|
+
const end = new RegExp(`<!--\\s*AEG:${field}:END\\s*-->`).exec(masked.slice(innerStart));
|
|
1425
|
+
if (!end)
|
|
1426
|
+
return null;
|
|
1427
|
+
const innerEnd = innerStart + end.index;
|
|
1428
|
+
return { outerStart: start.index, outerEnd: innerEnd + end[0].length, innerStart, innerEnd };
|
|
1429
|
+
}
|
|
1430
|
+
function anchoredRegion(body, field) {
|
|
1431
|
+
const bounds = anchoredRegionBounds(body, field);
|
|
1432
|
+
if (!bounds)
|
|
1433
|
+
return null;
|
|
1434
|
+
return body.slice(bounds.innerStart, bounds.innerEnd);
|
|
1435
|
+
}
|
|
1436
|
+
// ../../packages/aeg-core/src/parse-registry.ts
|
|
1437
|
+
function parseRegistry(md) {
|
|
1438
|
+
const rows = readMarkdownTable(md, /^##\s+Registry\b/i);
|
|
1439
|
+
const projects = [];
|
|
1440
|
+
for (const row of rows) {
|
|
1441
|
+
if (row.length < 4)
|
|
1442
|
+
continue;
|
|
1443
|
+
const name = stripBackticks(row[0] ?? "").trim();
|
|
1444
|
+
const path = stripBackticks(row[1] ?? "").trim();
|
|
1445
|
+
const specsPath = stripBackticks(row[2] ?? "").trim();
|
|
1446
|
+
const stateCell = (row[3] ?? "").trim();
|
|
1447
|
+
if (!name || !path || !specsPath)
|
|
1448
|
+
continue;
|
|
1449
|
+
projects.push({
|
|
1450
|
+
name,
|
|
1451
|
+
path,
|
|
1452
|
+
specsPath,
|
|
1453
|
+
statePath: parseStateCell(stateCell)
|
|
1454
|
+
});
|
|
1455
|
+
}
|
|
1456
|
+
return projects;
|
|
1457
|
+
}
|
|
1458
|
+
function parseStateCell(cell) {
|
|
1459
|
+
if (!cell)
|
|
1460
|
+
return null;
|
|
1461
|
+
const stripped = stripBackticks(cell).trim();
|
|
1462
|
+
if (/^[\w./@-]+\/?$/.test(stripped) && cell.includes("`")) {
|
|
1463
|
+
return stripped;
|
|
1464
|
+
}
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
function stripBackticks(s) {
|
|
1468
|
+
return s.replace(/`/g, "");
|
|
1469
|
+
}
|
|
1470
|
+
function readMarkdownTable(md, headingMatcher) {
|
|
1471
|
+
const lines = md.split(/\r?\n/);
|
|
1472
|
+
let i = 0;
|
|
1473
|
+
for (;i < lines.length; i++) {
|
|
1474
|
+
if (headingMatcher.test(lines[i] ?? "")) {
|
|
1475
|
+
i++;
|
|
1476
|
+
break;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
if (i >= lines.length)
|
|
1480
|
+
return [];
|
|
1481
|
+
for (;i < lines.length; i++) {
|
|
1482
|
+
if ((lines[i] ?? "").trim().startsWith("|"))
|
|
1483
|
+
break;
|
|
1484
|
+
}
|
|
1485
|
+
if (i + 1 >= lines.length)
|
|
1486
|
+
return [];
|
|
1487
|
+
i += 2;
|
|
1488
|
+
const out = [];
|
|
1489
|
+
for (;i < lines.length; i++) {
|
|
1490
|
+
const trimmed = (lines[i] ?? "").trim();
|
|
1491
|
+
if (!trimmed.startsWith("|"))
|
|
1492
|
+
break;
|
|
1493
|
+
out.push(splitTableRow(trimmed));
|
|
1494
|
+
}
|
|
1495
|
+
return out;
|
|
1496
|
+
}
|
|
1497
|
+
function splitTableRow(row) {
|
|
1498
|
+
const PLACEHOLDER = "\x00PIPE\x00";
|
|
1499
|
+
const escaped = row.replace(/\\\|/g, PLACEHOLDER);
|
|
1500
|
+
const parts = escaped.split("|").map((s) => s.trim());
|
|
1501
|
+
if (parts.length && parts[0] === "")
|
|
1502
|
+
parts.shift();
|
|
1503
|
+
if (parts.length && parts[parts.length - 1] === "")
|
|
1504
|
+
parts.pop();
|
|
1505
|
+
return parts.map((p) => p.split(PLACEHOLDER).join("|"));
|
|
1506
|
+
}
|
|
1460
1507
|
// ../../packages/aeg-core/src/state-machine-model.ts
|
|
1461
1508
|
var FORGE_FACT_INPUTS = [
|
|
1462
1509
|
{
|
|
@@ -1960,23 +2007,65 @@ function checkIssueRationale(body) {
|
|
|
1960
2007
|
function isTaskIssueLabelSet(labels) {
|
|
1961
2008
|
return hasLabel("tranche", labels);
|
|
1962
2009
|
}
|
|
2010
|
+
function isControlCodePoint(codePoint) {
|
|
2011
|
+
return codePoint <= 31 || codePoint >= 127 && codePoint <= 159;
|
|
2012
|
+
}
|
|
2013
|
+
function isDisplayControlCodePoint(codePoint) {
|
|
2014
|
+
return codePoint >= 8203 && codePoint <= 8207 || codePoint >= 8234 && codePoint <= 8238 || codePoint >= 8294 && codePoint <= 8297 || codePoint === 65279;
|
|
2015
|
+
}
|
|
2016
|
+
var MESSAGE_VALUE_COUNT_MAX = 5;
|
|
2017
|
+
function residueForMessage(values) {
|
|
2018
|
+
const shown = values.slice(0, MESSAGE_VALUE_COUNT_MAX).map((v) => `\`${forMessage(v)}\``);
|
|
2019
|
+
const rest = values.length - shown.length;
|
|
2020
|
+
return rest > 0 ? `${shown.join(", ")} and ${rest} more` : shown.join(", ");
|
|
2021
|
+
}
|
|
2022
|
+
var MESSAGE_VALUE_MAX = 64;
|
|
2023
|
+
function forMessage(value) {
|
|
2024
|
+
const kept = [];
|
|
2025
|
+
for (const ch of value.replace(/\s+/g, " ")) {
|
|
2026
|
+
const codePoint = ch.codePointAt(0) ?? 0;
|
|
2027
|
+
if (isControlCodePoint(codePoint) || isDisplayControlCodePoint(codePoint))
|
|
2028
|
+
continue;
|
|
2029
|
+
kept.push(ch);
|
|
2030
|
+
}
|
|
2031
|
+
const cleaned = kept.join("").trim();
|
|
2032
|
+
if (cleaned.length === 0)
|
|
2033
|
+
return "(unprintable)";
|
|
2034
|
+
const points = Array.from(cleaned);
|
|
2035
|
+
return points.length > MESSAGE_VALUE_MAX ? `${points.slice(0, MESSAGE_VALUE_MAX).join("")}…` : cleaned;
|
|
2036
|
+
}
|
|
1963
2037
|
function checkProjectsRegistered(body, _labels, registeredNames) {
|
|
1964
2038
|
if (registeredNames.length === 0)
|
|
1965
2039
|
return { status: "pass", errors: [] };
|
|
1966
|
-
const
|
|
1967
|
-
|
|
1968
|
-
if (unregistered.length === 0)
|
|
2040
|
+
const field = projectFieldFromBody(body);
|
|
2041
|
+
if (!field.declared)
|
|
1969
2042
|
return { status: "pass", errors: [] };
|
|
1970
|
-
const
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
2043
|
+
const known = new Set(registeredNames.map((n) => n.trim()));
|
|
2044
|
+
const registeredList = [...registeredNames].map(forMessage).join(", ");
|
|
2045
|
+
const errors = [];
|
|
2046
|
+
if (field.unreadable) {
|
|
2047
|
+
return {
|
|
2048
|
+
status: "fail",
|
|
2049
|
+
errors: [
|
|
2050
|
+
`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.`
|
|
2051
|
+
]
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
const unregistered = field.names.filter((p) => !known.has(p));
|
|
2055
|
+
if (unregistered.length > 0) {
|
|
2056
|
+
const caseHints = unregistered.map((p) => {
|
|
2057
|
+
const row = [...known].find((k) => k.toLowerCase() === p.toLowerCase());
|
|
2058
|
+
return row ? `\`${forMessage(p)}\` differs from the registered \`${forMessage(row)}\` only in case` : null;
|
|
2059
|
+
}).filter((h) => h !== null);
|
|
2060
|
+
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.`);
|
|
2061
|
+
}
|
|
2062
|
+
if (field.names.length === 0) {
|
|
2063
|
+
const residue = field.unparsed.length > 0 ? ` — ${residueForMessage(field.unparsed)}` : " — the field is empty";
|
|
2064
|
+
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.`);
|
|
2065
|
+
} else if (field.unparsed.length > 0) {
|
|
2066
|
+
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.`);
|
|
2067
|
+
}
|
|
2068
|
+
return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
|
|
1980
2069
|
}
|
|
1981
2070
|
|
|
1982
2071
|
// ../../packages/aeg-core/src/coherence-checks.ts
|
|
@@ -2939,6 +3028,21 @@ function evaluateTestPlanGate(body, branch) {
|
|
|
2939
3028
|
messages.push("All Test Plan items ticked — runtime verification complete.", "PASS.");
|
|
2940
3029
|
return { verdict: "pass", messages };
|
|
2941
3030
|
}
|
|
3031
|
+
// src/lib/diff-evidence.ts
|
|
3032
|
+
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
3033
|
+
function fileDiffAgainst(ref, path) {
|
|
3034
|
+
let out;
|
|
3035
|
+
try {
|
|
3036
|
+
out = execFileSync2("git", ["diff", `${ref}...HEAD`, "--", path], {
|
|
3037
|
+
encoding: "utf8",
|
|
3038
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
3039
|
+
}).trim();
|
|
3040
|
+
} catch {
|
|
3041
|
+
return null;
|
|
3042
|
+
}
|
|
3043
|
+
return out === "" ? null : out;
|
|
3044
|
+
}
|
|
3045
|
+
|
|
2942
3046
|
// src/checks/contract.ts
|
|
2943
3047
|
var CHECK_SCHEMA_VERSION = 1;
|
|
2944
3048
|
function emitCheckError(error) {
|
|
@@ -2947,7 +3051,7 @@ function emitCheckError(error) {
|
|
|
2947
3051
|
}
|
|
2948
3052
|
|
|
2949
3053
|
// src/lib/config.ts
|
|
2950
|
-
import { execFileSync as
|
|
3054
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
2951
3055
|
import { homedir } from "node:os";
|
|
2952
3056
|
import { dirname, join } from "node:path";
|
|
2953
3057
|
import { z } from "zod";
|
|
@@ -3062,7 +3166,7 @@ function trustAnchorRepo() {
|
|
|
3062
3166
|
return validated;
|
|
3063
3167
|
}
|
|
3064
3168
|
try {
|
|
3065
|
-
const url =
|
|
3169
|
+
const url = execFileSync3("git", ["remote", "get-url", "origin"], {
|
|
3066
3170
|
encoding: "utf-8",
|
|
3067
3171
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3068
3172
|
}).trim();
|
|
@@ -3076,7 +3180,7 @@ function ghFetchTrustAnchorConfig() {
|
|
|
3076
3180
|
const repo = trustAnchorRepo();
|
|
3077
3181
|
if (!repo)
|
|
3078
3182
|
throw new Error("could not resolve repo identity for the trust-anchor read");
|
|
3079
|
-
return
|
|
3183
|
+
return execFileSync3("gh", ["api", `repos/${repo}/contents/${LOCAL_CONFIG_FILENAME}`, "--jq", ".content"], {
|
|
3080
3184
|
encoding: "utf-8",
|
|
3081
3185
|
stdio: ["ignore", "pipe", "pipe"]
|
|
3082
3186
|
});
|
|
@@ -3124,7 +3228,7 @@ function loadTrustAnchorConfig(fetcher = ghFetchTrustAnchorConfig) {
|
|
|
3124
3228
|
var CHECK_NAME = "doc-coverage-push";
|
|
3125
3229
|
function git(args) {
|
|
3126
3230
|
try {
|
|
3127
|
-
return
|
|
3231
|
+
return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
3128
3232
|
} catch {
|
|
3129
3233
|
return "";
|
|
3130
3234
|
}
|
|
@@ -3165,13 +3269,16 @@ function main() {
|
|
|
3165
3269
|
process.exit(0);
|
|
3166
3270
|
}
|
|
3167
3271
|
const base = process.env.BASE_SHA || "origin/main";
|
|
3168
|
-
let
|
|
3169
|
-
|
|
3170
|
-
|
|
3272
|
+
let ref = base;
|
|
3273
|
+
let changed = changedFiles(ref);
|
|
3274
|
+
if (changed.length === 0) {
|
|
3275
|
+
ref = "main";
|
|
3276
|
+
changed = changedFiles(ref);
|
|
3277
|
+
}
|
|
3171
3278
|
if (changed.length === 0)
|
|
3172
3279
|
process.exit(0);
|
|
3173
3280
|
const content = existsSync(DOC_OWNERS_PATH) ? readFileSync(DOC_OWNERS_PATH, "utf8") : null;
|
|
3174
|
-
const result = evaluateC5(changed, content, resolvePrBody(), existsSync, waiverActiveFromEnv());
|
|
3281
|
+
const result = evaluateC5(changed, content, resolvePrBody(), existsSync, waiverActiveFromEnv(), (p) => fileDiffAgainst(ref, p));
|
|
3175
3282
|
for (const message of result.errors) {
|
|
3176
3283
|
emitCheckError({
|
|
3177
3284
|
schema: CHECK_SCHEMA_VERSION,
|