@attalabs/vinaya 0.25.0 → 0.26.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.
Files changed (59) hide show
  1. package/README.md +4 -4
  2. package/aeg-root/contracts/architect-planner.md +5 -5
  3. package/aeg-root/contracts/developer-reviewer.md +7 -7
  4. package/aeg-root/contracts/planner-developer.md +142 -0
  5. package/aeg-root/contracts/reviewer-archivist.md +2 -2
  6. package/aeg-root/contracts/tranche-archivist-planner.md +12 -11
  7. package/aeg-root/enforcement.md +15 -5
  8. package/aeg-root/process.md +53 -75
  9. package/aeg-root/roles/archivist.md +3 -3
  10. package/aeg-root/roles/developer.md +10 -10
  11. package/aeg-root/roles/planner.md +88 -23
  12. package/aeg-root/roles/principal.md +11 -11
  13. package/aeg-root/roles/reviewer.md +10 -10
  14. package/aeg-root/roles/security.md +7 -7
  15. package/aeg-root/roles/tranche-archivist.md +2 -2
  16. package/aeg-root/skills/aeg/SKILL.md +5 -5
  17. package/aeg-root/skills/aeg-roles/SKILL.md +7 -7
  18. package/aeg-root/state-machine.md +35 -34
  19. package/aeg-root/task-model.md +3 -3
  20. package/aeg-root/templates/brief-template.md +2 -2
  21. package/aeg-root/templates/issue-rationale-template.md +3 -3
  22. package/aeg-root/tranche-model.md +21 -21
  23. package/dist/checks/bin/check-body-bare-digits.js +116 -19
  24. package/dist/checks/bin/check-branch-topology.js +116 -19
  25. package/dist/checks/bin/check-brief-shape.js +121 -1101
  26. package/dist/checks/bin/check-changeset-coverage.js +877 -26
  27. package/dist/checks/bin/check-closes-n.js +116 -19
  28. package/dist/checks/bin/check-coherence.js +116 -19
  29. package/dist/checks/bin/check-dead-branch-push.js +116 -19
  30. package/dist/checks/bin/check-dispatch-readiness.js +119 -22
  31. package/dist/checks/bin/check-doc-coverage-push.js +877 -26
  32. package/dist/checks/bin/check-doc-coverage.js +879 -28
  33. package/dist/checks/bin/check-doctrine-no-procedures.js +116 -19
  34. package/dist/checks/bin/check-doctrine-portability.js +877 -26
  35. package/dist/checks/bin/check-evidence-fresh.js +116 -19
  36. package/dist/checks/bin/check-exec-bits.js +877 -26
  37. package/dist/checks/bin/check-first-push-dispatch.js +116 -19
  38. package/dist/checks/bin/check-issue-assignment.js +116 -19
  39. package/dist/checks/bin/check-main-branch-refusal.js +117 -20
  40. package/dist/checks/bin/check-no-disk-state.js +116 -19
  41. package/dist/checks/bin/check-pr-premise-reassert.js +5401 -0
  42. package/dist/checks/bin/check-pr-report-density.js +116 -19
  43. package/dist/checks/bin/check-quoted-command.js +875 -24
  44. package/dist/checks/bin/check-reader-resolvable-prose.js +875 -24
  45. package/dist/checks/bin/check-registry-gates.js +147 -21
  46. package/dist/checks/bin/check-retired-vocabulary.js +875 -24
  47. package/dist/checks/bin/check-review-gate.js +116 -19
  48. package/dist/checks/bin/check-single-plan-pr.js +116 -19
  49. package/dist/checks/bin/check-surface-scope.js +5722 -0
  50. package/dist/checks/bin/check-test-plan.js +116 -19
  51. package/dist/checks/bin/check-token-collection-wired.js +116 -19
  52. package/dist/checks/bin/check-token-report.js +128 -26
  53. package/dist/checks/bin/check-workspace-escape.js +875 -24
  54. package/dist/index.js +1002 -214
  55. package/package.json +1 -1
  56. package/aeg-root/contracts/brief-developer.md +0 -141
  57. package/aeg-root/contracts/planner-brief.md +0 -143
  58. package/aeg-root/roles/brief-author.md +0 -116
  59. package/aeg-root/skills/brief-authoring/SKILL.md +0 -509
@@ -27,6 +27,15 @@ var GATE_AUDIENCE = {
27
27
  internal: "A composite that shells this repo's own `typecheck`/`lint`/`test`/`build` scripts by name. Those names are this monorepo's toolchain (bun + turbo + biome), not an adopter's; the shipped equivalent of its intent is `vinaya check --all`, which is portable by construction."
28
28
  }
