@crewbie/cli 0.1.0-alpha.49

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 (165) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +253 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +506 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/config.d.ts +84 -0
  7. package/dist/config.js +178 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/core.d.ts +31 -0
  10. package/dist/core.js +186 -0
  11. package/dist/core.js.map +1 -0
  12. package/dist/execution/attribution.d.ts +10 -0
  13. package/dist/execution/attribution.js +102 -0
  14. package/dist/execution/attribution.js.map +1 -0
  15. package/dist/execution/cancel.d.ts +3 -0
  16. package/dist/execution/cancel.js +59 -0
  17. package/dist/execution/cancel.js.map +1 -0
  18. package/dist/execution/capabilities.d.ts +22 -0
  19. package/dist/execution/capabilities.js +101 -0
  20. package/dist/execution/capabilities.js.map +1 -0
  21. package/dist/execution/controls.d.ts +51 -0
  22. package/dist/execution/controls.js +257 -0
  23. package/dist/execution/controls.js.map +1 -0
  24. package/dist/execution/dispatch.d.ts +69 -0
  25. package/dist/execution/dispatch.js +831 -0
  26. package/dist/execution/dispatch.js.map +1 -0
  27. package/dist/execution/fix.d.ts +3 -0
  28. package/dist/execution/fix.js +267 -0
  29. package/dist/execution/fix.js.map +1 -0
  30. package/dist/execution/github.d.ts +15 -0
  31. package/dist/execution/github.js +43 -0
  32. package/dist/execution/github.js.map +1 -0
  33. package/dist/execution/merge.d.ts +15 -0
  34. package/dist/execution/merge.js +133 -0
  35. package/dist/execution/merge.js.map +1 -0
  36. package/dist/execution/planning-approval.d.ts +33 -0
  37. package/dist/execution/planning-approval.js +227 -0
  38. package/dist/execution/planning-approval.js.map +1 -0
  39. package/dist/execution/pr-review.d.ts +86 -0
  40. package/dist/execution/pr-review.js +339 -0
  41. package/dist/execution/pr-review.js.map +1 -0
  42. package/dist/execution/review-loop.d.ts +63 -0
  43. package/dist/execution/review-loop.js +463 -0
  44. package/dist/execution/review-loop.js.map +1 -0
  45. package/dist/execution/test-feature.d.ts +29 -0
  46. package/dist/execution/test-feature.js +240 -0
  47. package/dist/execution/test-feature.js.map +1 -0
  48. package/dist/execution/watch.d.ts +12 -0
  49. package/dist/execution/watch.js +34 -0
  50. package/dist/execution/watch.js.map +1 -0
  51. package/dist/memory/context.d.ts +14 -0
  52. package/dist/memory/context.js +82 -0
  53. package/dist/memory/context.js.map +1 -0
  54. package/dist/memory/improvement.d.ts +25 -0
  55. package/dist/memory/improvement.js +168 -0
  56. package/dist/memory/improvement.js.map +1 -0
  57. package/dist/memory/launch.d.ts +9 -0
  58. package/dist/memory/launch.js +65 -0
  59. package/dist/memory/launch.js.map +1 -0
  60. package/dist/memory/runner.d.ts +4 -0
  61. package/dist/memory/runner.js +74 -0
  62. package/dist/memory/runner.js.map +1 -0
  63. package/dist/memory/state.d.ts +10 -0
  64. package/dist/memory/state.js +42 -0
  65. package/dist/memory/state.js.map +1 -0
  66. package/dist/presentation.d.ts +19 -0
  67. package/dist/presentation.js +128 -0
  68. package/dist/presentation.js.map +1 -0
  69. package/dist/reporting/dashboard.d.ts +2 -0
  70. package/dist/reporting/dashboard.js +106 -0
  71. package/dist/reporting/dashboard.js.map +1 -0
  72. package/dist/reporting/pr-usage.d.ts +24 -0
  73. package/dist/reporting/pr-usage.js +179 -0
  74. package/dist/reporting/pr-usage.js.map +1 -0
  75. package/dist/reporting/records.d.ts +28 -0
  76. package/dist/reporting/records.js +152 -0
  77. package/dist/reporting/records.js.map +1 -0
  78. package/dist/setup/agents.d.ts +8 -0
  79. package/dist/setup/agents.js +83 -0
  80. package/dist/setup/agents.js.map +1 -0
  81. package/dist/setup/assessment.d.ts +31 -0
  82. package/dist/setup/assessment.js +75 -0
  83. package/dist/setup/assessment.js.map +1 -0
  84. package/dist/setup/auto-loaded.d.ts +17 -0
  85. package/dist/setup/auto-loaded.js +49 -0
  86. package/dist/setup/auto-loaded.js.map +1 -0
  87. package/dist/setup/copilot-version.d.ts +7 -0
  88. package/dist/setup/copilot-version.js +39 -0
  89. package/dist/setup/copilot-version.js.map +1 -0
  90. package/dist/setup/copilot.d.ts +22 -0
  91. package/dist/setup/copilot.js +164 -0
  92. package/dist/setup/copilot.js.map +1 -0
  93. package/dist/setup/init.d.ts +41 -0
  94. package/dist/setup/init.js +315 -0
  95. package/dist/setup/init.js.map +1 -0
  96. package/dist/setup/install.d.ts +12 -0
  97. package/dist/setup/install.js +211 -0
  98. package/dist/setup/install.js.map +1 -0
  99. package/dist/setup/instruction-quality.d.ts +25 -0
  100. package/dist/setup/instruction-quality.js +258 -0
  101. package/dist/setup/instruction-quality.js.map +1 -0
  102. package/dist/setup/inventory.d.ts +26 -0
  103. package/dist/setup/inventory.js +78 -0
  104. package/dist/setup/inventory.js.map +1 -0
  105. package/dist/setup/onboarding.d.ts +40 -0
  106. package/dist/setup/onboarding.js +472 -0
  107. package/dist/setup/onboarding.js.map +1 -0
  108. package/dist/setup/package.d.ts +2 -0
  109. package/dist/setup/package.js +8 -0
  110. package/dist/setup/package.js.map +1 -0
  111. package/dist/setup/planning-workflow.d.ts +3 -0
  112. package/dist/setup/planning-workflow.js +250 -0
  113. package/dist/setup/planning-workflow.js.map +1 -0
  114. package/dist/setup/repository-map.d.ts +34 -0
  115. package/dist/setup/repository-map.js +89 -0
  116. package/dist/setup/repository-map.js.map +1 -0
  117. package/dist/setup/review.d.ts +8 -0
  118. package/dist/setup/review.js +137 -0
  119. package/dist/setup/review.js.map +1 -0
  120. package/dist/setup/team.d.ts +18 -0
  121. package/dist/setup/team.js +172 -0
  122. package/dist/setup/team.js.map +1 -0
  123. package/dist/setup/templates.d.ts +11 -0
  124. package/dist/setup/templates.js +494 -0
  125. package/dist/setup/templates.js.map +1 -0
  126. package/dist/setup/terminal.d.ts +12 -0
  127. package/dist/setup/terminal.js +11 -0
  128. package/dist/setup/terminal.js.map +1 -0
  129. package/dist/setup/update.d.ts +6 -0
  130. package/dist/setup/update.js +71 -0
  131. package/dist/setup/update.js.map +1 -0
  132. package/dist/specification/batch.d.ts +50 -0
  133. package/dist/specification/batch.js +127 -0
  134. package/dist/specification/batch.js.map +1 -0
  135. package/dist/specification/plan-checks.d.ts +6 -0
  136. package/dist/specification/plan-checks.js +73 -0
  137. package/dist/specification/plan-checks.js.map +1 -0
  138. package/dist/specification/planning.d.ts +26 -0
  139. package/dist/specification/planning.js +507 -0
  140. package/dist/specification/planning.js.map +1 -0
  141. package/dist/specification/prose.d.ts +1 -0
  142. package/dist/specification/prose.js +15 -0
  143. package/dist/specification/prose.js.map +1 -0
  144. package/dist/tracking/ado.d.ts +19 -0
  145. package/dist/tracking/ado.js +128 -0
  146. package/dist/tracking/ado.js.map +1 -0
  147. package/dist/tracking/github.d.ts +8 -0
  148. package/dist/tracking/github.js +112 -0
  149. package/dist/tracking/github.js.map +1 -0
  150. package/dist/tracking/issues.d.ts +28 -0
  151. package/dist/tracking/issues.js +180 -0
  152. package/dist/tracking/issues.js.map +1 -0
  153. package/dist/tracking/native.d.ts +5 -0
  154. package/dist/tracking/native.js +30 -0
  155. package/dist/tracking/native.js.map +1 -0
  156. package/dist/tracking/refs.d.ts +14 -0
  157. package/dist/tracking/refs.js +56 -0
  158. package/dist/tracking/refs.js.map +1 -0
  159. package/dist/tracking/sources.d.ts +5 -0
  160. package/dist/tracking/sources.js +29 -0
  161. package/dist/tracking/sources.js.map +1 -0
  162. package/docs/operations.md +1114 -0
  163. package/examples/batch.json +27 -0
  164. package/examples/runs.json +22 -0
  165. package/package.json +50 -0
