@attalabs/vinaya 0.17.1 → 0.19.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 (87) hide show
  1. package/README.md +37 -1
  2. package/aeg-root/contracts/architect-planner.md +72 -0
  3. package/aeg-root/roles/architect.md +91 -0
  4. package/aeg-root/roles/planner.md +14 -15
  5. package/aeg-root/tranche-model.md +9 -1
  6. package/dist/checks/bin/check-body-bare-digits.js +62 -14
  7. package/dist/checks/bin/check-branch-topology.js +57 -12
  8. package/dist/checks/bin/check-brief-shape.js +57 -12
  9. package/dist/checks/bin/check-closes-n.js +57 -12
  10. package/dist/checks/bin/check-coherence.js +62 -14
  11. package/dist/checks/bin/check-dead-branch-push.js +57 -12
  12. package/dist/checks/bin/check-dispatch-readiness.js +129 -41
  13. package/dist/checks/bin/check-doc-coverage-push.js +62 -14
  14. package/dist/checks/bin/check-doc-coverage.js +62 -14
  15. package/dist/checks/bin/check-evidence-fresh.js +57 -12
  16. package/dist/checks/bin/check-first-push-dispatch.js +130 -42
  17. package/dist/checks/bin/check-issue-assignment.js +57 -12
  18. package/dist/checks/bin/check-no-disk-state.js +57 -12
  19. package/dist/checks/bin/check-reader-resolvable-prose.js +57 -12
  20. package/dist/checks/bin/check-registry-gates.js +57 -12
  21. package/dist/checks/bin/check-review-gate.js +62 -14
  22. package/dist/checks/bin/check-single-plan-pr.js +57 -12
  23. package/dist/checks/bin/check-test-plan.js +57 -12
  24. package/dist/index.js +889 -109
  25. package/package.json +1 -1
  26. package/studio-standalone/apps/vinaya-studio/web/.next/BUILD_ID +1 -1
  27. package/studio-standalone/apps/vinaya-studio/web/.next/build-manifest.json +3 -3
  28. package/studio-standalone/apps/vinaya-studio/web/.next/prerender-manifest.json +3 -3
  29. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.html +1 -1
  30. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.rsc +1 -1
  31. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  32. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  33. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  34. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page/server-reference-manifest.json +2 -2
  35. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  36. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  37. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/api/coherence/route.js.nft.json +1 -1
  38. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page/server-reference-manifest.json +2 -2
  39. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page.js.nft.json +1 -1
  40. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/page_client-reference-manifest.js +1 -1
  41. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page/server-reference-manifest.json +2 -2
  42. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page.js.nft.json +1 -1
  43. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/backlog/page_client-reference-manifest.js +1 -1
  44. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page/server-reference-manifest.json +2 -2
  45. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page.js.nft.json +1 -1
  46. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/page_client-reference-manifest.js +1 -1
  47. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page/server-reference-manifest.json +2 -2
  48. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page.js.nft.json +1 -1
  49. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/page_client-reference-manifest.js +1 -1
  50. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page/server-reference-manifest.json +2 -2
  51. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page.js.nft.json +1 -1
  52. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/page_client-reference-manifest.js +1 -1
  53. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page/server-reference-manifest.json +2 -2
  54. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page.js.nft.json +1 -1
  55. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/[name]/tranches/[slug]/tasks/[taskId]/page_client-reference-manifest.js +1 -1
  56. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page/server-reference-manifest.json +2 -2
  57. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page.js.nft.json +1 -1
  58. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/projects/page_client-reference-manifest.js +1 -1
  59. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page/server-reference-manifest.json +2 -2
  60. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page.js.nft.json +1 -1
  61. package/studio-standalone/apps/vinaya-studio/web/.next/server/app/studio/tranches/page_client-reference-manifest.js +1 -1
  62. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/[root-of-the-server]__0e95dv5._.js +1 -1
  63. 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
  64. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__029-1ch._.js +1 -1
  65. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__0q56ohl._.js +1 -1
  66. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/{[root-of-the-server]__1ccgndq._.js → [root-of-the-server]__0ulzsom._.js} +1 -1
  67. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1_ojzp1._.js +1 -1
  68. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1d_8nm4._.js +1 -1
  69. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__1zkwtx8._.js +1 -1
  70. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/[root-of-the-server]__210ehg9._.js +1 -1
  71. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_00yjcv1._.js +1 -1
  72. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_0rix02i._.js +1 -1
  73. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_18-d2be._.js +1 -1
  74. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1fqw88f._.js +5 -5
  75. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/_1gw6vc9._.js +1 -1
  76. package/studio-standalone/apps/vinaya-studio/web/.next/server/chunks/ssr/node_modules_1vo08dj._.js +2 -2
  77. package/studio-standalone/apps/vinaya-studio/web/.next/server/middleware-build-manifest.js +3 -3
  78. package/studio-standalone/apps/vinaya-studio/web/.next/server/pages/500.html +1 -1
  79. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.js +1 -1
  80. package/studio-standalone/apps/vinaya-studio/web/.next/server/server-reference-manifest.json +3 -3
  81. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{40x7f5hm7a5tk.js → 2r36w820aqwb9.js} +1 -1
  82. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0ojfbn3yvy-8_.js → 2xoclb4g7d9n7.js} +1 -1
  83. package/studio-standalone/apps/vinaya-studio/web/.next/static/chunks/{0l_vubs1ephe5.js → 3ocvtlhgckh81.js} +1 -1
  84. package/studio-standalone/apps/vinaya-studio/web/package.json +2 -2
  85. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{p5sWGp4ANwORiaX4Kk6U2 → RISz5nirLr1mUfMqzSaFS}/_buildManifest.js +0 -0
  86. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{p5sWGp4ANwORiaX4Kk6U2 → RISz5nirLr1mUfMqzSaFS}/_clientMiddlewareManifest.js +0 -0
  87. /package/studio-standalone/apps/vinaya-studio/web/.next/static/{p5sWGp4ANwORiaX4Kk6U2 → RISz5nirLr1mUfMqzSaFS}/_ssgManifest.js +0 -0