29
29
  };
30
+ var NON_GATE_BINS = [
31
+ "archive-task",
32
+ "assign-task-issue",
33
+ "dead-branch-audit",
34
+ "eval-agent-compliance",
35
+ "open-issue",
36
+ "open-pr",
37
+ "report-tokens"
38
+ ];
30
39
  function isShipped(a) {
31
40
  return "shippedAs" in a;
32
41
  }
@@ -387,7 +396,7 @@ var LABELS = [
387
396
  id: "vinaya/needs:brief-correction",
388
397
  category: "needs",
389
398
  form: "literal",
390
- carries: "Waiting on the Brief Author — the brief contradicts the surface it describes."
399
+ carries: "Waiting on the Planner — the brief contradicts the surface it describes."
391
400
  },
392
401
  {
393
402
  key: "waiver-docs",
@@ -2394,7 +2403,7 @@ function objectivesOf(body) {
2394
2403
  continue;
2395
2404
  }
2396
2405
  if (hasBacktickedPath(text) && wordCount(stripObjectiveBackticks(text)) < MIN_WORDS_OUTSIDE_BACKTICKS) {
2397
- errors.push(`O${n} is little more than a file path — an objective states an observable outcome, never a bare path (the Brief Author maps it to files).`);
2406
+ errors.push(`O${n} is little more than a file path — an objective states an observable outcome, never a bare path (the Planner maps it to files).`);
2398
2407
  continue;
2399
2408
  }
2400
2409
  objectives.push({ id: `O${n}`, text });
@@ -2606,18 +2615,14 @@ function checkTestPlanExclusivity(prBody) {
2606
2615
  function checkPrincipalPlaceholder(prBody) {
2607
2616
  const region = testPlanRegion(prBody);
2608
2617
  const lineRe = /^-\s*\[[ xX]\]\s*\*{2}\[principal\]\*{2}(.*)$/gim;
2618
+ const errors = [];
2609
2619
  for (const m of region.matchAll(lineRe)) {
2610
2620
  const content = m[1] ?? "";
2611
2621
  if (/^\s*None\b/i.test(content)) {
2612
- return {
2613
- status: "fail",
2614
- errors: [
2615
- 'brief-validation Test Plan shape: a `**[principal]**` checkbox item is a "None" placeholder — if there is no principal-runnable surface, omit the item entirely; an untickable placeholder box blocks the merge gate forever.'
2616
- ]
2617
- };
2622
+ errors.push(`brief-validation Test Plan shape: a \`**[principal]**\` checkbox item is a "None" placeholder ("${content.trim()}") — if there is no principal-runnable surface, omit the item entirely; an untickable placeholder box blocks the merge gate forever.`);
2618
2623
  }
2619
2624
  }
2620
- return { status: "pass", errors: [] };
2625
+ return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
2621
2626
  }
2622
2627
  function checkSurfaceMap(prBody) {
2623
2628
  return headingCheck(prBody, "(?:technical\\s+)?surface map", "Technical surface map");
@@ -2933,6 +2938,18 @@ function checkBriefSections(prBody, readTier, options = {}) {
2933
2938
  ];
2934
2939
  return { errors: results.flatMap((r) => r.errors) };
2935
2940
  }
2941
+ var AEG_BRIEF_V1_MARKER = "<!-- aeg:brief:v1 -->";
2942
+ function contentAfterTwoLines(body) {
2943
+ const first = body.indexOf(`
2944
+ `);
2945
+ if (first === -1)
2946
+ return "";
2947
+ const second = body.indexOf(`
2948
+ `, first + 1);
2949
+ if (second === -1)
2950
+ return "";
2951
+ return body.slice(second + 1);
2952
+ }
2936
2953
  // ../../packages/aeg-core/src/doctrine-portability.ts
2937
2954
  var DEFAULT_SHIPS_PREFIX = "aeg-root/";
2938
2955
  var STATIC_PORTABLE_PREFIXES = [
@@ -3175,6 +3192,66 @@ function checkIssueObjectives(body, issueNumber) {
3175
3192
  return { status: "pass", errors: [] };
3176
3193
  return { status: "fail", errors: result.errors.map((e) => `issue-validation objectives: ${e}`) };
3177
3194
  }
3195
+ function topLevelSectionText(body, headingName) {
3196
+ const headingRe = new RegExp(`^##[ \\t]*${headingName}[ \\t]*$`, "im");
3197
+ const heading = headingRe.exec(body);
3198
+ if (!heading)
3199
+ return null;
3200
+ const afterHeading = body.slice(heading.index + heading[0].length);
3201
+ const next = /^##[ \t]/m.exec(afterHeading);
3202
+ return next ? afterHeading.slice(0, next.index) : afterHeading;
3203
+ }
3204
+ function looksLikeFilePath(entry2) {
3205
+ const stripped = entry2.replace(/\/\*\*?$/, "");
3206
+ const lastSegment = stripped.split("/").pop() ?? stripped;
3207
+ const extMatch = /\.[A-Za-z0-9]{1,6}$/.exec(lastSegment);
3208
+ return extMatch !== null && extMatch.index > 0;
3209
+ }
3210
+ function splitGlobList(raw) {
3211
+ return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
3212
+ }
3213
+ function parseIssueSurface(body) {
3214
+ const section = topLevelSectionText(body, "Surface");
3215
+ if (section === null)
3216
+ return { ok: false, errors: ["no `## Surface` heading found in the body."] };
3217
+ const inLine = /^in:\s*(.+)$/im.exec(section);
3218
+ const outLine = /^out:\s*(.+)$/im.exec(section);
3219
+ const errors = [];
3220
+ if (!inLine)
3221
+ errors.push("`## Surface` has no `in:` line.");
3222
+ if (!outLine)
3223
+ errors.push("`## Surface` has no `out:` line.");
3224
+ if (errors.length > 0)
3225
+ return { ok: false, errors };
3226
+ const inGlobs = splitGlobList(inLine[1]);
3227
+ const outGlobs = splitGlobList(outLine[1]);
3228
+ for (const g of [...inGlobs, ...outGlobs]) {
3229
+ if (looksLikeFilePath(g)) {
3230
+ errors.push(`\`${g}\` in \`## Surface\` looks like a file path — Surface entries are directory-level globs, never file paths.`);
3231
+ }
3232
+ }
3233
+ if (inGlobs.length === 0)
3234
+ errors.push("`## Surface`'s `in:` line resolved to zero globs.");
3235
+ if (errors.length > 0)
3236
+ return { ok: false, errors };
3237
+ return { ok: true, value: { in: inGlobs, out: outGlobs } };
3238
+ }
3239
+ function globCoversPath(glob, path) {
3240
+ const g = glob.replace(/\/\*\*?$/, "").replace(/\/+$/, "");
3241
+ const p = path.replace(/\/+$/, "");
3242
+ return g === p || g.startsWith(`${p}/`) || p.startsWith(`${g}/`);
3243
+ }
3244
+ function checkSurfaceScope(changedFiles, outGlobs) {
3245
+ if (outGlobs.length === 0)
3246
+ return { ok: true };
3247
+ const violations = [];
3248
+ for (const file of changedFiles) {
3249
+ const glob = outGlobs.find((g) => globCoversPath(g, file));
3250
+ if (glob)
3251
+ violations.push({ file, glob });
3252
+ }
3253
+ return violations.length > 0 ? { ok: false, violations } : { ok: true };
3254
+ }
3178
3255
  function isTaskIssueLabelSet(labels) {
3179
3256
  return hasLabel("tranche", labels);
3180
3257
  }
@@ -3239,6 +3316,8 @@ function checkProjectsRegistered(body, _labels, registeredNames) {
3239
3316
  }
3240
3317
  return errors.length > 0 ? { status: "fail", errors } : { status: "pass", errors: [] };
3241
3318
  }
3319
+ var DOC_PATH_RE = /(?:(?:aeg-root|apps|packages|specs|docs|tools|\.claude|\.github)\/[\w./@-]*\.(?:md|mdx)|\.claude\/(?:skills|rules)\/[\w./-]+|\b(?:docs-index|decisions|projects|state-machine|enforcement|process|README|CLAUDE)\.md\b|\b[\w-]+-(?:spec|decisions|backlog)\.md\b)/i;
3320
+ var DOC_PATH_RE_GLOBAL = new RegExp(DOC_PATH_RE.source, "gi");
3242
3321
 
3243
3322
  // ../../packages/aeg-core/src/coherence-checks.ts
3244
3323
  var COHERENCE_ENFORCED_FROM = "2026-07-01";
@@ -3768,16 +3847,33 @@ function checkG1(rows, existsFn) {
3768
3847
  return { check: "G1", status: findings.length > 0 ? "fail" : "pass", findings };
3769
3848
  }
3770
3849
  var SCAFFOLD_PLACEHOLDER = "[undocumented — fill in why]";
3850
+ var NON_GATE_BIN_NAMES = NON_GATE_BINS;
3851
+ var TWIN_CANDIDATE_EXCEPTIONS = {
3852
+ "apps/cli/src/checks/bin/check-dead-branch-push.ts": 'Ring 0 "Pushing to a branch whose pull request already resolved" (implementation: check-push-target.ts)',
3853
+ "apps/cli/src/checks/bin/check-issue-assignment.ts": `Ring 0 "A task branch's first push (Issue self-assignment)" (implementation: assign-task-issue.ts)`,
3854
+ "apps/cli/src/checks/bin/check-closes-n.ts": 'Ring 1 "Closes linkage" (implementation: verify-coherence.ts)'
3855
+ };
3856
+ var NON_GATE_HOOK_SCRIPTS = [".claude/hooks/track-transcript.sh"];
3771
3857
  function checkG2(rows, candidateFiles) {
3772
3858
  const implementations = new Set(rows.map((r) => r.implementation).filter((p) => p !== ""));
3859
+ const claimedNames = new Set(rows.flatMap((r) => claimedCheckNames(r.implementation)));
3773
3860
  const findings = [];
3774
3861
  for (const path of candidateFiles) {
3775
- if (!implementations.has(path)) {
3776
- findings.push({
3777
- path,
3778
- reason: `"${path}" is not named as the implementation of any row in enforcement.md's ring tables`
3779
- });
3780
- }
3862
+ if (implementations.has(path))
3863
+ continue;
3864
+ if (path.startsWith(AEG_CORE_BIN_PATH_PREFIX) && NON_GATE_BIN_NAMES.includes(basenameNoExt(path)))
3865
+ continue;
3866
+ if (NON_GATE_HOOK_SCRIPTS.includes(path))
3867
+ continue;
3868
+ if (path in TWIN_CANDIDATE_EXCEPTIONS)
3869
+ continue;
3870
+ const candidateNames = claimedCheckNames(path);
3871
+ if (candidateNames.length > 0 && candidateNames.some((n) => claimedNames.has(n)))
3872
+ continue;
3873
+ findings.push({
3874
+ path,
3875
+ reason: `"${path}" is not named as the implementation of any row in enforcement.md's ring tables`
3876
+ });
3781
3877
  }
3782
3878
  for (const row of rows) {
3783
3879
  const carriesPlaceholder = [row.summary, row.description, row.spec].some((cell) => cell === SCAFFOLD_PLACEHOLDER);
@@ -3789,7 +3885,7 @@ function checkG2(rows, candidateFiles) {
3789
3885
  });
3790
3886
  }
3791
3887
  }
3792
- return { check: "G2", status: findings.length > 0 ? "info" : "pass", findings };
3888
+ return { check: "G2", status: findings.length > 0 ? "fail" : "pass", findings };
3793
3889
  }
