@attalabs/vinaya 0.19.3 → 0.20.1

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.
Files changed (112) hide show
  1. package/README.md +7 -0
  2. package/aeg-root/contracts/archivist-tranche-archivist.md +2 -2
  3. package/aeg-root/contracts/tranche-archivist-planner.md +8 -8
  4. package/aeg-root/enforcement.md +6 -4
  5. package/aeg-root/process.md +5 -5
  6. package/aeg-root/roles/archivist.md +9 -10
  7. package/aeg-root/roles/brief-author.md +1 -1
  8. package/aeg-root/roles/developer.md +1 -1
  9. package/aeg-root/roles/planner.md +1 -1
  10. package/aeg-root/roles/principal.md +1 -1
  11. package/aeg-root/roles/tranche-archivist.md +3 -16
  12. package/aeg-root/skills/aeg/SKILL.md +30 -1
  13. package/aeg-root/skills/aeg-roles/SKILL.md +1 -1
  14. package/aeg-root/skills/brief-authoring/SKILL.md +2 -2
  15. package/aeg-root/state-machine.md +8 -6
  16. package/aeg-root/task-model.md +1 -1
  17. package/aeg-root/templates/pr-report-template.md +4 -0
  18. package/dist/checks/bin/check-body-bare-digits.js +192 -27
  19. package/dist/checks/bin/check-branch-topology.js +518 -11
  20. package/dist/checks/bin/check-brief-shape.js +104 -11
  21. package/dist/checks/bin/check-closes-n.js +518 -11
  22. package/dist/checks/bin/check-coherence.js +526 -12
  23. package/dist/checks/bin/check-dead-branch-push.js +104 -11
  24. package/dist/checks/bin/check-dispatch-readiness.js +621 -14
  25. package/dist/checks/bin/check-doc-coverage-push.js +170 -20
  26. package/dist/checks/bin/check-doc-coverage.js +170 -20
  27. package/dist/checks/bin/check-doctrine-portability.js +112 -12
  28. package/dist/checks/bin/check-evidence-fresh.js +213 -20
  29. package/dist/checks/bin/check-first-push-dispatch.js +526 -12
  30. package/dist/checks/bin/check-issue-assignment.js +518 -11
  31. package/dist/checks/bin/check-main-branch-refusal.js +3724 -0
  32. package/dist/checks/bin/check-no-disk-state.js +104 -11
  33. package/dist/checks/bin/check-reader-resolvable-prose.js +186 -17
  34. package/dist/checks/bin/check-registry-gates.js +185 -16
  35. package/dist/checks/bin/check-retired-vocabulary.js +185 -16
  36. package/dist/checks/bin/check-review-gate.js +112 -12
  37. package/dist/checks/bin/check-single-plan-pr.js +104 -11
  38. package/dist/checks/bin/check-test-plan.js +104 -11
  39. package/dist/checks/bin/check-workspace-escape.js +104 -11
  40. package/dist/index.js +664 -56
  41. package/package.json +1 -1
  42. package/studio-standalone/_node_modules/@attalabs/aeg-core/bin/verify-coherence.ts +1089 -0
  43. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  44. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  45. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  46. package/studio-standalone/apps/vinaya-studio/web/.next/required-server-files.json +5 -0
  47. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  48. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  49. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  50. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  51. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  52. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  53. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  54. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  55. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  56. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  57. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js +1 -1
  58. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  63. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  81. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  82. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  83. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/{[externals]__0pvgf28._.js → [externals]__1agj1ww._.js} +2 -2
  84. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__02a-3g8._.js +1 -1
  85. 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
  86. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0053k9k._.js +1 -1
  87. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0112h-k._.js +1 -1
  88. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0o771t1._.js +1 -1
  89. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0puovz5._.js +1 -1
  90. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__03jepya._.js → [root-of-the-server]__16cuydh._.js} +1 -1
  91. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1hs0dcu._.js +1 -1
  92. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1wc4-ip._.js +1 -1
  93. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_03x_w6q._.js +1 -1
  94. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0gvm3og._.js +1 -1
  95. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0lwxg63._.js +1 -1
  96. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1n0cnq-._.js +1 -1
  97. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  98. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  99. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  100. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  101. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  102. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3noqvds-2bvrd.js → 0ea8-8nopz29h.js} +1 -1
  103. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/0la0hhmjprdxm.css +1 -0
  104. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/1lk7h-0buf4ab.js +1 -0
  105. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{3gehliafvmjo9.js → 2twnwe77d4n-m.js} +1 -1
  106. package/studio-standalone/apps/vinaya-studio/web/server.js +1 -1
  107. package/aeg-root/coordination.md +0 -292
  108. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/2iysedx-n06xc.js +0 -1
  109. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/42wxpzjhw7oy4.css +0 -1
  110. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5Lv83SnT--7YJDjSbxona → b2VfZK2AItZwlghK_pUN0}/_buildManifest.js +0 -0
  111. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5Lv83SnT--7YJDjSbxona → b2VfZK2AItZwlghK_pUN0}/_clientMiddlewareManifest.js +0 -0
  112. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{5Lv83SnT--7YJDjSbxona → b2VfZK2AItZwlghK_pUN0}/_ssgManifest.js +0 -0