@@ -0,0 +1,133 @@
1
+ import { mergeFor, reviewerFor } from "../config.js";
2
+ import { GitHubError, integer, record, string } from "../core.js";
3
+ import { requestReview, trustedReview } from "./pr-review.js";
4
+ /** Copilot leaves finished PRs as drafts; Crewbie marks them ready once the session completed and attribution ran. */
5
+ export async function markReady(client, pr) {
6
+ if (pr.draft !== true || pr.state !== "open")
7
+ return false;
8
+ const response = record(await client.request("POST", "/graphql", {
9
+ query: "mutation($id:ID!){markPullRequestReadyForReview(input:{pullRequestId:$id}){pullRequest{isDraft}}}",
10
+ variables: { id: string(pr.node_id, "PR node ID") },
11
+ }), "ready-for-review response");
12
+ if (Array.isArray(response.errors) && response.errors.length)
13
+ throw new Error(`GitHub did not mark PR #${pr.number} ready for review.`);
14
+ return true;
15
+ }
16
+ /** Reads check runs and statuses; the dispatch job sets its checks-scoped GITHUB_TOKEN so the user credential needs no Checks access. */
17
+ export const CHECK_READER = {};
18
+ const PASSED = new Set(["success", "skipped", "neutral"]);
19
+ /** Every check on the head passed. Re-runs of one check keep only the newest; the running dispatch job itself is excluded. */
20
+ export function checksPassed(runs, statuses) {
21
+ const newest = new Map();
22
+ for (const run of runs) {
23
+ const app = run.app === null || run.app === undefined ? "" : String(record(run.app, "check app").slug);
24
+ if (app === "github-actions" && run.name === "dispatch")
25
+ continue;
26
+ const key = `${app}/${String(run.name)}`;
27
+ const current = newest.get(key);
28
+ if (!current || integer(run.id, "check run ID") > integer(current.id, "check run ID"))
29
+ newest.set(key, run);
30
+ }
31
+ for (const run of newest.values()) {
32
+ if (run.status !== "completed")
33
+ return `Waiting for check ${String(run.name)}.`;
34
+ if (!PASSED.has(String(run.conclusion)))
35
+ return `Check ${String(run.name)} is ${String(run.conclusion)}.`;
36
+ }
37
+ const latest = new Map();
38
+ for (const status of statuses)
39
+ if (!latest.has(String(status.context)))
40
+ latest.set(String(status.context), status);
41
+ for (const status of latest.values()) {
42
+ if (status.state === "pending")
43
+ return `Waiting for status ${String(status.context)}.`;
44
+ if (status.state !== "success")
45
+ return `Status ${String(status.context)} is ${String(status.state)}.`;
46
+ }
47
+ return null;
48
+ }
49
+ async function matchesDefault(client, prefix, base, file) {
50
+ const path = String(file.filename).split("/").map(encodeURIComponent).join("/");
51
+ let current;
52
+ try {
53
+ current = String(record(await client.request("GET", `${prefix}/contents/${path}?ref=${encodeURIComponent(base)}`), "workflow file").sha);
54
+ }
55
+ catch (error) {
56
+ if (error instanceof GitHubError && error.status === 404)
57
+ current = null;
58
+ else
59
+ throw error;
60
+ }
61
+ return file.status === "removed" ? current === null : typeof file.sha === "string" && file.sha === current;
62
+ }
63
+ /** Merges a ready task PR at the completed session's head once every check passed and GitHub reports it mergeable. Never bypasses protection. */
64
+ export async function autoMerge(client, config, number, reviewed) {
65
+ const merge = mergeFor(config);
66
+ const prefix = `/repos/${config.repository}`;
67
+ const vetted = "Session completed on";
68
+ const pr = record(await client.request("GET", `${prefix}/pulls/${number}`), "pull request");
69
+ if (pr.state !== "open")
70
+ return { merged: false, reason: "PR is not open." };
71
+ if (pr.draft === true)
72
+ return { merged: false, reason: "PR is still a draft." };
73
+ const head = string(record(pr.head, "PR head").sha, "PR head SHA");
74
+ if (head !== reviewed)
75
+ return { merged: false, reason: "The PR head changed; waiting for the next dispatch run." };
76
+ // Workflows on the feature branch run with repository secrets for PRs into it, so a person merges workflow changes.
77
+ // A workflow file identical to the default branch (for example brought in by merging it) is not a change a person must vet.
78
+ const files = (await client.list(`${prefix}/pulls/${number}/files`)).filter((file) => String(file.filename).startsWith(".github/workflows/"));
79
+ const workflows = [];
80
+ if (files.length) {
81
+ const base = string(record(await client.request("GET", prefix), "repository").default_branch, "default branch");
82
+ for (const file of files)
83
+ if (!await matchesDefault(client, prefix, base, file))
84
+ workflows.push(String(file.filename));
85
+ }
86
+ if (workflows.length)
87
+ return { merged: false, reason: `PR #${number} changes ${workflows.join(", ")}; review and merge it yourself.` };
88
+ const checks = CHECK_READER.client ?? client;
89
+ const runs = record(await checks.request("GET", `${prefix}/commits/${head}/check-runs?per_page=100`), "check runs");
90
+ if (!Array.isArray(runs.check_runs) || integer(runs.total_count, "check run count", 0) > runs.check_runs.length) {
91
+ return { merged: false, reason: "Not every check run could be read; merge manually after verifying checks." };
92
+ }
93
+ const combined = record(await checks.request("GET", `${prefix}/commits/${head}/status`), "commit status");
94
+ const statuses = Array.isArray(combined.statuses) ? combined.statuses.map((status) => record(status, "commit status")) : [];
95
+ const ran = runs.check_runs.map((run) => record(run, "check run")).filter((run) => run.conclusion !== "action_required");
96
+ const waiting = checksPassed(ran, statuses);
97
+ if (waiting)
98
+ return { merged: false, reason: `${vetted} ${head.slice(0, 7)}. ${waiting}` };
99
+ // Crewbie assumes no CI and no Actions settings: checks that ran must pass, but none running (no CI, branch filters or
100
+ // runs held for approval) does not block, because the human-merged feature PR into the default branch is the gate.
101
+ const ciRan = ran.length > 0 || statuses.length > 0;
102
+ if (pr.mergeable === false)
103
+ return { merged: false, reason: "The PR conflicts with its base branch; resolve it, then Crewbie merges on its next run." };
104
+ if (pr.mergeable !== true)
105
+ return { merged: false, reason: "GitHub is still computing mergeability; Crewbie retries on its next run." };
106
+ // Green CI still says nothing about a task's own acceptance criteria, so a configured reviewer checks them first.
107
+ // Without CI the reviewer would only see untested code; the feature PR review and the human merge remain the gate.
108
+ const reviewer = ciRan ? reviewerFor(config) : undefined;
109
+ if (reviewer) {
110
+ const review = await trustedReview(client, config, number, head);
111
+ if (!review)
112
+ return { merged: false, reason: await requestReview(client, config, number, head) };
113
+ if (review.verdict !== "pass")
114
+ return { merged: false, reason: `crewbie-${reviewer.role} requested changes on ${head.slice(0, 7)}; fix the task PR before it merges.` };
115
+ if (review.partial)
116
+ return { merged: false, reason: "The task review omitted patches; review them and merge manually." };
117
+ }
118
+ try {
119
+ const result = record(await client.request("PUT", `${prefix}/pulls/${number}/merge`, { sha: head, merge_method: merge.method }), "merge result");
120
+ if (result.merged !== true)
121
+ return { merged: false, reason: `GitHub did not confirm the merge of PR #${number}.` };
122
+ }
123
+ catch (error) {
124
+ if (error instanceof GitHubError && [405, 409, 422].includes(error.status)) {
125
+ return { merged: false, reason: `GitHub refused the merge (HTTP ${error.status}); branch protection or a new head may apply.` };
126
+ }
127
+ throw error;
128
+ }
129
+ const verified = !ciRan ? "no checks ran on it; the feature PR is where it gets tested"
130
+ : reviewer ? "every check passed and the task review passed" : "every check passed";
131
+ return { merged: true, reason: `Auto-merged ${head.slice(0, 7)} into ${string(record(pr.base, "PR base").ref, "base branch")}: the session completed and ${verified}.` };
132
+ }
133
+ //# sourceMappingURL=merge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.js","sourceRoot":"","sources":["../../src/execution/merge.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { type Config } from "../config.js";
2
+ import { type Batch } from "../specification/batch.js";
3
+ import { type GitHubApi } from "../tracking/github.js";
4
+ import type { AdoApi } from "../tracking/ado.js";
5
+ export interface PlanExecution {
6
+ schemaVersion: 1;
7
+ sourceIssue: number;
8
+ runId: number;
9
+ baseSha: string;
10
+ key: string;
11
+ baseConfigHash: string;
12
+ configHash: string;
13
+ batchDigest: string;
14
+ files: Record<string, string>;
15
+ }
16
+ export declare function repoText(client: GitHubApi, repo: string, path: string, ref: string): Promise<{
17
+ content: string;
18
+ sha: string;
19
+ }>;
20
+ export declare function verifyPlanningRun(client: GitHubApi, config: Config, runId: number, baseSha: string, completed?: boolean): Promise<string>;
21
+ export declare function allowedPlanningFile(path: string, directory: string): boolean;
22
+ export declare function planningLocation(ref: string): {
23
+ sourceIssue: number;
24
+ keyPrefix: string;
25
+ directory: string;
26
+ };
27
+ export declare function approvedMergedPlan(client: GitHubApi, config: Config, number: number): Promise<{
28
+ batch: Batch;
29
+ headSha: string;
30
+ approver: string;
31
+ merger: string;
32
+ }>;
33
+ export declare function releaseMergedPlan(client: GitHubApi, config: Config, number: number, ado?: AdoApi): Promise<string>;
@@ -0,0 +1,227 @@
1
+ import { parseConfig, requireExecution } from "../config.js";
2
+ import { GitHubError, hash, integer, json, record, string, textHash } from "../core.js";
3
+ import { approvedBatch, batchDigest, parseBatch } from "../specification/batch.js";
4
+ import { isWriter } from "../tracking/github.js";
5
+ import { verifySources } from "../tracking/sources.js";
6
+ import { publish } from "../tracking/issues.js";
7
+ import { withDispatchLock } from "./dispatch.js";
8
+ import { SHARED_HOT } from "../memory/context.js";
9
+ function sha(value) {
10
+ const result = string(value, "commit SHA");
11
+ if (!/^[a-f0-9]{40}$/.test(result))
12
+ throw new Error("Expected an exact commit SHA.");
13
+ return result;
14
+ }
15
+ function digest(value) {
16
+ const result = string(value, "content fingerprint");
17
+ if (!/^[a-f0-9]{64}$/.test(result))
18
+ throw new Error("Expected a SHA-256 fingerprint.");
19
+ return result;
20
+ }
21
+ export async function repoText(client, repo, path, ref) {
22
+ const file = record(await client.request("GET", `/repos/${repo}/contents/${path}?ref=${sha(ref)}`), "repository file");
23
+ if (file.type !== "file" || file.encoding !== "base64" || typeof file.size !== "number" || file.size > 100_000)
24
+ throw new Error(`Expected a bounded regular file: ${path}`);
25
+ const content = Buffer.from(string(file.content, "file content", true), "base64").toString("utf8");
26
+ if (Buffer.byteLength(content) > 100_000)
27
+ throw new Error(`Repository file exceeds 100 KB: ${path}`);
28
+ return { content, sha: sha(file.sha) };
29
+ }
30
+ export async function verifyPlanningRun(client, config, runId, baseSha, completed = false) {
31
+ const run = record(await client.request("GET", `/repos/${config.repository}/actions/runs/${integer(runId, "planning run")}`), "planning workflow run");
32
+ if (!["issues", "issue_comment", "workflow_dispatch"].includes(String(run.event)) || run.path !== ".github/workflows/crewbie-plan.yml" || run.head_sha !== baseSha
33
+ || record(run.head_repository, "run repository").full_name !== config.repository || !await isWriter(client, config.repository, run.actor)
34
+ || (run.triggering_actor !== undefined && !await isWriter(client, config.repository, run.triggering_actor))
35
+ || (completed && (run.status !== "completed" || run.conclusion !== "success"))) {
36
+ throw new Error("Planning must originate from the approved default-branch issue workflow and complete successfully before execution.");
37
+ }
38
+ return string(run.head_branch, "planning branch");
39
+ }
40
+ function executionManifest(value) {
41
+ const data = record(value, "execution manifest");
42
+ if (data.schemaVersion !== 1)
43
+ throw new Error("Unsupported execution manifest.");
44
+ const files = Object.fromEntries(Object.entries(record(data.files, "reviewed files")).map(([path, value]) => [path, digest(value)]));
45
+ if (!Object.keys(files).length || Object.keys(files).length > 100)
46
+ throw new Error("Execution manifest requires 1-100 reviewed files.");
47
+ return {
48
+ schemaVersion: 1, sourceIssue: integer(data.sourceIssue, "source issue"), runId: integer(data.runId, "planning run"),
49
+ baseSha: sha(data.baseSha), key: digest(data.key), baseConfigHash: digest(data.baseConfigHash),
50
+ configHash: digest(data.configHash), batchDigest: digest(data.batchDigest), files,
51
+ };
52
+ }
53
+ export function allowedPlanningFile(path, directory) {
54
+ return [`${directory}/setup.json`, `${directory}/batch.json`, `${directory}/plan.md`].includes(path);
55
+ }
56
+ export function planningLocation(ref) {
57
+ const match = /^crewbie\/plans\/((?:[a-z0-9]+(?:-[a-z0-9]+)*-)?issue-(\d+))-([a-f0-9]{16})$/.exec(ref);
58
+ if (!match)
59
+ throw new Error("Expected a generated planning branch.");
60
+ return { sourceIssue: integer(Number(match[2]), "source issue"), keyPrefix: match[3], directory: `.crewbie/plans/${match[1]}` };
61
+ }
62
+ function nonRolePolicy(config) {
63
+ const { roles: _roles, ...policy } = config;
64
+ delete policy.approvers;
65
+ return policy;
66
+ }
67
+ export async function approvedMergedPlan(client, config, number) {
68
+ if (!config.planning?.enabled || !config.planning.executeOnMerge)
69
+ throw new Error("Execution on planning-PR merge is not enabled.");
70
+ requireExecution(config);
71
+ const prefix = `/repos/${config.repository}`;
72
+ const pr = record(await client.request("GET", `${prefix}/pulls/${integer(number, "planning PR")}`), "planning PR");
73
+ const head = record(pr.head, "planning head"), base = record(pr.base, "planning base");
74
+ const location = planningLocation(string(head.ref, "planning branch"));
75
+ if (pr.merged !== true || pr.state !== "closed" || pr.draft !== false
76
+ || record(head.repo, "head repository").full_name !== config.repository
77
+ || record(base.repo, "base repository").full_name !== config.repository)
78
+ throw new Error("Only a merged, same-repository planning PR can authorize execution.");
79
+ const headSha = sha(head.sha), mergeSha = sha(pr.merge_commit_sha);
80
+ const directory = location.directory;
81
+ const manifestPath = `${directory}/execution.json`;
82
+ let manifestFile;
83
+ try {
84
+ manifestFile = await repoText(client, config.repository, manifestPath, headSha);
85
+ }
86
+ catch (error) {
87
+ if (error instanceof GitHubError && error.status === 404)
88
+ throw new Error("This PR has no executable planning manifest. Resolve questions and generate a merge-enabled plan before requesting execution.");
89
+ throw error;
90
+ }
91
+ const manifest = executionManifest(JSON.parse(manifestFile.content));
92
+ if (manifest.sourceIssue !== location.sourceIssue || !manifest.key.startsWith(location.keyPrefix)
93
+ || !String(pr.body ?? "").includes(`<!-- crewbie-plan:${manifest.key} -->`))
94
+ throw new Error("Planning PR provenance does not match its execution manifest.");
95
+ const original = parseConfig(JSON.parse((await repoText(client, config.repository, ".crewbie/config.json", manifest.baseSha)).content));
96
+ if (original.repository !== config.repository || !original.planning?.enabled || !original.planning.executeOnMerge
97
+ || hash(json(original)) !== manifest.baseConfigHash
98
+ || json(nonRolePolicy(original)) !== json(nonRolePolicy(config))) {
99
+ throw new Error("A planning PR cannot grant itself execution permission or replace prior policy.");
100
+ }
101
+ const runBranch = await verifyPlanningRun(client, original, manifest.runId, manifest.baseSha, true);
102
+ const repository = record(await client.request("GET", prefix), "repository");
103
+ if (base.ref !== repository.default_branch)
104
+ throw new Error("Planning must be merged into the current default branch.");
105
+ if (runBranch !== repository.default_branch)
106
+ throw new Error("Planning policy must come from a default-branch workflow run.");
107
+ if (!await isWriter(client, original.repository, pr.merged_by))
108
+ throw new Error("A human user with write access must merge the planning PR.");
109
+ const mergedAt = Date.parse(string(pr.merged_at, "merge time"));
110
+ if (!Number.isFinite(mergedAt))
111
+ throw new Error("Invalid merge time.");
112
+ const reviews = await client.list(`${prefix}/pulls/${number}/reviews`);
113
+ const latest = new Map();
114
+ for (const review of [...reviews].sort((a, b) => integer(a.id, "review ID") - integer(b.id, "review ID"))) {
115
+ if (!await isWriter(client, original.repository, review.user) || !["APPROVED", "CHANGES_REQUESTED", "DISMISSED"].includes(String(review.state)))
116
+ continue;
117
+ const submitted = Date.parse(string(review.submitted_at, "review time"));
118
+ if (!Number.isFinite(submitted))
119
+ throw new Error("Invalid review time.");
120
+ if (submitted <= mergedAt)
121
+ latest.set(string(record(review.user, "reviewer").login, "reviewer login"), review);
122
+ }
123
+ if ([...latest.values()].some((review) => review.state === "CHANGES_REQUESTED"))
124
+ throw new Error("A write-access human reviewer still requests changes.");
125
+ const approval = [...latest.values()].find((review) => review.state === "APPROVED" && review.commit_id === headSha);
126
+ if (!approval)
127
+ throw new Error("A configured human must approve the exact final planning head before merge.");
128
+ const current = record(await client.request("GET", `${prefix}/git/ref/heads/${encodeURIComponent(string(repository.default_branch, "default branch"))}`), "default ref");
129
+ const currentSha = sha(record(current.object, "default ref object").sha);
130
+ const compare = record(await client.request("GET", `${prefix}/compare/${mergeSha}...${currentSha}`), "merge ancestry");
131
+ if (!["ahead", "identical"].includes(String(compare.status)) || record(compare.merge_base_commit, "merge base").sha !== mergeSha)
132
+ throw new Error("The approved planning merge is no longer on the default branch.");
133
+ const files = (await client.list(`${prefix}/pulls/${number}/files`));
134
+ const expected = new Set([...Object.keys(manifest.files), manifestPath]);
135
+ const changed = new Map(files.map((file) => [string(file.filename, "changed file"), file]));
136
+ // A planning PR may also record shared decisions; that memory is reviewed with the plan but never drives execution.
137
+ const decisions = changed.get(SHARED_HOT);
138
+ if (decisions && (!["added", "modified"].includes(String(decisions.status)) || decisions.previous_filename !== undefined))
139
+ throw new Error(`Planning PR contains an unapproved change: ${SHARED_HOT}`);
140
+ changed.delete(SHARED_HOT);
141
+ if (changed.size > expected.size || pr.changed_files !== files.length || files.length !== changed.size + (decisions ? 1 : 0)
142
+ || !changed.has(manifestPath))
143
+ throw new Error("Planning PR file coverage is incomplete or differs from the approved manifest.");
144
+ for (const [path, file] of changed) {
145
+ if (!expected.has(path)
146
+ || !["added", "modified"].includes(String(file.status)) || file.previous_filename !== undefined)
147
+ throw new Error(`Planning PR contains an unapproved change: ${path}`);
148
+ }
149
+ const contents = new Map();
150
+ for (const path of expected) {
151
+ if (path !== manifestPath && !allowedPlanningFile(path, directory))
152
+ throw new Error(`Planning manifest contains a non-plan file: ${path}. Planning PRs may only add plan files; regenerate the plan.`);
153
+ const approved = path === manifestPath ? manifestFile : await repoText(client, config.repository, path, headSha);
154
+ const file = changed.get(path);
155
+ if (file) {
156
+ if (approved.sha !== file.sha)
157
+ throw new Error(`Planning file is not the reviewed regular blob: ${path}`);
158
+ }
159
+ else {
160
+ // GitHub omits unchanged files from the PR diff, not from the reviewed manifest.
161
+ let prior;
162
+ try {
163
+ prior = await repoText(client, config.repository, path, manifest.baseSha);
164
+ }
165
+ catch (error) {
166
+ if (error instanceof GitHubError && error.status === 404)
167
+ throw new Error(`Planning PR file coverage omits a missing file: ${path}`);
168
+ throw error;
169
+ }
170
+ if (prior.sha !== approved.sha)
171
+ throw new Error(`Planning PR file coverage omits a changed file: ${path}`);
172
+ }
173
+ const fingerprint = textHash(approved.content);
174
+ if (path !== manifestPath && fingerprint !== manifest.files[path])
175
+ throw new Error(`Planning file changed after generation: ${path}. Regenerate the plan and review the new head.`);
176
+ for (const revision of new Set([mergeSha, currentSha])) {
177
+ if (textHash((await repoText(client, config.repository, path, revision)).content) !== fingerprint)
178
+ throw new Error(`Reviewed planning content changed at merge or afterwards: ${path}`);
179
+ }
180
+ contents.set(path, approved.content);
181
+ }
182
+ if (manifest.configHash !== hash(json(config)))
183
+ throw new Error("Active team configuration differs from the reviewed plan.");
184
+ const mergedConfig = parseConfig(JSON.parse((await repoText(client, config.repository, ".crewbie/config.json", currentSha)).content));
185
+ if (json(mergedConfig) !== json(config))
186
+ throw new Error("Checked-out configuration is stale.");
187
+ const batchText = contents.get(`${directory}/batch.json`);
188
+ if (!batchText)
189
+ throw new Error("Resolve planning questions before requesting execution.");
190
+ const batch = parseBatch(JSON.parse(batchText), config);
191
+ if (batch.approval !== null || batchDigest(batch) !== manifest.batchDigest)
192
+ throw new Error("The coordinator cannot self-approve or change the reviewed task batch.");
193
+ const source = `https://github.com/${config.repository}/issues/${manifest.sourceIssue}`;
194
+ if (batch.sources.length !== 1 || batch.sources[0]?.uri !== source || !batch.sources[0].fingerprint)
195
+ throw new Error("Execution must retain the exact planning source.");
196
+ await verifySources(batch.sources, client, config);
197
+ const fresh = record(await client.request("GET", `${prefix}/git/ref/heads/${encodeURIComponent(string(repository.default_branch, "default branch"))}`), "current default ref");
198
+ if (record(fresh.object, "ref object").sha !== currentSha)
199
+ throw new Error("Default branch changed during authorization. Retry against stable reviewed content.");
200
+ return { batch: approvedBatch(batch, true), headSha, approver: string(record(approval.user, "approver").login, "approver login"), merger: string(record(pr.merged_by, "merger").login, "merger login") };
201
+ }
202
+ export async function releaseMergedPlan(client, config, number, ado) {
203
+ const proof = await approvedMergedPlan(client, config, number);
204
+ const mappings = await withDispatchLock(client, config, async () => {
205
+ const published = await publish(client, config, proof.batch, ado, false);
206
+ for (const item of published) {
207
+ const path = `/repos/${config.repository}/issues/${item.issue}/comments`;
208
+ const body = `Execution authorized by ${proof.approver}'s approval and ${proof.merger}'s merge of planning PR #${number}, reviewed head \`${proof.headSha}\`.\nRecorded by the configured automation credential; no agent self-approval.\n\n<!-- crewbie-plan-approval:${number}:${proof.headSha} -->`;
209
+ const comments = await client.list(path);
210
+ let exists = false;
211
+ for (const comment of comments) {
212
+ if (comment.created_at === comment.updated_at && comment.body === body && await isWriter(client, config.repository, comment.user))
213
+ exists = true;
214
+ }
215
+ if (!exists) {
216
+ await client.request("POST", path, { body });
217
+ }
218
+ }
219
+ return published;
220
+ });
221
+ const repository = record(await client.request("GET", `/repos/${config.repository}`), "repository");
222
+ await client.request("POST", `/repos/${config.repository}/actions/workflows/crewbie-dispatch.yml/dispatches`, {
223
+ ref: string(repository.default_branch, "default branch"), inputs: { issue_numbers: mappings.map((item) => item.issue).join(",") },
224
+ });
225
+ return `Published ${mappings.length} specialist-owned tasks from approved planning PR #${number}; guarded cloud dispatch requested. Existing matching issues and persistent launch claims are reused on recovery.`;
226
+ }
227
+ //# sourceMappingURL=planning-approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planning-approval.js","sourceRoot":"","sources":["../../src/execution/planning-approval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { type Config } from "../config.js";
2
+ import { type TaskMetadata } from "../specification/batch.js";
3
+ import { type GitHubApi } from "../tracking/github.js";
4
+ export declare const REVIEW_WORKFLOW = "crewbie-review.yml";
5
+ export type Verdict = "pass" | "changes";
6
+ export interface Finding {
7
+ severity: "blocking" | "minor";
8
+ path: string;
9
+ line: number | null;
10
+ body: string;
11
+ }
12
+ export interface TrustedReview {
13
+ verdict: Verdict;
14
+ partial: boolean;
15
+ head: string;
16
+ url: string;
17
+ body: string;
18
+ createdAt: string;
19
+ }
20
+ /** Each review covers exactly one task PR or one feature PR. */
21
+ interface Snapshot {
22
+ schemaVersion: 1;
23
+ pr: number;
24
+ head: string;
25
+ feature?: {
26
+ batch: string;
27
+ branch: string;
28
+ };
29
+ task?: {
30
+ issue: number;
31
+ branch: string;
32
+ };
33
+ role: string;
34
+ runId: number;
35
+ omitted: string[];
36
+ ci?: string;
37
+ }
38
+ export declare function reviewRunName(pr: number, head: string): string;
39
+ export declare function closingTasks(client: GitHubApi, config: Config, pr: number): Promise<{
40
+ issue: Record<string, unknown>;
41
+ metadata: TaskMetadata;
42
+ }[]>;
43
+ /** The plan behind a feature PR: a same-repository Crewbie feature branch into the default branch, closing that plan's tasks. */
44
+ export declare function featureTasks(client: GitHubApi, config: Config, pull: Record<string, unknown>): Promise<{
45
+ batch: string;
46
+ branch: string;
47
+ tasks: {
48
+ issue: Record<string, unknown>;
49
+ metadata: TaskMetadata;
50
+ }[];
51
+ } | null>;
52
+ /**
53
+ * The head's CI results from the GitHub Checks and Statuses APIs: a deterministic signal the reviewer weighs instead of
54
+ * trusting (or demanding) a narrative in the PR text.
55
+ */
56
+ export declare function ciEvidence(client: GitHubApi, config: Config, head: string): Promise<{
57
+ summary: string;
58
+ details: string[];
59
+ }>;
60
+ /** Builds the reviewer prompt from the PR's API diff and the default-branch reviewer charter; no PR code is checked out. */
61
+ export declare function prepareReview(root: string, client: GitHubApi, config: Config, pr: number, head: string, runId: number): Promise<{
62
+ ready: boolean;
63
+ reason: string;
64
+ model: string;
65
+ }>;
66
+ export declare function parseReview(text: string): {
67
+ verdict: Verdict;
68
+ summary: string;
69
+ findings: Finding[];
70
+ };
71
+ export declare function renderReview(_config: Config, snapshot: Snapshot, review: ReturnType<typeof parseReview>): string;
72
+ export declare function publishReview(root: string, client: GitHubApi, config: Config): Promise<string>;
73
+ export declare function reviewMarker(body: string): {
74
+ run: number;
75
+ pr: number;
76
+ head: string;
77
+ verdict: Verdict;
78
+ partial: boolean;
79
+ } | null;
80
+ /** The newest review comment for this head, trusted only when the default-branch review workflow, started by a write-access user, posted it. */
81
+ export declare function trustedReview(client: GitHubApi, config: Config, pr: number, head: string, comments?: Record<string, unknown>[]): Promise<TrustedReview | null>;
82
+ /** The newest trusted Crewbie review comment for this feature PR, regardless of whether the head moved since. */
83
+ export declare function trustedLatestReview(client: GitHubApi, config: Config, pr: number, comments?: Record<string, unknown>[]): Promise<TrustedReview | null>;
84
+ /** Requests one review run per head. A failed run is reported, not retried, so a broken reviewer cannot loop on paid requests. */
85
+ export declare function requestReview(client: GitHubApi, config: Config, pr: number, head: string): Promise<string>;
86
+ export {};