@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
@@ -270,6 +270,9 @@ function issueListByLabelArgs(owner, repo, label) {
270
270
  "200"
271
271
  ];
272
272
  }
273
+ function ghIssueListByLabel(owner, repo, label) {
274
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
275
+ }
273
276
  async function ghIssueListByLabelAsync(owner, repo, label) {
274
277
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
275
278
  }
@@ -285,18 +288,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
285
288
  return dedupeByNumber(perLabel.flat());
286
289
  }
287
290
 
288
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
289
- function findMilestoneForSlug(owner, repo, slug) {
290
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
291
- const match = milestones.find((m) => m.title === slug);
292
- if (!match)
293
- return null;
294
- return {
295
- goal: match.description ?? "",
296
- lifecycle: match.state === "closed" ? "complete" : "active"
297
- };
298
- }
299
-
300
291
  // ../../packages/aeg-forge-state/src/labels.ts
301
292
  var LABELS = [
302
293
  {
@@ -539,6 +530,60 @@ function resolveTaskIssueRef(title, labels) {
539
530
  return { trancheSlug, taskId };
540
531
  }
541
532
 
533
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
534
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
535
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
536
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
537
+ function intentGoalForSlug(description, slug) {
538
+ const text = stripCode(description, { inlineSpans: "keep" });
539
+ const start = text.match(INTENTS_HEADING);
540
+ if (!start || start.index === undefined)
541
+ return "";
542
+ const rest = text.slice(start.index + start[0].length);
543
+ const next = rest.match(NEXT_HEADING);
544
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
545
+ for (const line of section.split(`
546
+ `)) {
547
+ const trimmed = line.trim();
548
+ if (trimmed.length === 0)
549
+ continue;
550
+ const m = trimmed.match(INTENT_BULLET);
551
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
552
+ return (m[2] ?? "").trim();
553
+ }
554
+ return "";
555
+ }
556
+ function matchesLegacyMilestone(milestones, slug) {
557
+ return milestones.find((m) => m.title === slug) ?? null;
558
+ }
559
+ function factsFromLegacyMilestone(milestone) {
560
+ return {
561
+ goal: milestone.description ?? "",
562
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
563
+ };
564
+ }
565
+ function lifecycleFromIssues(issues) {
566
+ if (issues.length === 0)
567
+ return "planned";
568
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
569
+ }
570
+ function goalFromMilestones(milestones, slug) {
571
+ for (const m of milestones) {
572
+ const goal = intentGoalForSlug(m.description ?? "", slug);
573
+ if (goal)
574
+ return goal;
575
+ }
576
+ return "";
577
+ }
578
+ function findMilestoneForSlug(owner, repo, slug) {
579
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
580
+ const legacy = matchesLegacyMilestone(milestones, slug);
581
+ if (legacy)
582
+ return factsFromLegacyMilestone(legacy);
583
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
584
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
585
+ }
586
+
542
587
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
543
588
  function trancheFromIssues(slug, issues, known) {
544
589
  return {
@@ -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 {
@@ -276,6 +276,9 @@ function issueListByLabelArgs(owner, repo, label) {
276
276
  "200"
277
277
  ];
278
278
  }
279
+ function ghIssueListByLabel(owner, repo, label) {
280
+ return JSON.parse(run(issueListByLabelArgs(owner, repo, label)));
281
+ }
279
282
  async function ghIssueListByLabelAsync(owner, repo, label) {
280
283
  return JSON.parse(await runAsync(issueListByLabelArgs(owner, repo, label)));
281
284
  }
@@ -291,18 +294,6 @@ async function ghIssueListByAnyLabelAsync(owner, repo, labels) {
291
294
  return dedupeByNumber(perLabel.flat());
292
295
  }
293
296
 
294
- // ../../packages/aeg-forge-state/src/fetch-milestone.ts
295
- function findMilestoneForSlug(owner, repo, slug) {
296
- const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
297
- const match = milestones.find((m) => m.title === slug);
298
- if (!match)
299
- return null;
300
- return {
301
- goal: match.description ?? "",
302
- lifecycle: match.state === "closed" ? "complete" : "active"
303
- };
304
- }
305
-
306
297
  // ../../packages/aeg-forge-state/src/labels.ts
307
298
  var LABELS = [
308
299
  {
@@ -545,6 +536,60 @@ function resolveTaskIssueRef(title, labels) {
545
536
  return { trancheSlug, taskId };
546
537
  }
547
538
 
539
+ // ../../packages/aeg-forge-state/src/fetch-milestone.ts
540
+ var INTENTS_HEADING = /^#{1,6}\s*Tranche intents\s*$/im;
541
+ var NEXT_HEADING = /^#{1,6}\s+\S/m;
542
+ var INTENT_BULLET = /^-\s+([a-z0-9][a-z0-9-]*)\s*:\s*(.+)$/i;
543
+ function intentGoalForSlug(description, slug) {
544
+ const text = stripCode(description, { inlineSpans: "keep" });
545
+ const start = text.match(INTENTS_HEADING);
546
+ if (!start || start.index === undefined)
547
+ return "";
548
+ const rest = text.slice(start.index + start[0].length);
549
+ const next = rest.match(NEXT_HEADING);
550
+ const section = rest.slice(0, next && next.index !== undefined ? next.index : rest.length);
551
+ for (const line of section.split(`
552
+ `)) {
553
+ const trimmed = line.trim();
554
+ if (trimmed.length === 0)
555
+ continue;
556
+ const m = trimmed.match(INTENT_BULLET);
557
+ if (m && (m[1] ?? "").toLowerCase() === slug.toLowerCase())
558
+ return (m[2] ?? "").trim();
559
+ }
560
+ return "";
561
+ }
562
+ function matchesLegacyMilestone(milestones, slug) {
563
+ return milestones.find((m) => m.title === slug) ?? null;
564
+ }
565
+ function factsFromLegacyMilestone(milestone) {
566
+ return {
567
+ goal: milestone.description ?? "",
568
+ lifecycle: milestone.state === "closed" ? "complete" : "active"
569
+ };
570
+ }
571
+ function lifecycleFromIssues(issues) {
572
+ if (issues.length === 0)
573
+ return "planned";
574
+ return issues.some((i) => i.state === "OPEN") ? "active" : "complete";
575
+ }
576
+ function goalFromMilestones(milestones, slug) {
577
+ for (const m of milestones) {
578
+ const goal = intentGoalForSlug(m.description ?? "", slug);
579
+ if (goal)
580
+ return goal;
581
+ }
582
+ return "";
583
+ }
584
+ function findMilestoneForSlug(owner, repo, slug) {
585
+ const milestones = ghApiGet(`repos/${owner}/${repo}/milestones?state=all&per_page=100`);
586
+ const legacy = matchesLegacyMilestone(milestones, slug);
587
+ if (legacy)
588
+ return factsFromLegacyMilestone(legacy);
589
+ const issues = ghIssueListByLabel(owner, repo, trancheLabel(slug));
590
+ return { goal: goalFromMilestones(milestones, slug), lifecycle: lifecycleFromIssues(issues) };
591
+ }
592
+
548
593
  // ../../packages/aeg-forge-state/src/derive-from-forge.ts
549
594
  function trancheFromIssues(slug, issues, known) {
550
595
  return {
@@ -3197,7 +3242,8 @@ var BRIEF_BUILTINS = [
3197
3242
  "for",
3198
3243
  "closesN",
3199
3244
  "premiseCoverage",
3200
- "issueRationale"
3245
+ "issueRationale",
3246
+ "milestoneShape"
3201
3247
  ];
3202
3248
  var BriefSectionSchema = z2.union([
3203
3249
  z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
@@ -3207,7 +3253,9 @@ var BriefSectionSchema = z2.union([
3207
3253
  ]);
3208
3254
  var BriefSchemaSchema = z2.object({
3209
3255
  pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3210
- issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional()
3256
+ issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3257
+ milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3258
+ ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
3211
3259
  });
3212
3260
  function isSafeRepoRelPath(p) {
3213
3261
  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-dispatch-readiness.ts
4
- import { execFile as execFile4, execFileSync as execFileSync3 } from "node:child_process";
4
+ import { execFile as execFile4, execFileSync as execFileSync4 } from "node:child_process";
5
5
  import { promisify as promisify4 } from "node:util";
6
6
  // ../../packages/aeg-forge-state/src/strip-code.ts
7
7
  function maskCode(body) {
@@ -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 {
@@ -3195,7 +3240,8 @@ var BRIEF_BUILTINS = [
3195
3240
  "for",
3196
3241
  "closesN",
3197
3242
  "premiseCoverage",
3198
- "issueRationale"
3243
+ "issueRationale",
3244
+ "milestoneShape"
3199
3245
  ];
3200
3246
  var BriefSectionSchema = z2.union([
3201
3247
  z2.object({ builtin: z2.enum(BRIEF_BUILTINS) }),
@@ -3205,7 +3251,9 @@ var BriefSectionSchema = z2.union([
3205
3251
  ]);
3206
3252
  var BriefSchemaSchema = z2.object({
3207
3253
  pr: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3208
- issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional()
3254
+ issue: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3255
+ milestone: z2.object({ sections: z2.array(BriefSectionSchema) }).optional(),
3256
+ ack: z2.array(z2.enum(BRIEF_BUILTINS)).optional()
3209
3257
  });
3210
3258
  function isSafeRepoRelPath(p) {
3211
3259
  if (p.length === 0)
@@ -3317,12 +3365,75 @@ function loadTrustAnchorConfig(fetcher = ghFetchTrustAnchorConfig) {
3317
3365
  }
3318
3366
  }
3319
3367
 
3368
+ // src/checks/edge-resolve.ts
3369
+ import { execFileSync as execFileSync3 } from "node:child_process";
3370
+ var DIRECT_ISSUE_REF = /#(\d+)/;
3371
+ var issueCache = new Map;
3372
+ function fetchIssueState(num, repo) {
3373
+ const key = `${repo.owner}/${repo.repo}#${num}`;
3374
+ if (issueCache.has(key))
3375
+ return issueCache.get(key) ?? null;
3376
+ let out = null;
3377
+ try {
3378
+ const raw = execFileSync3("gh", [
3379
+ "issue",
3380
+ "view",
3381
+ String(num),
3382
+ "-R",
3383
+ `${repo.owner}/${repo.repo}`,
3384
+ "--json",
3385
+ "state,stateReason,closedByPullRequestsReferences"
3386
+ ], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
3387
+ out = JSON.parse(raw);
3388
+ } catch {
3389
+ out = null;
3390
+ }
3391
+ issueCache.set(key, out);
3392
+ return out;
3393
+ }
3394
+ function edgeFromIssueJson(num, json) {
3395
+ if (json === null) {
3396
+ return { issue: num, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3397
+ }
3398
+ const state = json.state === "CLOSED" ? "closed" : json.state === "OPEN" ? "open" : null;
3399
+ const reason = json.stateReason === "COMPLETED" ? "completed" : json.stateReason === "NOT_PLANNED" ? "not_planned" : null;
3400
+ const closedByMergedPr = (json.closedByPullRequestsReferences ?? []).length > 0;
3401
+ return {
3402
+ issue: num,
3403
+ merged: state === "closed" && closedByMergedPr,
3404
+ open: false,
3405
+ issueState: state,
3406
+ stateReason: reason,
3407
+ closedByActor: null
3408
+ };
3409
+ }
3410
+ function resolveEdge(id, taskById, factsByTaskId, repo) {
3411
+ const target = taskById.get(id);
3412
+ if (target) {
3413
+ const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3414
+ return {
3415
+ issue: target.issue,
3416
+ merged: facts?.prState === "merged",
3417
+ open: facts?.prState === "open",
3418
+ issueState: facts?.issueState ?? null,
3419
+ stateReason: facts?.stateReason ?? null,
3420
+ closedByActor: facts?.closedByActor ?? null
3421
+ };
3422
+ }
3423
+ const direct = id.match(DIRECT_ISSUE_REF);
3424
+ if (direct) {
3425
+ const num = Number(direct[1]);
3426
+ return edgeFromIssueJson(num, fetchIssueState(num, repo));
3427
+ }
3428
+ return { issue: null, merged: false, open: false, issueState: null, stateReason: null, closedByActor: null };
3429
+ }
3430
+
3320
3431
  // src/checks/bin/check-dispatch-readiness.ts
3321
3432
  var CHECK_NAME = "dispatch-readiness";
3322
3433
  var execFileAsync4 = promisify4(execFile4);
3323
3434
  function git(args) {
3324
3435
  try {
3325
- return execFileSync3("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3436
+ return execFileSync4("git", args, { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
3326
3437
  } catch {
3327
3438
  return "";
3328
3439
  }
@@ -3369,29 +3480,6 @@ function fail(message, prompt) {
3369
3480
  });
3370
3481
  process.exit(1);
3371
3482
  }
3372
- function resolveEdge(id, taskById, factsByTaskId) {
3373
- const target = taskById.get(id);
3374
- if (target) {
3375
- const facts = target.issue !== null ? factsByTaskId.get(target.id) : undefined;
3376
- return {
3377
- issue: target.issue,
3378
- merged: facts?.prState === "merged",
3379
- open: facts?.prState === "open",
3380
- issueState: facts?.issueState ?? null,
3381
- stateReason: facts?.stateReason ?? null,
3382
- closedByActor: facts?.closedByActor ?? null
3383
- };
3384
- }
3385
- const direct = id.match(/^#(\d+)$/);
3386
- return {
3387
- issue: direct ? Number(direct[1]) : null,
3388
- merged: false,
3389
- open: false,
3390
- issueState: null,
3391
- stateReason: null,
3392
- closedByActor: null
3393
- };
3394
- }
3395
3483
  async function main() {
3396
3484
  const branch = currentBranch();
3397
3485
  const m = branch.match(/^task\/([^/]+)\/(.+)$/);
@@ -3432,7 +3520,7 @@ async function main() {
3432
3520
  const taskById = new Map(tranche.tasks.map((t) => [t.id, t]));
3433
3521
  const factsByTaskId = snapshot.facts;
3434
3522
  const dependsOn = task.dependsOn.map((dep) => {
3435
- const r = resolveEdge(dep, taskById, factsByTaskId);
3523
+ const r = resolveEdge(dep, taskById, factsByTaskId, repo);
3436
3524
  return {
3437
3525
  id: dep,
3438
3526
  issue: r.issue,
@@ -3443,7 +3531,7 @@ async function main() {
3443
3531
  };
3444
3532
  });
3445
3533
  const conflictsWith = task.conflictsWith.map((c) => {
3446
- const r = resolveEdge(c, taskById, factsByTaskId);
3534
+ const r = resolveEdge(c, taskById, factsByTaskId, repo);
3447
3535
  return { id: c, issue: r.issue, openOrInFlight: r.open };
3448
3536
  });
3449
3537
  const priorTrancheArchival = [];