3794
3890
  function checkG3(ring0Rows, crossingFiles) {
3795
3891
  const ring0Implementations = new Set(ring0Rows.map((r) => r.implementation));
@@ -4665,7 +4761,6 @@ function findWorkspaceEscapes(files, knownPaths, workspaceDirs = DEFAULT_WORKSPA
4665
4761
  import { z } from "zod";
4666
4762
  var ROLE_VALUES = [
4667
4763
  "planner",
4668
- "brief-author",
4669
4764
  "developer",
4670
4765
  "code-reviewer",
4671
4766
  "security",
@@ -4739,18 +4834,20 @@ var dispatchShared = {
4739
4834
  round: z.number().int().optional(),
4740
4835
  effect_id: z.string()
4741
4836
  };
4837
+ var dispatchUsageField = z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable();
4742
4838
  var DispatchEventSchema = z.discriminatedUnion("event", [
4743
4839
  z.object({ ...dispatchShared, event: z.literal("dispatched"), prompt_hash: z.string() }).strict(),
4744
4840
  z.object({
4745
4841
  ...dispatchShared,
4746
4842
  event: z.literal("outcome_received"),
4747
4843
  outcome: DispatchOutcomeSchema,
4748
- usage: z.object({ input: z.number().nonnegative(), output: z.number().nonnegative() }).strict().nullable()
4844
+ usage: dispatchUsageField
4749
4845
  }).strict(),
4750
4846
  z.object({
4751
4847
  ...dispatchShared,
4752
4848
  event: z.literal("dispatch_failed"),
4753
- reason: z.enum(["timeout", "crash", "refused", "unattributed_write"])
4849
+ reason: z.enum(["timeout", "crash", "refused", "unattributed_write"]),
4850
+ usage: dispatchUsageField
4754
4851
  }).strict()
4755
4852
  ]);
4756
4853
  var loopShared = {