@@ -274,6 +274,9 @@ function issueListByLabelArgs(owner, repo, label) {
274
274
  "200"
275
275
  ];
276
276
  }
277
+ function ghIssueListByLabel(owner, repo, label) {
278
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
279
+ }
277
280
  async function ghIssueListByLabelAsync(owner, repo, label) {
278
281
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
279
282
  }
@@ -289,18 +292,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
289
292
  return dedupeByNumber(perLabel.flat());
290
293
  }
291
294
 
292
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
293
- function findMilestoneForSlug(owner, repo, slug) {
294
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
295
- const match = milestones.find((m) => m.title === slug);
296
- if (!match)
297
- return null;
298
- return {
299
- goal: match.description ?? "",
300
- lifecycle: match.state === "closed" ? "complete" : "active"
301
- };
302
- }
303
-
304
295
  // ../../packages/aeg-forge-state/src/labels.ts
305
296
  var LABELS = [
306
297
  {
@@ -543,6 +534,60 @@ function resolveTaskIssueRef(title, labels) {
543
534
  return { trancheSlug, taskId };
544
535
  }
545
536
 
537
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
538
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
539
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
540
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
541
+ function intentGoalForSlug(description, slug) {
542
+ const text = stripCode(description, { inlineSpans: "keep" });
543
+ const start = text.match(INTENTS_HEADING);
544
+ if (!start || start.index === undefined)
545
+ return "";
546
+ const rest = text.slice(start.index + start[0].length);
547
+ const next = rest.match(NEXT_HEADING);
548
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
549
+ for (const line of section.split(`
550
+ `)) {
551
+ const trimmed = line.trim();
552
+ if (trimmed.length === 0)
553
+ continue;
554
+ const m = trimmed.match(INTENT_BULLET);
555
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
556
+ return (m[2] ?? "").trim();
557
+ }
558
+ return "";
559
+ }
560
+ function matchesLegacyMilestone(milestones, slug) {
561
+ return milestones.find((m) => m.title === slug) ?? null;
562
+ }
563
+ function factsFromLegacyMilestone(milestone) {
564
+ return {
565
+ goal: milestone.description ?? "",
566
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
567
+ };
568
+ }
569
+ function lifecycleFromIssues(issues) {
570
+ if (issues.length === 0)
571
+ return "planned";
572
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
573
+ }
574
+ function goalFromMilestones(milestones, slug) {
575
+ for (const m of milestones) {
576
+ const goal = intentGoalForSlug(m.description ?? "", slug);
577
+ if (goal)
578
+ return goal;
579
+ }
580
+ return "";
581
+ }
582
+ function findMilestoneForSlug(owner, repo, slug) {
583
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
584
+ const legacy = matchesLegacyMilestone(milestones, slug);
585
+ if (legacy)
586
+ return factsFromLegacyMilestone(legacy);
587
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
588
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
589
+ }
590
+
546
591
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
547
592
  function trancheFromIssues(slug, issues, known) {
548
593
  return {
@@ -3196,7 +3241,8 @@ var BRIEF_BUILTINS = [
3196
3241
  "for",
3197
3242
  "closesN",
3198
3243
  "premiseCoverage",
3199
- "issueRationale"
3244
+ "issueRationale",
3245
+ "milestoneShape"
3200
3246
  ];
3201
3247
  var BriefSectionSchema = z.union([
3202
3248
  z.object({ builtin: z.enum(BRIEF_BUILTINS) }),
@@ -3206,7 +3252,9 @@ var BriefSectionSchema = z.union([
3206
3252
  ]);
3207
3253
  var BriefSchemaSchema = z.object({
3208
3254
  pr: z.object({ sections: z.array(BriefSectionSchema) }).optional(),
3209
- issue: z.object({ sections: z.array(BriefSectionSchema) }).optional()
3255
+ issue: z.object({ sections: z.array(BriefSectionSchema) }).optional(),
3256
+ milestone: z.object({ sections: z.array(BriefSectionSchema) }).optional(),
3257
+ ack: z.array(z.enum(BRIEF_BUILTINS)).optional()
3210
3258
  });
3211
3259
  function isSafeRepoRelPath(p) {
3212
3260
  if (p.length === 0)
@@ -274,6 +274,9 @@ function issueListByLabelArgs(owner, repo, label) {
274
274
  "200"
275
275
  ];
276
276
  }
277
+ function ghIssueListByLabel(owner, repo, label) {
278
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
279
+ }
277
280
  async function ghIssueListByLabelAsync(owner, repo, label) {
278
281
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
279
282
  }
@@ -289,18 +292,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
289
292
  return dedupeByNumber(perLabel.flat());
290
293
  }
291
294
 
292
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
293
- function findMilestoneForSlug(owner, repo, slug) {
294
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
295
- const match = milestones.find((m) => m.title === slug);
296
- if (!match)
297
- return null;
298
- return {
299
- goal: match.description ?? "",
300
- lifecycle: match.state === "closed" ? "complete" : "active"
301
- };
302
- }
303
-
304
295
  // ../../packages/aeg-forge-state/src/labels.ts
305
296
  var LABELS = [
306
297
  {
@@ -543,6 +534,60 @@ function resolveTaskIssueRef(title, labels) {
543
534
  return { trancheSlug, taskId };
544
535
  }
545
536
 
537
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
538
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
539
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
540
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
541
+ function intentGoalForSlug(description, slug) {
542
+ const text = stripCode(description, { inlineSpans: "keep" });
543
+ const start = text.match(INTENTS_HEADING);
544
+ if (!start || start.index === undefined)
545
+ return "";
546
+ const rest = text.slice(start.index + start[0].length);
547
+ const next = rest.match(NEXT_HEADING);
548
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
549
+ for (const line of section.split(`
550
+ `)) {
551
+ const trimmed = line.trim();
552
+ if (trimmed.length === 0)
553
+ continue;
554
+ const m = trimmed.match(INTENT_BULLET);
555
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
556
+ return (m[2] ?? "").trim();
557
+ }
558
+ return "";
559
+ }
560
+ function matchesLegacyMilestone(milestones, slug) {
561
+ return milestones.find((m) => m.title === slug) ?? null;
562
+ }
563
+ function factsFromLegacyMilestone(milestone) {
564
+ return {
565
+ goal: milestone.description ?? "",
566
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
567
+ };
568
+ }
569
+ function lifecycleFromIssues(issues) {
570
+ if (issues.length === 0)
571
+ return "planned";
572
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
573
+ }
574
+ function goalFromMilestones(milestones, slug) {
575
+ for (const m of milestones) {
576
+ const goal = intentGoalForSlug(m.description ?? "", slug);
577
+ if (goal)
578
+ return goal;
579
+ }
580
+ return "";
581
+ }
582
+ function findMilestoneForSlug(owner, repo, slug) {
583
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
584
+ const legacy = matchesLegacyMilestone(milestones, slug);
585
+ if (legacy)
586
+ return factsFromLegacyMilestone(legacy);
587
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
588
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
589
+ }
590
+
546
591
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
547
592
  function trancheFromIssues(slug, issues, known) {
548
593
  return {
@@ -3196,7 +3241,8 @@ var BRIEF_BUILTINS = [
3196
3241
  "for",
3197
3242
  "closesN",
3198
3243
  "premiseCoverage",
3199
- "issueRationale"
3244
+ "issueRationale",
3245
+ "milestoneShape"
3200
3246
  ];
3201
3247
  var BriefSectionSchema = z.union([
3202
3248
  z.object({ builtin: z.enum(BRIEF_BUILTINS) }),
@@ -3206,7 +3252,9 @@ var BriefSectionSchema = z.union([
3206
3252
  ]);
3207
3253
  var BriefSchemaSchema = z.object({
3208
3254
  pr: z.object({ sections: z.array(BriefSectionSchema) }).optional(),
3209
- issue: z.object({ sections: z.array(BriefSectionSchema) }).optional()
3255
+ issue: z.object({ sections: z.array(BriefSectionSchema) }).optional(),
3256
+ milestone: z.object({ sections: z.array(BriefSectionSchema) }).optional(),
3257
+ ack: z.array(z.enum(BRIEF_BUILTINS)).optional()
3210
3258
  });
3211
3259
  function isSafeRepoRelPath(p) {
3212
3260
  if (p.length === 0)
@@ -273,6 +273,9 @@ function issueListByLabelArgs(owner, repo, label) {
273
273
  "200"
274
274
  ];
275
275
  }
276
+ function ghIssueListByLabel(owner, repo, label) {
277
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
278
+ }
276
279
  async function ghIssueListByLabelAsync(owner, repo, label) {
277
280
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
278
281
  }
@@ -288,18 +291,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
288
291
  return dedupeByNumber(perLabel.flat());
289
292
  }
290
293
 
291
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
292
- function findMilestoneForSlug(owner, repo, slug) {
293
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
294
- const match = milestones.find((m) => m.title === slug);
295
- if (!match)
296
- return null;
297
- return {
298
- goal: match.description ?? "",
299
- lifecycle: match.state === "closed" ? "complete" : "active"
300
- };
301
- }
302
-
303
294
  // ../../packages/aeg-forge-state/src/labels.ts
304
295
  var LABELS = [
305
296
  {
@@ -542,6 +533,60 @@ function resolveTaskIssueRef(title, labels) {
542
533
  return { trancheSlug, taskId };
543
534
  }
544
535
 
536
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
537
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
538
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
539
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
540
+ function intentGoalForSlug(description, slug) {
541
+ const text = stripCode(description, { inlineSpans: "keep" });
542
+ const start = text.match(INTENTS_HEADING);
543
+ if (!start || start.index === undefined)
544
+ return "";
545
+ const rest = text.slice(start.index + start[0].length);
546
+ const next = rest.match(NEXT_HEADING);
547
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
548
+ for (const line of section.split(`
549
+ `)) {
550
+ const trimmed = line.trim();
551
+ if (trimmed.length === 0)
552
+ continue;
553
+ const m = trimmed.match(INTENT_BULLET);
554
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
555
+ return (m[2] ?? "").trim();
556
+ }
557
+ return "";
558
+ }
559
+ function matchesLegacyMilestone(milestones, slug) {
560
+ return milestones.find((m) => m.title === slug) ?? null;
561
+ }
562
+ function factsFromLegacyMilestone(milestone) {
563
+ return {
564
+ goal: milestone.description ?? "",
565
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
566
+ };
567
+ }
568
+ function lifecycleFromIssues(issues) {
569
+ if (issues.length === 0)
570
+ return "planned";
571
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
572
+ }
573
+ function goalFromMilestones(milestones, slug) {
574
+ for (const m of milestones) {
575
+ const goal = intentGoalForSlug(m.description ?? "", slug);
576
+ if (goal)
577
+ return goal;
578
+ }
579
+ return "";
580
+ }
581
+ function findMilestoneForSlug(owner, repo, slug) {
582
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
583
+ const legacy = matchesLegacyMilestone(milestones, slug);
584
+ if (legacy)
585
+ return factsFromLegacyMilestone(legacy);
586
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
587
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
588
+ }
589
+
545
590
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
546
591
  function trancheFromIssues(slug, issues, known) {
547
592
  return {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/checks/bin/check-first-push-dispatch.ts
4
- import { execFileSync as execFileSync3 } from "node:child_process";
4
+ import { execFileSync as execFileSync4 } from "node:child_process";
5
5
  // ../../packages/aeg-forge-state/src/strip-code.ts
6
6
  function maskCode(body) {
7
7
  const fill = (line) => " ".repeat(line.length);
@@ -273,6 +273,9 @@ function issueListByLabelArgs(owner, repo, label) {
273
273
  "200"
274
274
  ];
275
275
  }
276
+ function ghIssueListByLabel(owner, repo, label) {
277
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
278
+ }
276
279
  async function ghIssueListByLabelAsync(owner, repo, label) {
277
280
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
278
281
  }
@@ -288,18 +291,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
288
291
  return dedupeByNumber(perLabel.flat());
289
292
  }
290
293
 
291
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
292
- function findMilestoneForSlug(owner, repo, slug) {
293
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
294
- const match = milestones.find((m) => m.title === slug);
295
- if (!match)
296
- return null;
297
- return {
298
- goal: match.description ?? "",
299
- lifecycle: match.state === "closed" ? "complete" : "active"
300
- };
301
- }
302
-
303
294
  // ../../packages/aeg-forge-state/src/labels.ts
304
295
  var LABELS = [
305
296
  {
@@ -542,6 +533,60 @@ function resolveTaskIssueRef(title, labels) {
542
533
  return { trancheSlug, taskId };
543
534
  }
544
535
 
536
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
537
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
538
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
539
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
540
+ function intentGoalForSlug(description, slug) {
541
+ const text = stripCode(description, { inlineSpans: "keep" });
542
+ const start = text.match(INTENTS_HEADING);
543
+ if (!start || start.index === undefined)
544
+ return "";
545
+ const rest = text.slice(start.index + start[0].length);
546
+ const next = rest.match(NEXT_HEADING);
547
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
548
+ for (const line of section.split(`
549
+ `)) {
550
+ const trimmed = line.trim();
551
+ if (trimmed.length === 0)
552
+ continue;
553
+ const m = trimmed.match(INTENT_BULLET);
554
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
555
+ return (m[2] ?? "").trim();
556
+ }
557
+ return "";
558
+ }
559
+ function matchesLegacyMilestone(milestones, slug) {
560
+ return milestones.find((m) => m.title === slug) ?? null;
561
+ }
562
+ function factsFromLegacyMilestone(milestone) {
563
+ return {
564
+ goal: milestone.description ?? "",
565
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
566
+ };
567
+ }
568
+ function lifecycleFromIssues(issues) {
569
+ if (issues.length === 0)
570
+ return "planned";
571
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
572
+ }
573
+ function goalFromMilestones(milestones, slug) {
574
+ for (const m of milestones) {
575
+ const goal = intentGoalForSlug(m.description ?? "", slug);
576
+ if (goal)
577
+ return goal;
578
+ }
579
+ return "";
580
+ }
581
+ function findMilestoneForSlug(owner, repo, slug) {
582
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
583
+ const legacy = matchesLegacyMilestone(milestones, slug);
584
+ if (legacy)
585
+ return factsFromLegacyMilestone(legacy);
586
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
587
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
588
+ }
589
+
545
590
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
546
591
  function trancheFromIssues(slug, issues, known) {
547
592
  return {
@@ -3194,7 +3239,8 @@ var BRIEF_BUILTINS = [
3194
3239
  "for",
3195
3240
  "closesN",
3196
3241
  "premiseCoverage",
3197
- "issueRationale"
3242
+ "issueRationale",
3243
+ "milestoneShape"
3198
3244
  ];
3199
3245
  var BriefSectionSchema = z2.union([
3200
3246
  z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
@@ -3204,7 +3250,9 @@ var BriefSectionSchema = z2.union([
3204
3250
  ]);
3205
3251
  var BriefSchemaSchema = z2.object({
3206
3252
  pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3207
- issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional()
3253
+ issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3254
+ milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3255
+ ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
3208
3256
  });
3209
3257
  function isSafeRepoRelPath(p) {
3210
3258
  if (p.length === 0)
@@ -3316,11 +3364,74 @@ function loadTrustAnchorConfig(fetcher = ghFetchTrustAnchorConfig) {
3316
3364
  }
3317
3365
  }
3318
3366
 
3367
+ // src/checks/edge-resolve.ts
3368
+ import { execFileSync as execFileSync3 } from "node:child_process";
3369
+ var DIRECT_ISSUE_REF = /#(\d+)/;
3370
+ var issueCache = new Map;
3371
+ function fetchIssueState(num, repo) {
3372
+ const key = `${repo.owner}/${repo.repo}#${num}`;
3373
+ if (issueCache.has(key))
3374
+ return issueCache.get(key) ?? null;
3375
+ let out = null;
3376
+ try {
3377
+ const raw = execFileSync3("gh", [
3378
+ "issue",
3379
+ "view",
3380
+ String(num),
3381
+ "-R",
3382
+ `${repo.owner}/${repo.repo}`,
3383
+ "--json",
3384
+ "state,stateReason,closedByPullRequestsReferences"
3385
+ ], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
3386
+ out = JSON.parse(raw);
3387
+ } catch {
3388
+ out = null;
3389
+ }
3390
+ issueCache.set(key, out);
3391
+ return out;
3392
+ }
3393
+ function edgeFromIssueJson(num, json) {
3394
+ if (json === null) {
3395
+ return { issue: num, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3396
+ }
3397
+ const state = json.state === "CLOSED" ? "closed" : json.state === "OPEN" ? "open" : null;
3398
+ const reason = json.stateReason === "COMPLETED" ? "completed" : json.stateReason === "NOT_PLANNED" ? "not_planned" : null;
3399
+ const closedByMergedPr = (json.closedByPullRequestsReferences ?? []).length > 0;
3400
+ return {
3401
+ issue: num,
3402
+ merged: state === "closed" && closedByMergedPr,
3403
+ open: false,
3404
+ issueState: state,
3405
+ stateReason: reason,
3406
+ closedByActor: null
3407
+ };
3408
+ }
3409
+ function resolveEdge(id, taskById, factsByTaskId, repo) {
3410
+ const target = taskById.get(id);
3411
+ if (target) {
3412
+ const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3413
+ return {
3414
+ issue: target.issue,
3415
+ merged: facts?.prState === "merged",
3416
+ open: facts?.prState === "open",
3417
+ issueState: facts?.issueState ?? null,
3418
+ stateReason: facts?.stateReason ?? null,
3419
+ closedByActor: facts?.closedByActor ?? null
3420
+ };
3421
+ }
3422
+ const direct = id.match(DIRECT_ISSUE_REF);
3423
+ if (direct) {
3424
+ const num = Number(direct[1]);
3425
+ return edgeFromIssueJson(num, fetchIssueState(num, repo));
3426
+ }
3427
+ return { issue: null, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3428
+ }
3429
+
3319
3430
  // src/checks/bin/check-first-push-dispatch.ts
3320
3431
  var CHECK_NAME = "first-push-dispatch";
3321
3432
  function git(args) {
3322
3433
  try {
3323
- return execFileSync3("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3434
+ return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3324
3435
  } catch {
3325
3436
  return "";
3326
3437
  }
@@ -3343,7 +3454,7 @@ function resolveRepo2() {
3343
3454
  }
3344
3455
  function prExistsFor(branch) {
3345
3456
  try {
3346
- const out = execFileSync3("gh", ["pr", "list", "--head", branch, "--state", "all", "--json", "number", "--limit", "1"], {
3457
+ const out = execFileSync4("gh", ["pr", "list", "--head", branch, "--state", "all", "--json", "number", "--limit", "1"], {
3347
3458
  encoding: "utf8",
3348
3459
  stdio: ["ignore", "pipe", "ignore"]
3349
3460
  });
@@ -3353,29 +3464,6 @@ function prExistsFor(branch) {
3353
3464
  return false;
3354
3465
  }
3355
3466
  }
3356
- function resolveEdge(id, taskById, factsByTaskId) {
3357
- const target = taskById.get(id);
3358
- if (target) {
3359
- const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3360
- return {
3361
- issue: target.issue,
3362
- merged: facts?.prState === "merged",
3363
- open: facts?.prState === "open",
3364
- issueState: facts?.issueState ?? null,
3365
- stateReason: facts?.stateReason ?? null,
3366
- closedByActor: facts?.closedByActor ?? null
3367
- };
3368
- }
3369
- const direct = id.match(/^#(\d+)$/);
3370
- return {
3371
- issue: direct ? Number(direct[1]) : null,
3372
- merged: false,
3373
- open: false,
3374
- issueState: null,
3375
- stateReason: null,
3376
- closedByActor: null
3377
- };
3378
- }
3379
3467
  async function classifyReadiness(trancheSlug, taskId) {
3380
3468
  const repo = resolveRepo2();
3381
3469
  if (!repo)
@@ -3403,7 +3491,7 @@ async function classifyReadiness(trancheSlug, taskId) {
3403
3491
  const taskById = new Map(tranche.tasks.map((t) => [t.id, t]));
3404
3492
  const factsByTaskId = snapshot.facts;
3405
3493
  const dependsOn = task.dependsOn.map((dep) => {
3406
- const r = resolveEdge(dep, taskById, factsByTaskId);
3494
+ const r = resolveEdge(dep, taskById, factsByTaskId, repo);
3407
3495
  return {
3408
3496
  id: dep,
3409
3497
  issue: r.issue,
@@ -3414,7 +3502,7 @@ async function classifyReadiness(trancheSlug, taskId) {
3414
3502
  };
3415
3503
  });
3416
3504
  const conflictsWith = task.conflictsWith.map((c) => {
3417
- const r = resolveEdge(c, taskById, factsByTaskId);
3505
+ const r = resolveEdge(c, taskById, factsByTaskId, repo);
3418
3506
  return { id: c, issue: r.issue, openOrInFlight: r.open };
3419
3507
  });
3420
3508
  const input = {
@@ -273,6 +273,9 @@ function issueListByLabelArgs(owner, repo, label) {
273
273
  "200"
274
274
  ];
275
275
  }
276
+ function ghIssueListByLabel(owner, repo, label) {
277
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
278
+ }
276
279
  async function ghIssueListByLabelAsync(owner, repo, label) {
277
280
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
278
281
  }
@@ -288,18 +291,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
288
291
  return dedupeByNumber(perLabel.flat());
289
292
  }
290
293
 
291
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
292
- function findMilestoneForSlug(owner, repo, slug) {
293
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
294
- const match = milestones.find((m) => m.title === slug);
295
- if (!match)
296
- return null;
297
- return {
298
- goal: match.description ?? "",
299
- lifecycle: match.state === "closed" ? "complete" : "active"
300
- };
301
- }
302
-
303
294
  // ../../packages/aeg-forge-state/src/labels.ts
304
295
  var LABELS = [
305
296
  {
@@ -542,6 +533,60 @@ function resolveTaskIssueRef(title, labels) {
542
533
  return { trancheSlug, taskId };
543
534
  }
544
535
 
536
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
537
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
538
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
539
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
540
+ function intentGoalForSlug(description, slug) {
541
+ const text = stripCode(description, { inlineSpans: "keep" });
542
+ const start = text.match(INTENTS_HEADING);
543
+ if (!start || start.index === undefined)
544
+ return "";
545
+ const rest = text.slice(start.index + start[0].length);
546
+ const next = rest.match(NEXT_HEADING);
547
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
548
+ for (const line of section.split(`
549
+ `)) {
550
+ const trimmed = line.trim();
551
+ if (trimmed.length === 0)
552
+ continue;
553
+ const m = trimmed.match(INTENT_BULLET);
554
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
555
+ return (m[2] ?? "").trim();
556
+ }
557
+ return "";
558
+ }
559
+ function matchesLegacyMilestone(milestones, slug) {
560
+ return milestones.find((m) => m.title === slug) ?? null;
561
+ }
562
+ function factsFromLegacyMilestone(milestone) {
563
+ return {
564
+ goal: milestone.description ?? "",
565
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
566
+ };
567
+ }
568
+ function lifecycleFromIssues(issues) {
569
+ if (issues.length === 0)
570
+ return "planned";
571
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
572
+ }
573
+ function goalFromMilestones(milestones, slug) {
574
+ for (const m of milestones) {
575
+ const goal = intentGoalForSlug(m.description ?? "", slug);
576
+ if (goal)
577
+ return goal;
578
+ }
579
+ return "";
580
+ }
581
+ function findMilestoneForSlug(owner, repo, slug) {
582
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
583
+ const legacy = matchesLegacyMilestone(milestones, slug);
584
+ if (legacy)
585
+ return factsFromLegacyMilestone(legacy);
586
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
587
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
588
+ }
589
+
545
590
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
546
591
  function trancheFromIssues(slug, issues, known) {
547
592
  return {