@@ -6,17 +6,17 @@ import { readdirSync, readFileSync as readFileSync2, statSync } from "node:fs";
6
6
  import { join as join2 } from "node:path";
7
7
  // ../../packages/aeg-core/src/gate-audience.ts
8
8
  var GATE_AUDIENCE = {
9
- "check-branch-topology": { shippedAs: "branch-topology" },
10
- "check-first-push-dispatch": { shippedAs: "first-push-dispatch" },
11
- "check-no-disk-state": { shippedAs: "no-disk-state" },
12
- "verify-brief": { shippedAs: "brief-shape" },
13
- "verify-coherence": { shippedAs: "coherence" },
14
- "verify-dispatch": { shippedAs: "dispatch-readiness" },
15
- "verify-docs": { shippedAs: ["doc-coverage", "doc-coverage-push"] },
16
- "verify-registry": { shippedAs: "registry-gates" },
17
- "verify-review-gate": { shippedAs: "review-gate" },
18
- "verify-single-plan-pr": { shippedAs: "single-plan-pr" },
19
- "verify-test-plan": { shippedAs: "test-plan" },
9
+ "check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
10
+ "check-first-push-dispatch": { shippedAs: "first-push-dispatch", ring: 0 },
11
+ "check-no-disk-state": { shippedAs: "no-disk-state", ring: 0 },
12
+ "verify-brief": { shippedAs: "brief-shape", ring: 0 },
13
+ "verify-coherence": { shippedAs: "coherence", ring: 0 },
14
+ "verify-dispatch": { shippedAs: "dispatch-readiness", ring: 0 },
15
+ "verify-docs": { shippedAs: ["doc-coverage", "doc-coverage-push"], ring: 0 },
16
+ "verify-registry": { shippedAs: "registry-gates", ring: 0 },
17
+ "verify-review-gate": { shippedAs: "review-gate", ring: 1 },
18
+ "verify-single-plan-pr": { shippedAs: "single-plan-pr", ring: 0 },
19
+ "verify-test-plan": { shippedAs: "test-plan", ring: 1 },
20
20
  "check-direct-main-push": {
21
21
  internal: "Reaches adopters through `vinaya audit --only=direct-push`, not the check registry: it is a ring-2 sweep over merge history keyed on `GITHUB_SHA`, run by the generated archivist job on push to the default branch. It has no diff to key on, so there is nothing for `vinaya check` to run it against."
22
22
  },
@@ -328,6 +328,13 @@ var LABELS = [
328
328
  form: "literal",
329
329
  carries: "Execution is halted pending an external unblock; wins over every other derived status."
330
330
  },
331
+ {
332
+ key: "backlog",
333
+ id: "vinaya/backlog",
334
+ category: "state",
335
+ form: "literal",
336
+ carries: "The Issue is deliberately unplanned — belongs to no tranche and is excluded from dispatch, until a Planner promotes it."
337
+ },
331
338
  {
332
339
  key: "tier-0",
333
340
  id: "vinaya/tier:0",
@@ -1986,7 +1993,59 @@ function overrideActive(opts) {
1986
1993
  return false;
1987
1994
  }
1988
1995
  // ../../packages/aeg-core/src/premise-check.ts
1996
+ import { createHash } from "node:crypto";
1989
1997
  var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
1998
+ function isPremiseHeader(line) {
1999
+ const stripped = line.replace(/[*#]/g, "").trim();
2000
+ return /^premise\s*:?$/i.test(stripped);
2001
+ }
2002
+ var PREMISE_LINE = /^[-*]\s*(\S+)\s+(contains|absent|sha256)\s*:\s*(.+)$/i;
2003
+ function parsePremiseBlock(prBody) {
2004
+ const searchIn = anchoredRegion(prBody, "PREMISE") ?? prBody;
2005
+ const lines = searchIn.split(/\r?\n/);
2006
+ const assertions = [];
2007
+ let inBlock = false;
2008
+ for (const raw of lines) {
2009
+ const line = raw.trim();
2010
+ if (!inBlock) {
2011
+ if (isPremiseHeader(line))
2012
+ inBlock = true;
2013
+ continue;
2014
+ }
2015
+ if (line === "")
2016
+ break;
2017
+ const m = line.match(PREMISE_LINE);
2018
+ if (!m)
2019
+ break;
2020
+ const [, path, kindRaw, value] = m;
2021
+ const kind = kindRaw.toLowerCase();
2022
+ if (!ASSERTION_KINDS.has(kind))
2023
+ continue;
2024
+ assertions.push({ kind, path, value: value.trim() });
2025
+ }
2026
+ return assertions;
2027
+ }
2028
+ function checkPremises(assertions, fileReader) {
2029
+ const failures = [];
2030
+ for (const a of assertions) {
2031
+ const content = fileReader(a.path);
2032
+ if (content === null) {
2033
+ failures.push(`premise-check: ${a.path} does not exist on disk (premise asserted \`${a.kind}: ${a.value}\`) — the surface moved since this brief was authored; re-dig before proceeding.`);
2034
+ continue;
2035
+ }
2036
+ if (a.kind === "contains" && !content.includes(a.value)) {
2037
+ failures.push(`premise-check: ${a.path} no longer contains "${a.value}" — the premise this brief pinned has moved; re-dig before proceeding.`);
2038
+ } else if (a.kind === "absent" && content.includes(a.value)) {
2039
+ failures.push(`premise-check: ${a.path} now contains "${a.value}", but the brief pinned it absent — the premise has moved; re-dig before proceeding.`);
2040
+ } else if (a.kind === "sha256") {
2041
+ const actual = createHash("sha256").update(content).digest("hex");
2042
+ if (actual !== a.value.toLowerCase()) {
2043
+ failures.push(`premise-check: ${a.path} sha256 mismatch (brief pinned ${a.value}, file is now ${actual}) — the file changed since this brief was authored; re-dig before proceeding.`);
2044
+ }
2045
+ }
2046
+ }
2047
+ return { pass: failures.length === 0, failures };
2048
+ }
1990
2049
 
1991
2050
  // ../../packages/aeg-core/src/test-plan-section.ts
1992
2051
  var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
@@ -2790,6 +2849,7 @@ function checkG1(rows, existsFn) {
2790
2849
  }
2791
2850
  return { check: "G1", status: findings.length > 0 ? "fail" : "pass", findings };
2792
2851
  }
2852
+ var SCAFFOLD_PLACEHOLDER = "[undocumented — fill in why]";
2793
2853
  function checkG2(rows, candidateFiles) {
2794
2854
  const implementations = new Set(rows.map((r) => r.implementation).filter((p) => p !== ""));
2795
2855
  const findings = [];
@@ -2801,6 +2861,16 @@ function checkG2(rows, candidateFiles) {
2801
2861
  });
2802
2862
  }
2803
2863
  }
2864
+ for (const row of rows) {
2865
+ const carriesPlaceholder = [row.summary, row.description, row.spec].some((cell) => cell === SCAFFOLD_PLACEHOLDER);
2866
+ if (carriesPlaceholder) {
2867
+ findings.push({
2868
+ row: row.action,
2869
+ path: row.implementation,
2870
+ reason: `${row.ring} row "${row.action}" still carries the scaffold placeholder marker — the why is owed`
2871
+ });
2872
+ }
2873
+ }
2804
2874
  return { check: "G2", status: findings.length > 0 ? "info" : "pass", findings };
2805
2875
  }
2806
2876
  function checkG3(ring0Rows, crossingFiles) {
@@ -3320,6 +3390,29 @@ function isNewDiskStateFile(path, status) {
3320
3390
  return false;
3321
3391
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
3322
3392
  }
3393
+ // ../../packages/aeg-core/src/main-branch-refusal.ts
3394
+ function checkMainBranchRefusal(facts) {
3395
+ const { currentSymbolicBranch, defaultBranch } = facts;
3396
+ if (currentSymbolicBranch === null)
3397
+ return null;
3398
+ if (defaultBranch === null) {
3399
+ return {
3400
+ reason: "default-branch-undetermined",
3401
+ severity: "warning",
3402
+ currentBranch: currentSymbolicBranch,
3403
+ defaultBranch: null
3404
+ };
3405
+ }
3406
+ if (currentSymbolicBranch === defaultBranch) {
3407
+ return {
3408
+ reason: "on-default-branch",
3409
+ severity: "error",
3410
+ currentBranch: currentSymbolicBranch,
3411
+ defaultBranch
3412
+ };
3413
+ }
3414
+ return null;
3415
+ }
3323
3416
  // ../../packages/aeg-core/src/branch-topology-gate.ts
3324
3417
  function taskBranchTopologyFields(branch) {
3325
3418
  if (!/^task\/.*\/.*$/.test(branch))
@@ -3666,8 +3759,15 @@ function isSafeRepoRelPath(p) {
3666
3759
  var SafeRepoRelPath = z.string().refine(isSafeRepoRelPath, {
3667
3760
  message: "must be a repo-root-relative path with no `..` segment or absolute root"
3668
3761
  });
3762
+ var CANONICAL_HOOK_BLOCK_PREFIXES = [".git/", ".husky/", ".vinaya/hooks/", ".claude/hooks/"];
3763
+ function isCanonicalHookBlockPath(p) {
3764
+ return CANONICAL_HOOK_BLOCK_PREFIXES.some((prefix) => p.startsWith(prefix) && p.length > prefix.length);
3765
+ }
3766
+ var ManagedHookBlockPath = SafeRepoRelPath.refine(isCanonicalHookBlockPath, {
3767
+ message: `must start with one of ${CANONICAL_HOOK_BLOCK_PREFIXES.join(", ")} (byte-exact, case-sensitive)`
3768
+ });
3669
3769
  var ManagedBlockRecordSchema = z.object({
3670
- path: SafeRepoRelPath,
3770
+ path: ManagedHookBlockPath,
3671
3771
  marker: z.string(),
3672
3772
  comment: z.enum(["hash", "html"])
3673
3773
  });
@@ -2,19 +2,48 @@
2
2
 
3
3
  // src/checks/bin/check-evidence-fresh.ts
4
4
  import { execFileSync as execFileSync2 } from "node:child_process";
5
+
6
+ // src/checks/contract.ts
7
+ var CHECK_SCHEMA_VERSION = 1;
8
+ function emitCheckError(error) {
9
+ process.stderr.write(`${JSON.stringify(error)}
10
+ `);
11
+ }
12
+
13
+ // src/lib/numstat.ts
14
+ function summariseNumstat(numstat) {
15
+ const rows = numstat.split(`
16
+ `).filter((l) => l.trim() !== "");
17
+ let added = 0;
18
+ let deleted = 0;
19
+ let binary = 0;
20
+ for (const row of rows) {
21
+ const [a, d] = row.split("\t");
22
+ if (a === "-" || d === "-") {
23
+ binary++;
24
+ continue;
25
+ }
26
+ added += Number(a) || 0;
27
+ deleted += Number(d) || 0;
28
+ }
29
+ const files = `${rows.length} file${rows.length === 1 ? "" : "s"} changed`;
30
+ const bin = binary > 0 ? `, ${binary} binary` : "";
31
+ return `${files}, ${added} insertion${added === 1 ? "" : "s"}(+), ${deleted} deletion${deleted === 1 ? "" : "s"}(-)${bin}`;
32
+ }
33
+ var EVIDENCE_SUMMARY_PREFIX = "Summary: ";
5
34
  // ../../packages/aeg-core/src/gate-audience.ts
6
35
  var GATE_AUDIENCE = {
7
- "check-branch-topology": { shippedAs: "branch-topology" },
8
- "check-first-push-dispatch": { shippedAs: "first-push-dispatch" },
9
- "check-no-disk-state": { shippedAs: "no-disk-state" },
10
- "verify-brief": { shippedAs: "brief-shape" },
11
- "verify-coherence": { shippedAs: "coherence" },
12
- "verify-dispatch": { shippedAs: "dispatch-readiness" },
13
- "verify-docs": { shippedAs: ["doc-coverage", "doc-coverage-push"] },
14
- "verify-registry": { shippedAs: "registry-gates" },
15
- "verify-review-gate": { shippedAs: "review-gate" },
16
- "verify-single-plan-pr": { shippedAs: "single-plan-pr" },
17
- "verify-test-plan": { shippedAs: "test-plan" },
36
+ "check-branch-topology": { shippedAs: "branch-topology", ring: 0 },
37
+ "check-first-push-dispatch": { shippedAs: "first-push-dispatch", ring: 0 },
38
+ "check-no-disk-state": { shippedAs: "no-disk-state", ring: 0 },
39
+ "verify-brief": { shippedAs: "brief-shape", ring: 0 },
40
+ "verify-coherence": { shippedAs: "coherence", ring: 0 },
41
+ "verify-dispatch": { shippedAs: "dispatch-readiness", ring: 0 },
42
+ "verify-docs": { shippedAs: ["doc-coverage", "doc-coverage-push"], ring: 0 },
43
+ "verify-registry": { shippedAs: "registry-gates", ring: 0 },
44
+ "verify-review-gate": { shippedAs: "review-gate", ring: 1 },
45
+ "verify-single-plan-pr": { shippedAs: "single-plan-pr", ring: 0 },
46
+ "verify-test-plan": { shippedAs: "test-plan", ring: 1 },
18
47
  "check-direct-main-push": {
19
48
  internal: "Reaches adopters through `vinaya audit --only=direct-push`, not the check registry: it is a ring-2 sweep over merge history keyed on `GITHUB_SHA`, run by the generated archivist job on push to the default branch. It has no diff to key on, so there is nothing for `vinaya check` to run it against."
20
49
  },
@@ -326,6 +355,13 @@ var LABELS = [
326
355
  form: "literal",
327
356
  carries: "Execution is halted pending an external unblock; wins over every other derived status."
328
357
  },
358
+ {
359
+ key: "backlog",
360
+ id: "vinaya/backlog",
361
+ category: "state",
362
+ form: "literal",
363
+ carries: "The Issue is deliberately unplanned — belongs to no tranche and is excluded from dispatch, until a Planner promotes it."
364
+ },
329
365
  {
330
366
  key: "tier-0",
331
367
  id: "vinaya/tier:0",
@@ -1984,7 +2020,59 @@ function overrideActive(opts) {
1984
2020
  return false;
1985
2021
  }
1986
2022
  // ../../packages/aeg-core/src/premise-check.ts
2023
+ import { createHash } from "node:crypto";
1987
2024
  var ASSERTION_KINDS = new Set(["contains", "absent", "sha256"]);
2025
+ function isPremiseHeader(line) {
2026
+ const stripped = line.replace(/[*#]/g, "").trim();
2027
+ return /^premise\s*:?$/i.test(stripped);
2028
+ }
2029
+ var PREMISE_LINE = /^[-*]\s*(\S+)\s+(contains|absent|sha256)\s*:\s*(.+)$/i;
2030
+ function parsePremiseBlock(prBody) {
2031
+ const searchIn = anchoredRegion(prBody, "PREMISE") ?? prBody;
2032
+ const lines = searchIn.split(/\r?\n/);
2033
+ const assertions = [];
2034
+ let inBlock = false;
2035
+ for (const raw of lines) {
2036
+ const line = raw.trim();
2037
+ if (!inBlock) {
2038
+ if (isPremiseHeader(line))
2039
+ inBlock = true;
2040
+ continue;
2041
+ }
2042
+ if (line === "")
2043
+ break;
2044
+ const m = line.match(PREMISE_LINE);
2045
+ if (!m)
2046
+ break;
2047
+ const [, path, kindRaw, value] = m;
2048
+ const kind = kindRaw.toLowerCase();
2049
+ if (!ASSERTION_KINDS.has(kind))
2050
+ continue;
2051
+ assertions.push({ kind, path, value: value.trim() });
2052
+ }
2053
+ return assertions;
2054
+ }
2055
+ function checkPremises(assertions, fileReader) {
2056
+ const failures = [];
2057
+ for (const a of assertions) {
2058
+ const content = fileReader(a.path);
2059
+ if (content === null) {
2060
+ failures.push(`premise-check: ${a.path} does not exist on disk (premise asserted \`${a.kind}: ${a.value}\`) — the surface moved since this brief was authored; re-dig before proceeding.`);
2061
+ continue;
2062
+ }
2063
+ if (a.kind === "contains" && !content.includes(a.value)) {
2064
+ failures.push(`premise-check: ${a.path} no longer contains "${a.value}" — the premise this brief pinned has moved; re-dig before proceeding.`);
2065
+ } else if (a.kind === "absent" && content.includes(a.value)) {
2066
+ failures.push(`premise-check: ${a.path} now contains "${a.value}", but the brief pinned it absent — the premise has moved; re-dig before proceeding.`);
2067
+ } else if (a.kind === "sha256") {
2068
+ const actual = createHash("sha256").update(content).digest("hex");
2069
+ if (actual !== a.value.toLowerCase()) {
2070
+ failures.push(`premise-check: ${a.path} sha256 mismatch (brief pinned ${a.value}, file is now ${actual}) — the file changed since this brief was authored; re-dig before proceeding.`);
2071
+ }
2072
+ }
2073
+ }
2074
+ return { pass: failures.length === 0, failures };
2075
+ }
1988
2076
 
1989
2077
  // ../../packages/aeg-core/src/test-plan-section.ts
1990
2078
  var HEADING_FORM_RE = /^#{1,4}[ \t]*(?:\d+[a-z]?\.[ \t]*)?\*{0,2}Test Plan\*{0,2}[ \t]*$/im;
@@ -2788,6 +2876,7 @@ function checkG1(rows, existsFn) {
2788
2876
  }
2789
2877
  return { check: "G1", status: findings.length > 0 ? "fail" : "pass", findings };
2790
2878
  }
2879
+ var SCAFFOLD_PLACEHOLDER = "[undocumented — fill in why]";
2791
2880
  function checkG2(rows, candidateFiles) {
2792
2881
  const implementations = new Set(rows.map((r) => r.implementation).filter((p) => p !== ""));
2793
2882
  const findings = [];
@@ -2799,6 +2888,16 @@ function checkG2(rows, candidateFiles) {
2799
2888
  });
2800
2889
  }
2801
2890
  }
2891
+ for (const row of rows) {
2892
+ const carriesPlaceholder = [row.summary, row.description, row.spec].some((cell) => cell === SCAFFOLD_PLACEHOLDER);
2893
+ if (carriesPlaceholder) {
2894
+ findings.push({
2895
+ row: row.action,
2896
+ path: row.implementation,
2897
+ reason: `${row.ring} row "${row.action}" still carries the scaffold placeholder marker — the why is owed`
2898
+ });
2899
+ }
2900
+ }
2802
2901
  return { check: "G2", status: findings.length > 0 ? "info" : "pass", findings };
2803
2902
  }
2804
2903
  function checkG3(ring0Rows, crossingFiles) {
@@ -3318,6 +3417,29 @@ function isNewDiskStateFile(path, status) {
3318
3417
  return false;
3319
3418
  return ANY_DEPTH_TRANCHES_MD.test(path) || TOKENS_FILE.test(path);
3320
3419
  }
3420
+ // ../../packages/aeg-core/src/main-branch-refusal.ts
3421
+ function checkMainBranchRefusal(facts) {
3422
+ const { currentSymbolicBranch, defaultBranch } = facts;
3423
+ if (currentSymbolicBranch === null)
3424
+ return null;
3425
+ if (defaultBranch === null) {
3426
+ return {
3427
+ reason: "default-branch-undetermined",
3428
+ severity: "warning",
3429
+ currentBranch: currentSymbolicBranch,
3430
+ defaultBranch: null
3431
+ };
3432
+ }
3433
+ if (currentSymbolicBranch === defaultBranch) {
3434
+ return {
3435
+ reason: "on-default-branch",
3436
+ severity: "error",
3437
+ currentBranch: currentSymbolicBranch,
3438
+ defaultBranch
3439
+ };
3440
+ }
3441
+ return null;
3442
+ }
3321
3443
  // ../../packages/aeg-core/src/branch-topology-gate.ts
3322
3444
  function taskBranchTopologyFields(branch) {
3323
3445
  if (!/^task\/.*\/.*$/.test(branch))
@@ -3571,17 +3693,64 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
3571
3693
  }
3572
3694
  return findings;
3573
3695
  }
3574
- // src/checks/contract.ts
3575
- var CHECK_SCHEMA_VERSION = 1;
3576
- function emitCheckError(error) {
3577
- process.stderr.write(`${JSON.stringify(error)}
3578
- `);
3696
+ // src/checks/scan-context.ts
3697
+ var ZERO_WIDTH = /[\u200B-\u200D\u2060\uFEFF]/g;
3698
+ var HTML_ENTITIES = {
3699
+ """: '"',
3700
+ "'": "'",
3701
+ "&lt;": "<",
3702
+ "&gt;": ">",
3703
+ "&amp;": "&"
3704
+ };
3705
+ function decodeNamedEntities(body) {
3706
+ return body.replace(/&(?:quot|apos|lt|gt|amp);/g, (m) => HTML_ENTITIES[m] ?? m);
3707
+ }
3708
+ function normalizeBody(rawBody) {
3709
+ return decodeNamedEntities(rawBody.replace(ZERO_WIDTH, ""));
3710
+ }
3711
+ function buildAnchorLookupMask(body) {
3712
+ return maskDetailsBlocks(maskCode(body));
3713
+ }
3714
+
3715
+ class ScanContext {
3716
+ raw;
3717
+ normalised;
3718
+ masked;
3719
+ constructor(raw, normalised, masked) {
3720
+ this.raw = raw;
3721
+ this.normalised = normalised;
3722
+ this.masked = masked;
3723
+ }
3724
+ static from(rawBody) {
3725
+ const normalised = normalizeBody(rawBody);
3726
+ return new ScanContext(rawBody, normalised, buildAnchorLookupMask(normalised));
3727
+ }
3728
+ rawResolutionAgrees(field) {
3729
+ const rawHasPair = anchoredRegionBounds(this.raw, field) !== null;
3730
+ return rawHasPair === (anchoredRegionBounds(this.masked, field) !== null);
3731
+ }
3732
+ }
3733
+ function resolveAnchoredRegion(ctx, field) {
3734
+ const bounds = anchoredRegionBounds(ctx.masked, field);
3735
+ if (bounds === null) {
3736
+ return anchoredRegionBounds(ctx.normalised, field) !== null ? "hidden" : null;
3737
+ }
3738
+ return {
3739
+ region: ctx.normalised.slice(bounds.innerStart, bounds.innerEnd),
3740
+ maskedRegion: ctx.masked.slice(bounds.innerStart, bounds.innerEnd)
3741
+ };
3742
+ }
3743
+ function summaryLineIndex(maskedRegion) {
3744
+ const index = maskedRegion.split(`
3745
+ `).findIndex((line) => line.startsWith(EVIDENCE_SUMMARY_PREFIX));
3746
+ return index === -1 ? null : index;
3579
3747
  }
3580
3748
 
3581
3749
  // src/checks/evidence-fresh-logic.ts
3582
3750
  var HEAD_LINE = /^Head:\s*([0-9a-f]{7,40})\s*$/m;
3583
3751
  var FENCE = /```[^\n]*\n?([\s\S]*?)```/g;
3584
- function compareEvidenceBlock(region, resolvedHead, actualNumstat) {
3752
+ function compareEvidenceBlock(resolved, resolvedHead, actualNumstat) {
3753
+ const region = resolved.region;
3585
3754
  const headMatch = region.match(HEAD_LINE);
3586
3755
  const fences = [...region.matchAll(FENCE)].map((m) => (m[1] ?? "").trim());
3587
3756
  if (!headMatch || fences.length < 1) {
@@ -3607,6 +3776,20 @@ function compareEvidenceBlock(region, resolvedHead, actualNumstat) {
3607
3776
  ].join(`
3608
3777
  `));
3609
3778
  }
3779
+ const summaryIndex = summaryLineIndex(resolved.maskedRegion);
3780
+ if (summaryIndex !== null) {
3781
+ const storedSummary = resolved.region.split(`
3782
+ `)[summaryIndex];
3783
+ const expectedSummary = `${EVIDENCE_SUMMARY_PREFIX}\`${summariseNumstat(actual)}\``;
3784
+ if (storedSummary !== expectedSummary) {
3785
+ errors.push([
3786
+ "evidence-fresh: the Summary line does not match a fresh recompute of the diff it summarises.",
3787
+ ` block: ${JSON.stringify(storedSummary)}`,
3788
+ ` actual: ${JSON.stringify(expectedSummary)}`
3789
+ ].join(`
3790
+ `));
3791
+ }
3792
+ }
3610
3793
  return errors.length > 0 ? { status: "fail", errors } : { status: "pass" };
3611
3794
  }
3612
3795
 
@@ -3665,10 +3848,20 @@ function main() {
3665
3848
  if (!body) {
3666
3849
  process.exit(0);
3667
3850
  }
3668
- const region = anchoredRegion(body, "EVIDENCE");
3669
- if (region === null) {
3851
+ const resolved = resolveAnchoredRegion(ScanContext.from(body), "EVIDENCE");
3852
+ if (resolved === null) {
3670
3853
  process.exit(0);
3671
3854
  }
3855
+ if (resolved === "hidden") {
3856
+ emitCheckError({
3857
+ schema: CHECK_SCHEMA_VERSION,
3858
+ check: CHECK_NAME,
3859
+ severity: "error",
3860
+ message: "evidence-fresh: the AEG:EVIDENCE block sits inside a `<details>` block, where its digits are exempt from `body-bare-digits` and nothing can verify what it claims. Move the block out of the collapsed section.",
3861
+ agent_recovery_prompt: "Move the AEG:EVIDENCE anchor pair out of the `<details>` block (the canonical home is its own `## Evidence` section), then re-run `vinaya pr report --write <body-file>`."
3862
+ });
3863
+ process.exit(1);
3864
+ }
3672
3865
  const prNumberStr = process.env.PR_NUMBER;
3673
3866
  if (!prNumberStr) {
3674
3867
  process.exit(0);
@@ -3699,7 +3892,7 @@ function main() {
3699
3892
  });
3700
3893
  process.exit(1);
3701
3894
  }
3702
- const result = compareEvidenceBlock(region, resolvedHead, actualNumstat);
3895
+ const result = compareEvidenceBlock(resolved, resolvedHead, actualNumstat);
3703
3896
  if (result.status === "fail") {
3704
3897
  for (const message of result.errors) {
3705
3898
  emitCheckError({