@codedrifters/configulator 0.0.288 → 0.0.290
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.
- package/lib/index.d.mts +162 -245
- package/lib/index.d.ts +163 -246
- package/lib/index.js +527 -503
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +525 -495
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -205,8 +205,8 @@ __export(index_exports, {
|
|
|
205
205
|
DEFAULT_API_EXTRACTOR_REPORT_FILENAME: () => DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
|
|
206
206
|
DEFAULT_API_EXTRACTOR_REPORT_FOLDER: () => DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
|
|
207
207
|
DEFAULT_AUDIT_REPORT_DIR: () => DEFAULT_AUDIT_REPORT_DIR,
|
|
208
|
+
DEFAULT_BUNDLE_OVERRIDES: () => DEFAULT_BUNDLE_OVERRIDES,
|
|
208
209
|
DEFAULT_DECOMPOSITION_TEMPLATE: () => DEFAULT_DECOMPOSITION_TEMPLATE,
|
|
209
|
-
DEFAULT_DELEGATE_TO_PR_REVIEWER: () => DEFAULT_DELEGATE_TO_PR_REVIEWER,
|
|
210
210
|
DEFAULT_DISPATCH_MODEL: () => DEFAULT_DISPATCH_MODEL,
|
|
211
211
|
DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO: () => DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
|
|
212
212
|
DEFAULT_HOUSEKEEPING_MODEL: () => DEFAULT_HOUSEKEEPING_MODEL,
|
|
@@ -216,7 +216,6 @@ __export(index_exports, {
|
|
|
216
216
|
DEFAULT_ISSUE_TEMPLATES_ENABLED: () => DEFAULT_ISSUE_TEMPLATES_ENABLED,
|
|
217
217
|
DEFAULT_ISSUE_TEMPLATES_PATH: () => DEFAULT_ISSUE_TEMPLATES_PATH,
|
|
218
218
|
DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE: () => DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE,
|
|
219
|
-
DEFAULT_MERGE_METHOD: () => DEFAULT_MERGE_METHOD,
|
|
220
219
|
DEFAULT_OFF_PEAK_CRON_EXAMPLE: () => DEFAULT_OFF_PEAK_CRON_EXAMPLE,
|
|
221
220
|
DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE: () => DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE,
|
|
222
221
|
DEFAULT_PRIORITY_LABELS: () => DEFAULT_PRIORITY_LABELS,
|
|
@@ -226,7 +225,6 @@ __export(index_exports, {
|
|
|
226
225
|
DEFAULT_PROGRESS_FILES_FORMAT: () => DEFAULT_PROGRESS_FILES_FORMAT,
|
|
227
226
|
DEFAULT_PROGRESS_FILES_STALE_AFTER_HOURS: () => DEFAULT_PROGRESS_FILES_STALE_AFTER_HOURS,
|
|
228
227
|
DEFAULT_PROGRESS_FILES_STATE_DIR: () => DEFAULT_PROGRESS_FILES_STATE_DIR,
|
|
229
|
-
DEFAULT_REQUIRE_LINKED_ISSUE: () => DEFAULT_REQUIRE_LINKED_ISSUE,
|
|
230
228
|
DEFAULT_REQUIRE_PRODUCT_CONTEXT: () => DEFAULT_REQUIRE_PRODUCT_CONTEXT,
|
|
231
229
|
DEFAULT_SAMPLE_COMPILER_OPTIONS: () => DEFAULT_SAMPLE_COMPILER_OPTIONS,
|
|
232
230
|
DEFAULT_SCHEDULED_TASKS_ROOT: () => DEFAULT_SCHEDULED_TASKS_ROOT,
|
|
@@ -258,7 +256,6 @@ __export(index_exports, {
|
|
|
258
256
|
MINIMUM_RELEASE_AGE: () => MINIMUM_RELEASE_AGE,
|
|
259
257
|
MONOREPO_LAYOUT: () => MONOREPO_LAYOUT,
|
|
260
258
|
MonorepoProject: () => MonorepoProject,
|
|
261
|
-
PREFLIGHT_MERGE_METHOD_VALUES: () => PREFLIGHT_MERGE_METHOD_VALUES,
|
|
262
259
|
PROD_DEPLOY_NAME: () => PROD_DEPLOY_NAME,
|
|
263
260
|
PROGRESS_FILES_FORMAT_VALUES: () => PROGRESS_FILES_FORMAT_VALUES,
|
|
264
261
|
PnpmWorkspace: () => PnpmWorkspace,
|
|
@@ -362,8 +359,6 @@ __export(index_exports, {
|
|
|
362
359
|
renderIssueTemplatesRuleContent: () => renderIssueTemplatesRuleContent,
|
|
363
360
|
renderIssueTemplatesStarterPage: () => renderIssueTemplatesStarterPage,
|
|
364
361
|
renderMeetingTypesSection: () => renderMeetingTypesSection,
|
|
365
|
-
renderPreflightPrSection: () => renderPreflightPrSection,
|
|
366
|
-
renderPreflightPrShellHelpers: () => renderPreflightPrShellHelpers,
|
|
367
362
|
renderPriorityRulesSection: () => renderPriorityRulesSection,
|
|
368
363
|
renderProgressFileName: () => renderProgressFileName,
|
|
369
364
|
renderProgressFilePath: () => renderProgressFilePath,
|
|
@@ -396,7 +391,7 @@ __export(index_exports, {
|
|
|
396
391
|
resolveModelAlias: () => resolveModelAlias,
|
|
397
392
|
resolveOrchestratorAssets: () => resolveOrchestratorAssets,
|
|
398
393
|
resolveOutdirFromPackageName: () => resolveOutdirFromPackageName,
|
|
399
|
-
|
|
394
|
+
resolveOverrideForLabels: () => resolveOverrideForLabels,
|
|
400
395
|
resolveProgressFiles: () => resolveProgressFiles,
|
|
401
396
|
resolveRunRatio: () => resolveRunRatio,
|
|
402
397
|
resolveScheduledTasks: () => resolveScheduledTasks,
|
|
@@ -417,7 +412,6 @@ __export(index_exports, {
|
|
|
417
412
|
validateAgentTierConfig: () => validateAgentTierConfig,
|
|
418
413
|
validateIssueTemplatesConfig: () => validateIssueTemplatesConfig,
|
|
419
414
|
validateMonorepoLayout: () => validateMonorepoLayout,
|
|
420
|
-
validatePreflightPrConfig: () => validatePreflightPrConfig,
|
|
421
415
|
validateProgressFilesConfig: () => validateProgressFilesConfig,
|
|
422
416
|
validateRunRatioConfig: () => validateRunRatioConfig,
|
|
423
417
|
validateScheduledTasksConfig: () => validateScheduledTasksConfig,
|
|
@@ -11169,291 +11163,6 @@ var meetingAnalysisBundle = {
|
|
|
11169
11163
|
]
|
|
11170
11164
|
};
|
|
11171
11165
|
|
|
11172
|
-
// src/agent/bundles/preflight-pr.ts
|
|
11173
|
-
var DEFAULT_DELEGATE_TO_PR_REVIEWER = true;
|
|
11174
|
-
var DEFAULT_MERGE_METHOD = "squash";
|
|
11175
|
-
var DEFAULT_REQUIRE_LINKED_ISSUE = true;
|
|
11176
|
-
var PREFLIGHT_MERGE_METHOD_VALUES = [
|
|
11177
|
-
"squash",
|
|
11178
|
-
"merge",
|
|
11179
|
-
"rebase"
|
|
11180
|
-
];
|
|
11181
|
-
function resolvePreflightPr(config) {
|
|
11182
|
-
const mergeMethod = config?.mergeMethod ?? DEFAULT_MERGE_METHOD;
|
|
11183
|
-
assertValidMergeMethod(mergeMethod);
|
|
11184
|
-
const eligibleLabels = config?.eligibleLabels ?? [];
|
|
11185
|
-
assertValidEligibleLabels(eligibleLabels);
|
|
11186
|
-
return {
|
|
11187
|
-
enabled: config?.enabled ?? true,
|
|
11188
|
-
delegateToPrReviewer: config?.delegateToPrReviewer ?? DEFAULT_DELEGATE_TO_PR_REVIEWER,
|
|
11189
|
-
mergeMethod,
|
|
11190
|
-
requireLinkedIssue: config?.requireLinkedIssue ?? DEFAULT_REQUIRE_LINKED_ISSUE,
|
|
11191
|
-
eligibleLabels
|
|
11192
|
-
};
|
|
11193
|
-
}
|
|
11194
|
-
function validatePreflightPrConfig(config) {
|
|
11195
|
-
return resolvePreflightPr(config);
|
|
11196
|
-
}
|
|
11197
|
-
function renderPreflightPrSection(pf) {
|
|
11198
|
-
const lines = [
|
|
11199
|
-
"## Pre-flight PR merge",
|
|
11200
|
-
"",
|
|
11201
|
-
"The orchestrator runs a **pre-flight PR merge sweep** before its",
|
|
11202
|
-
"triage walk so already-approved, CI-green PRs land before the",
|
|
11203
|
-
"unblock and queue-scan phases read dependency state. Without",
|
|
11204
|
-
"pre-flight, an issue whose only remaining blocker is an approved",
|
|
11205
|
-
"PR stuck behind a branch-protection delay shows up as blocked on",
|
|
11206
|
-
"every dispatch cycle \u2014 the pipeline thrashes on stale dependency",
|
|
11207
|
-
"state.",
|
|
11208
|
-
""
|
|
11209
|
-
];
|
|
11210
|
-
if (!pf.enabled) {
|
|
11211
|
-
lines.push(
|
|
11212
|
-
"**Pre-flight is disabled for this project.** The orchestrator",
|
|
11213
|
-
"skips the pre-flight sweep entirely; PR merges land via the",
|
|
11214
|
-
"existing batch PR review step on housekeeping runs or via a",
|
|
11215
|
-
"human operator. Enable pre-flight via",
|
|
11216
|
-
"`AgentConfigOptions.preflightPr.enabled = true` once the repo",
|
|
11217
|
-
"is comfortable with automated merges.",
|
|
11218
|
-
""
|
|
11219
|
-
);
|
|
11220
|
-
return lines.join("\n");
|
|
11221
|
-
}
|
|
11222
|
-
lines.push(
|
|
11223
|
-
"### When pre-flight runs",
|
|
11224
|
-
"",
|
|
11225
|
-
"Pre-flight runs on **every orchestrator invocation** \u2014",
|
|
11226
|
-
"both dispatch and housekeeping runs \u2014 immediately after Phase A",
|
|
11227
|
-
"(Startup) and before any other phase reads issue or PR state. The",
|
|
11228
|
-
"sweep is idempotent: `gh pr merge` on an already-merged PR is a",
|
|
11229
|
-
"no-op, so re-running pre-flight on back-to-back invocations is",
|
|
11230
|
-
"safe and cheap.",
|
|
11231
|
-
"",
|
|
11232
|
-
"### Eligibility",
|
|
11233
|
-
"",
|
|
11234
|
-
"A PR is eligible for a pre-flight merge when **all** of the",
|
|
11235
|
-
"following hold:",
|
|
11236
|
-
"",
|
|
11237
|
-
"- PR is not draft and not closed.",
|
|
11238
|
-
"- PR is `MERGEABLE` (no branch conflicts).",
|
|
11239
|
-
"- Every required CI check has passed (`SUCCESS` or `SKIPPED`).",
|
|
11240
|
-
"- PR carries at least one `APPROVED` review **or** auto-merge is",
|
|
11241
|
-
" already enabled on the PR.",
|
|
11242
|
-
"- PR is not carrying `status:needs-attention`."
|
|
11243
|
-
);
|
|
11244
|
-
if (pf.requireLinkedIssue) {
|
|
11245
|
-
lines.push(
|
|
11246
|
-
"- PR body contains a `Closes #<n>` / `Fixes #<n>` / `Resolves`",
|
|
11247
|
-
" `#<n>` keyword referencing an open issue."
|
|
11248
|
-
);
|
|
11249
|
-
} else {
|
|
11250
|
-
lines.push(
|
|
11251
|
-
"- Linked-issue check is **disabled** for this project \u2014 PRs",
|
|
11252
|
-
" without a `Closes/Fixes/Resolves` keyword are eligible."
|
|
11253
|
-
);
|
|
11254
|
-
}
|
|
11255
|
-
if (pf.eligibleLabels.length > 0) {
|
|
11256
|
-
lines.push(
|
|
11257
|
-
"- PR carries at least one of the following consumer-declared",
|
|
11258
|
-
" eligibility labels:",
|
|
11259
|
-
"",
|
|
11260
|
-
...pf.eligibleLabels.map((label) => ` - \`${label}\``)
|
|
11261
|
-
);
|
|
11262
|
-
}
|
|
11263
|
-
lines.push(
|
|
11264
|
-
"",
|
|
11265
|
-
"PRs that fail any criterion are skipped with a one-line",
|
|
11266
|
-
"diagnostic and left for the `pr-reviewer` or a human operator to",
|
|
11267
|
-
"handle on the normal review path.",
|
|
11268
|
-
"",
|
|
11269
|
-
"### Execution mode",
|
|
11270
|
-
""
|
|
11271
|
-
);
|
|
11272
|
-
if (pf.delegateToPrReviewer) {
|
|
11273
|
-
lines.push(
|
|
11274
|
-
"**Delegate mode (default).** The orchestrator invokes the",
|
|
11275
|
-
"`pr-reviewer` sub-agent to run the pre-flight sweep. This",
|
|
11276
|
-
"mirrors vortex's step 0b contract: the merge workflow runs on",
|
|
11277
|
-
"its own (cheaper) model rather than consuming the orchestrator's",
|
|
11278
|
-
"more expensive model budget for mechanical merges.",
|
|
11279
|
-
"",
|
|
11280
|
-
"The sub-agent returns a one-line structured summary as the last",
|
|
11281
|
-
"line of its response:",
|
|
11282
|
-
"",
|
|
11283
|
-
"```",
|
|
11284
|
-
"Merged <n> (#<list>), skipped <m> (#<pr> \u2014 <reason>), <k> eligible left.",
|
|
11285
|
-
"```",
|
|
11286
|
-
"",
|
|
11287
|
-
"If the sub-agent's final line is missing, malformed, or",
|
|
11288
|
-
"indicates an error, the orchestrator **does not retry** \u2014 it",
|
|
11289
|
-
"proceeds to the next phase anyway. PR merges are idempotent at",
|
|
11290
|
-
"GitHub, so the next orchestrator session re-runs pre-flight.",
|
|
11291
|
-
"The only cost of a skipped sub-agent pass is that dependency",
|
|
11292
|
-
"issues stay open one more cycle."
|
|
11293
|
-
);
|
|
11294
|
-
} else {
|
|
11295
|
-
lines.push(
|
|
11296
|
-
"**Inline mode.** The orchestrator performs the pre-flight merge",
|
|
11297
|
-
"itself \u2014 it does **not** delegate to the `pr-reviewer`",
|
|
11298
|
-
"sub-agent. Use this mode in repos that prefer a single-process",
|
|
11299
|
-
"pipeline or that have not wired a `pr-reviewer` sub-agent.",
|
|
11300
|
-
"",
|
|
11301
|
-
`For each eligible PR, the orchestrator runs \`gh pr merge --${pf.mergeMethod}\``,
|
|
11302
|
-
"with `--delete-branch` and re-confirms that the linked issue",
|
|
11303
|
-
"closes (applying `status:done` if the auto-close did not fire)."
|
|
11304
|
-
);
|
|
11305
|
-
}
|
|
11306
|
-
lines.push(
|
|
11307
|
-
"",
|
|
11308
|
-
"### Post-merge verification",
|
|
11309
|
-
"",
|
|
11310
|
-
"After each successful merge, the orchestrator re-reads the linked",
|
|
11311
|
-
"issue state. When the merge commit did **not** auto-close the",
|
|
11312
|
-
"issue (closing-keyword typos, linked-issue keyword dropped during",
|
|
11313
|
-
"a rebase), the orchestrator closes the issue explicitly and",
|
|
11314
|
-
"applies `status:done` so the downstream unblock loop sees the",
|
|
11315
|
-
"dependency as resolved. This is the only way pre-flight prevents",
|
|
11316
|
-
"the stale-dependency thrash \u2014 without the verification step a",
|
|
11317
|
-
"merged-but-not-closed issue still reads as blocking to",
|
|
11318
|
-
"`check-blocked.sh unblock`."
|
|
11319
|
-
);
|
|
11320
|
-
return lines.join("\n");
|
|
11321
|
-
}
|
|
11322
|
-
function renderPreflightPrShellHelpers(pf) {
|
|
11323
|
-
const requireIssueFlag = pf.requireLinkedIssue ? "1" : "0";
|
|
11324
|
-
const hasLabelFilter = pf.eligibleLabels.length > 0;
|
|
11325
|
-
const lines = [
|
|
11326
|
-
"# Scan open PRs and emit one eligibility line per PR in the",
|
|
11327
|
-
"# canonical `PREFLIGHT_MERGE PR #<n> issue:#<m> branch:<b> \u2014",
|
|
11328
|
-
'# "<title>"` / `PREFLIGHT_SKIP PR #<n> \u2014 <reason> \u2014 "<title>"` /',
|
|
11329
|
-
"# `NO_PREFLIGHT_PRS` format. Orchestrator-side loops read this",
|
|
11330
|
-
"# output and either merge the PR inline or delegate the sweep to",
|
|
11331
|
-
"# the pr-reviewer sub-agent.",
|
|
11332
|
-
"cmd_preflight() {",
|
|
11333
|
-
' if [[ "$PREFLIGHT_ENABLED" != "1" ]]; then',
|
|
11334
|
-
' echo "NO_PREFLIGHT_PRS"',
|
|
11335
|
-
" return 0",
|
|
11336
|
-
" fi",
|
|
11337
|
-
" local prs",
|
|
11338
|
-
" prs=$(gh pr list --state open --json number,title,isDraft,mergeable,headRefName,labels,body,reviewDecision,autoMergeRequest \\",
|
|
11339
|
-
' --limit 50 2>/dev/null || echo "[]")',
|
|
11340
|
-
"",
|
|
11341
|
-
" local count",
|
|
11342
|
-
` count=$(echo "$prs" | jq 'length')`,
|
|
11343
|
-
' if [[ "$count" -eq 0 ]]; then',
|
|
11344
|
-
' echo "NO_PREFLIGHT_PRS"',
|
|
11345
|
-
" return 0",
|
|
11346
|
-
" fi",
|
|
11347
|
-
"",
|
|
11348
|
-
" # Stage 1: filter in jq on purely structural fields (draft,",
|
|
11349
|
-
" # labels, linked-issue keyword). CI state lives outside the",
|
|
11350
|
-
" # list endpoint so it is checked per-PR below.",
|
|
11351
|
-
" local candidates",
|
|
11352
|
-
` candidates=$(echo "$prs" | jq -r --arg require_issue "$PREFLIGHT_REQUIRE_LINKED_ISSUE" '`,
|
|
11353
|
-
" .[] |",
|
|
11354
|
-
" select(.isDraft == false) |",
|
|
11355
|
-
' select(.labels | map(.name) | index("status:needs-attention") | not) |'
|
|
11356
|
-
];
|
|
11357
|
-
if (hasLabelFilter) {
|
|
11358
|
-
const jqArray = pf.eligibleLabels.map((label) => `"${label.replace(/"/g, '\\"')}"`).join(",");
|
|
11359
|
-
lines.push(
|
|
11360
|
-
` select((.labels | map(.name)) as $names | [${jqArray}] | any(. as $l | $names | index($l))) |`
|
|
11361
|
-
);
|
|
11362
|
-
}
|
|
11363
|
-
lines.push(
|
|
11364
|
-
' (.body | capture("(?:Closes|Fixes|Resolves) #(?<num>[0-9]+)"; "i") | .num) as $issue |',
|
|
11365
|
-
' select($require_issue == "0" or ($issue != null and $issue != "")) |',
|
|
11366
|
-
' "\\(.number)\\t\\(.title)\\t\\(.headRefName)\\t\\($issue // "")\\t\\(.mergeable // "UNKNOWN")\\t\\(.reviewDecision // "")\\t\\((.autoMergeRequest // null) != null)"',
|
|
11367
|
-
" ' 2>/dev/null)",
|
|
11368
|
-
"",
|
|
11369
|
-
' if [[ -z "$candidates" ]]; then',
|
|
11370
|
-
' echo "NO_PREFLIGHT_PRS"',
|
|
11371
|
-
" return 0",
|
|
11372
|
-
" fi",
|
|
11373
|
-
"",
|
|
11374
|
-
" local found_eligible=false",
|
|
11375
|
-
" while IFS=$'\\t' read -r pr_num title branch issue_num mergeable review_decision auto_merge; do",
|
|
11376
|
-
' [[ -z "$pr_num" ]] && continue',
|
|
11377
|
-
"",
|
|
11378
|
-
" # Mergeable gate.",
|
|
11379
|
-
' if [[ "$mergeable" != "MERGEABLE" ]]; then',
|
|
11380
|
-
' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 not mergeable (${mergeable}) \u2014 \\"${title}\\""',
|
|
11381
|
-
" continue",
|
|
11382
|
-
" fi",
|
|
11383
|
-
"",
|
|
11384
|
-
" # Approval gate: APPROVED review OR auto-merge already",
|
|
11385
|
-
" # enabled. Either signal means a human has signed off on the",
|
|
11386
|
-
" # change; pre-flight only needs to wait for CI + branch",
|
|
11387
|
-
" # protection to clear.",
|
|
11388
|
-
' if [[ "$review_decision" != "APPROVED" && "$auto_merge" != "true" ]]; then',
|
|
11389
|
-
' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 not approved and auto-merge not set \u2014 \\"${title}\\""',
|
|
11390
|
-
" continue",
|
|
11391
|
-
" fi",
|
|
11392
|
-
"",
|
|
11393
|
-
" # CI gate. `gh pr checks` returns one row per check; anything",
|
|
11394
|
-
" # that is neither SUCCESS nor SKIPPED counts as failing or",
|
|
11395
|
-
" # pending.",
|
|
11396
|
-
" local failing_checks",
|
|
11397
|
-
' failing_checks=$(gh pr checks "$pr_num" --json name,state \\',
|
|
11398
|
-
` --jq '[.[] | select(.state != "SUCCESS" and .state != "SKIPPED")] | length' 2>/dev/null || echo "-1")`,
|
|
11399
|
-
"",
|
|
11400
|
-
' if [[ "$failing_checks" == "-1" ]]; then',
|
|
11401
|
-
' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 could not read CI status \u2014 \\"${title}\\""',
|
|
11402
|
-
" continue",
|
|
11403
|
-
" fi",
|
|
11404
|
-
"",
|
|
11405
|
-
' if [[ "$failing_checks" -gt 0 ]]; then',
|
|
11406
|
-
' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 ${failing_checks} CI check(s) not passing \u2014 \\"${title}\\""',
|
|
11407
|
-
" continue",
|
|
11408
|
-
" fi",
|
|
11409
|
-
"",
|
|
11410
|
-
" # Linked-issue state gate. A PR whose linked issue is already",
|
|
11411
|
-
" # closed should not block pre-flight \u2014 skip it so the unblock",
|
|
11412
|
-
" # loop picks up the dependency. This path also fires when the",
|
|
11413
|
-
" # PR was merged but the closing keyword was dropped.",
|
|
11414
|
-
' if [[ -n "$issue_num" ]]; then',
|
|
11415
|
-
" local issue_state",
|
|
11416
|
-
` issue_state=$(gh issue view "$issue_num" --json state --jq '.state' 2>/dev/null || echo "UNKNOWN")`,
|
|
11417
|
-
' if [[ "$issue_state" == "CLOSED" ]]; then',
|
|
11418
|
-
' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 linked issue #${issue_num} already closed \u2014 \\"${title}\\""',
|
|
11419
|
-
" continue",
|
|
11420
|
-
" fi",
|
|
11421
|
-
" fi",
|
|
11422
|
-
"",
|
|
11423
|
-
" found_eligible=true",
|
|
11424
|
-
' echo "PREFLIGHT_MERGE PR #${pr_num} issue:#${issue_num:-none} branch:${branch} \u2014 \\"${title}\\""',
|
|
11425
|
-
' done <<< "$candidates"',
|
|
11426
|
-
"",
|
|
11427
|
-
" if ! $found_eligible; then",
|
|
11428
|
-
' echo "NO_PREFLIGHT_PRS"',
|
|
11429
|
-
" fi",
|
|
11430
|
-
"}"
|
|
11431
|
-
);
|
|
11432
|
-
void requireIssueFlag;
|
|
11433
|
-
return lines.join("\n");
|
|
11434
|
-
}
|
|
11435
|
-
function assertValidMergeMethod(value) {
|
|
11436
|
-
if (!PREFLIGHT_MERGE_METHOD_VALUES.includes(value)) {
|
|
11437
|
-
throw new Error(
|
|
11438
|
-
`PreflightPrConfig.mergeMethod must be one of ${PREFLIGHT_MERGE_METHOD_VALUES.join(
|
|
11439
|
-
" | "
|
|
11440
|
-
)}; got ${JSON.stringify(value)}`
|
|
11441
|
-
);
|
|
11442
|
-
}
|
|
11443
|
-
}
|
|
11444
|
-
function assertValidEligibleLabels(labels) {
|
|
11445
|
-
for (let i = 0; i < labels.length; i += 1) {
|
|
11446
|
-
const label = labels[i];
|
|
11447
|
-
if (typeof label !== "string" || label.trim().length === 0) {
|
|
11448
|
-
throw new Error(
|
|
11449
|
-
`PreflightPrConfig.eligibleLabels[${i}] must be a non-empty string; got ${JSON.stringify(
|
|
11450
|
-
label
|
|
11451
|
-
)}`
|
|
11452
|
-
);
|
|
11453
|
-
}
|
|
11454
|
-
}
|
|
11455
|
-
}
|
|
11456
|
-
|
|
11457
11166
|
// src/agent/bundles/run-ratio.ts
|
|
11458
11167
|
var DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO = 4;
|
|
11459
11168
|
var DEFAULT_STATE_FILE_PATH = ".state/orchestrator-runs.json";
|
|
@@ -11540,23 +11249,18 @@ function renderRunRatioSection(ratio) {
|
|
|
11540
11249
|
"### Dispatch-run pipeline",
|
|
11541
11250
|
"",
|
|
11542
11251
|
"1. Phase A \u2014 startup (fetch + checkout default branch).",
|
|
11543
|
-
"2. Phase
|
|
11544
|
-
"
|
|
11545
|
-
"3. Phase C \u2014 triage unblock (resolve `Depends on:` chains).",
|
|
11546
|
-
"4. Phase E \u2014 queue scan (pick the top `PICK` line, run the scope",
|
|
11252
|
+
"2. Phase C \u2014 triage unblock (resolve `Depends on:` chains).",
|
|
11253
|
+
"3. Phase E \u2014 queue scan (pick the top `PICK` line, run the scope",
|
|
11547
11254
|
" gate, emit `NEXT_WORK_ITEM`).",
|
|
11548
|
-
"
|
|
11255
|
+
"4. Phase F \u2014 cleanup.",
|
|
11549
11256
|
"",
|
|
11550
11257
|
"### Housekeeping-run pipeline",
|
|
11551
11258
|
"",
|
|
11552
11259
|
"1. Phase A \u2014 startup.",
|
|
11553
|
-
"2. Phase
|
|
11554
|
-
"
|
|
11555
|
-
" PRs land cleanly).",
|
|
11556
|
-
"3. Phase B \u2014 batch PR review across every eligible open PR.",
|
|
11557
|
-
"4. Phase D \u2014 maintenance scan (stale detection, orphaned branches,",
|
|
11260
|
+
"2. Phase B \u2014 batch PR review across every eligible open PR.",
|
|
11261
|
+
"3. Phase D \u2014 maintenance scan (stale detection, orphaned branches,",
|
|
11558
11262
|
" needs-attention summary).",
|
|
11559
|
-
"
|
|
11263
|
+
"4. Phase F \u2014 cleanup.",
|
|
11560
11264
|
"",
|
|
11561
11265
|
"### Model recommendations",
|
|
11562
11266
|
"",
|
|
@@ -11683,7 +11387,7 @@ var DEFAULT_SCHEDULED_TASK_ENTRIES = [
|
|
|
11683
11387
|
recommendedModel: "sonnet",
|
|
11684
11388
|
enabled: false,
|
|
11685
11389
|
cron: null,
|
|
11686
|
-
description: "End-to-end pipeline manager:
|
|
11390
|
+
description: "End-to-end pipeline manager: triage, maintenance, queue scan, delegate the picked issue to the issue-worker, then cleanup."
|
|
11687
11391
|
},
|
|
11688
11392
|
// Tier 1 — research.
|
|
11689
11393
|
{
|
|
@@ -12309,6 +12013,14 @@ var DEFAULT_SOURCES_THRESHOLDS = {
|
|
|
12309
12013
|
smallMax: 2,
|
|
12310
12014
|
mediumMax: 5
|
|
12311
12015
|
};
|
|
12016
|
+
var DEFAULT_BUNDLE_OVERRIDES = {
|
|
12017
|
+
"req:write": {
|
|
12018
|
+
acceptanceCriteria: { smallMax: 3, mediumMax: 20 }
|
|
12019
|
+
},
|
|
12020
|
+
"bcm:scaffold": {
|
|
12021
|
+
acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
|
|
12022
|
+
}
|
|
12023
|
+
};
|
|
12312
12024
|
var DEFAULT_DECOMPOSITION_TEMPLATE = [
|
|
12313
12025
|
"## Scope gate: issue is too large to dispatch",
|
|
12314
12026
|
"",
|
|
@@ -12348,22 +12060,58 @@ function resolveScopeGate(config) {
|
|
|
12348
12060
|
DEFAULT_SOURCES_THRESHOLDS,
|
|
12349
12061
|
"sources"
|
|
12350
12062
|
);
|
|
12063
|
+
const bundleOverrides = resolveBundleOverrides(
|
|
12064
|
+
config?.bundleOverrides,
|
|
12065
|
+
DEFAULT_BUNDLE_OVERRIDES
|
|
12066
|
+
);
|
|
12351
12067
|
return {
|
|
12352
12068
|
enabled: config?.enabled ?? true,
|
|
12353
12069
|
acceptanceCriteria: ac,
|
|
12354
12070
|
sources,
|
|
12355
12071
|
autoFile: config?.autoFile ?? false,
|
|
12356
|
-
decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE
|
|
12072
|
+
decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE,
|
|
12073
|
+
bundleOverrides
|
|
12074
|
+
};
|
|
12075
|
+
}
|
|
12076
|
+
function resolveOverrideForLabels(gate, labels) {
|
|
12077
|
+
const overrideKeys = Object.keys(gate.bundleOverrides);
|
|
12078
|
+
if (overrideKeys.length === 0 || labels.length === 0) {
|
|
12079
|
+
return {
|
|
12080
|
+
acceptanceCriteria: gate.acceptanceCriteria,
|
|
12081
|
+
sources: gate.sources
|
|
12082
|
+
};
|
|
12083
|
+
}
|
|
12084
|
+
const sortedLabels = [...labels].sort((a, b) => a.localeCompare(b));
|
|
12085
|
+
const matchedLabel = sortedLabels.find(
|
|
12086
|
+
(label) => Object.prototype.hasOwnProperty.call(gate.bundleOverrides, label)
|
|
12087
|
+
);
|
|
12088
|
+
if (matchedLabel === void 0) {
|
|
12089
|
+
return {
|
|
12090
|
+
acceptanceCriteria: gate.acceptanceCriteria,
|
|
12091
|
+
sources: gate.sources
|
|
12092
|
+
};
|
|
12093
|
+
}
|
|
12094
|
+
const override = gate.bundleOverrides[matchedLabel];
|
|
12095
|
+
return {
|
|
12096
|
+
acceptanceCriteria: override.acceptanceCriteria ?? gate.acceptanceCriteria,
|
|
12097
|
+
sources: override.sources ?? gate.sources,
|
|
12098
|
+
matchedLabel
|
|
12357
12099
|
};
|
|
12358
12100
|
}
|
|
12359
12101
|
function validateScopeGateConfig(config) {
|
|
12360
12102
|
return resolveScopeGate(config);
|
|
12361
12103
|
}
|
|
12362
|
-
function classifyIssueScope(body, gate) {
|
|
12104
|
+
function classifyIssueScope(body, gate, labels = []) {
|
|
12363
12105
|
const acCount = countAcceptanceCriteria(body);
|
|
12364
12106
|
const sourcesCount = countSources(body);
|
|
12365
|
-
const
|
|
12366
|
-
|
|
12107
|
+
const effective = resolveOverrideForLabels(gate, labels);
|
|
12108
|
+
const scope = resolveScopeClass(acCount, sourcesCount, effective);
|
|
12109
|
+
return {
|
|
12110
|
+
scope,
|
|
12111
|
+
acCount,
|
|
12112
|
+
sourcesCount,
|
|
12113
|
+
matchedLabel: effective.matchedLabel
|
|
12114
|
+
};
|
|
12367
12115
|
}
|
|
12368
12116
|
function renderScopeGateSection(gate) {
|
|
12369
12117
|
const { acceptanceCriteria: ac, sources } = gate;
|
|
@@ -12439,6 +12187,38 @@ function renderScopeGateSection(gate) {
|
|
|
12439
12187
|
" is in place."
|
|
12440
12188
|
);
|
|
12441
12189
|
}
|
|
12190
|
+
const overrideEntries = Object.entries(gate.bundleOverrides);
|
|
12191
|
+
if (overrideEntries.length > 0) {
|
|
12192
|
+
lines.push(
|
|
12193
|
+
"",
|
|
12194
|
+
"### Per-phase-label thresholds",
|
|
12195
|
+
"",
|
|
12196
|
+
"Issues whose `type:*` or phase label matches an entry below are",
|
|
12197
|
+
"classified against the override's thresholds instead of the",
|
|
12198
|
+
"global `acceptance-criteria` / `sources` defaults. This",
|
|
12199
|
+
"accommodates **content-spec workflows** \u2014 issues whose AC list",
|
|
12200
|
+
"is the per-section content checklist for one cohesive document,",
|
|
12201
|
+
"not a phase-completion checklist that can be decomposed.",
|
|
12202
|
+
"",
|
|
12203
|
+
"| Phase label | AC `mediumMax` | Sources `mediumMax` |",
|
|
12204
|
+
"|-------------|----------------|---------------------|"
|
|
12205
|
+
);
|
|
12206
|
+
const sortedKeys = overrideEntries.map(([k]) => k).sort();
|
|
12207
|
+
for (const label of sortedKeys) {
|
|
12208
|
+
const override = gate.bundleOverrides[label];
|
|
12209
|
+
const acMax = override.acceptanceCriteria?.mediumMax;
|
|
12210
|
+
const sourcesMax = override.sources?.mediumMax;
|
|
12211
|
+
const acCell = acMax === void 0 ? "_(global)_" : `${acMax}`;
|
|
12212
|
+
const sourcesCell = sourcesMax === void 0 ? "_(global)_" : `${sourcesMax}`;
|
|
12213
|
+
lines.push(`| \`${label}\` | ${acCell} | ${sourcesCell} |`);
|
|
12214
|
+
}
|
|
12215
|
+
lines.push(
|
|
12216
|
+
"",
|
|
12217
|
+
"When an issue carries multiple labels that each match an entry",
|
|
12218
|
+
"in this table, the orchestrator picks the **first match in",
|
|
12219
|
+
"alphabetical order on the label name** \u2014 first-wins, deterministic."
|
|
12220
|
+
);
|
|
12221
|
+
}
|
|
12442
12222
|
lines.push(
|
|
12443
12223
|
"",
|
|
12444
12224
|
"### Decomposition-proposal template",
|
|
@@ -12455,12 +12235,21 @@ function renderScopeGateSection(gate) {
|
|
|
12455
12235
|
}
|
|
12456
12236
|
function renderScopeGateShellHelpers(gate) {
|
|
12457
12237
|
const { acceptanceCriteria: ac, sources } = gate;
|
|
12458
|
-
|
|
12238
|
+
const lines = [
|
|
12459
12239
|
"# Classify an issue body against the scope-gate thresholds.",
|
|
12460
12240
|
"# Reads the issue body from stdin. Echoes one of 'small',",
|
|
12461
12241
|
"# 'medium', or 'large' on stdout. Writes the observed counts to",
|
|
12462
12242
|
"# stderr as `ac=<n> sources=<n>` so the caller can embed them in",
|
|
12463
12243
|
"# the decomposition-proposal comment.",
|
|
12244
|
+
"#",
|
|
12245
|
+
"# Effective per-issue thresholds are read from the four",
|
|
12246
|
+
"# `_EFFECTIVE_*` shell variables. The caller is expected to set",
|
|
12247
|
+
"# them via `bundle_override_for()` before invoking scope_of();",
|
|
12248
|
+
"# defaults preserve the global thresholds.",
|
|
12249
|
+
`_EFFECTIVE_AC_SMALL_MAX="\${_EFFECTIVE_AC_SMALL_MAX:-${ac.smallMax}}"`,
|
|
12250
|
+
`_EFFECTIVE_AC_MEDIUM_MAX="\${_EFFECTIVE_AC_MEDIUM_MAX:-${ac.mediumMax}}"`,
|
|
12251
|
+
`_EFFECTIVE_SOURCES_SMALL_MAX="\${_EFFECTIVE_SOURCES_SMALL_MAX:-${sources.smallMax}}"`,
|
|
12252
|
+
`_EFFECTIVE_SOURCES_MEDIUM_MAX="\${_EFFECTIVE_SOURCES_MEDIUM_MAX:-${sources.mediumMax}}"`,
|
|
12464
12253
|
"scope_of() {",
|
|
12465
12254
|
" local body",
|
|
12466
12255
|
" body=$(cat)",
|
|
@@ -12489,15 +12278,79 @@ function renderScopeGateShellHelpers(gate) {
|
|
|
12489
12278
|
" END { print count }",
|
|
12490
12279
|
" ')",
|
|
12491
12280
|
` printf 'ac=%s sources=%s\\n' "$ac_count" "$sources_count" >&2`,
|
|
12492
|
-
|
|
12281
|
+
' if [ "$ac_count" -le "$_EFFECTIVE_AC_SMALL_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_SMALL_MAX" ]; then',
|
|
12493
12282
|
" echo small",
|
|
12494
|
-
|
|
12283
|
+
' elif [ "$ac_count" -le "$_EFFECTIVE_AC_MEDIUM_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_MEDIUM_MAX" ]; then',
|
|
12495
12284
|
" echo medium",
|
|
12496
12285
|
" else",
|
|
12497
12286
|
" echo large",
|
|
12498
12287
|
" fi",
|
|
12288
|
+
"}",
|
|
12289
|
+
"",
|
|
12290
|
+
"# Resolve per-phase-label threshold overrides. Reads a newline-",
|
|
12291
|
+
"# separated list of label names on stdin and echoes effective",
|
|
12292
|
+
"# threshold variable assignments (one per line) on stdout. The",
|
|
12293
|
+
"# caller `eval`s the output to set the four `_EFFECTIVE_*`",
|
|
12294
|
+
"# variables before calling `scope_of()`. Also echoes",
|
|
12295
|
+
"# `MATCHED_LABEL=<label>` (or `MATCHED_LABEL=`) so the caller can",
|
|
12296
|
+
"# report which override fired.",
|
|
12297
|
+
"#",
|
|
12298
|
+
"# Tie-breaking: when more than one label matches an override key,",
|
|
12299
|
+
"# the alphabetical first-wins rule applies \u2014 the same rule the",
|
|
12300
|
+
"# TypeScript classifier uses.",
|
|
12301
|
+
"bundle_override_for() {",
|
|
12302
|
+
` local default_ac_small=${ac.smallMax}`,
|
|
12303
|
+
` local default_ac_medium=${ac.mediumMax}`,
|
|
12304
|
+
` local default_sources_small=${sources.smallMax}`,
|
|
12305
|
+
` local default_sources_medium=${sources.mediumMax}`,
|
|
12306
|
+
" # Sort labels alphabetically so the first match is deterministic.",
|
|
12307
|
+
" local sorted_labels",
|
|
12308
|
+
" sorted_labels=$(LC_ALL=C sort)",
|
|
12309
|
+
" local matched_label=''",
|
|
12310
|
+
" local ac_small=$default_ac_small",
|
|
12311
|
+
" local ac_medium=$default_ac_medium",
|
|
12312
|
+
" local sources_small=$default_sources_small",
|
|
12313
|
+
" local sources_medium=$default_sources_medium",
|
|
12314
|
+
" while IFS= read -r label; do",
|
|
12315
|
+
' [ -z "$label" ] && continue',
|
|
12316
|
+
` case "$label" in`
|
|
12317
|
+
];
|
|
12318
|
+
const sortedKeys = Object.keys(gate.bundleOverrides).sort();
|
|
12319
|
+
if (sortedKeys.length === 0) {
|
|
12320
|
+
lines.push(" *) ;;");
|
|
12321
|
+
} else {
|
|
12322
|
+
for (const label of sortedKeys) {
|
|
12323
|
+
const override = gate.bundleOverrides[label];
|
|
12324
|
+
const acThresholds = override.acceptanceCriteria;
|
|
12325
|
+
const sourcesThresholds = override.sources;
|
|
12326
|
+
const branchLines = [` '${label}')`];
|
|
12327
|
+
if (acThresholds !== void 0) {
|
|
12328
|
+
branchLines.push(` ac_small=${acThresholds.smallMax}`);
|
|
12329
|
+
branchLines.push(` ac_medium=${acThresholds.mediumMax}`);
|
|
12330
|
+
}
|
|
12331
|
+
if (sourcesThresholds !== void 0) {
|
|
12332
|
+
branchLines.push(` sources_small=${sourcesThresholds.smallMax}`);
|
|
12333
|
+
branchLines.push(
|
|
12334
|
+
` sources_medium=${sourcesThresholds.mediumMax}`
|
|
12335
|
+
);
|
|
12336
|
+
}
|
|
12337
|
+
branchLines.push(` matched_label='${label}'`);
|
|
12338
|
+
branchLines.push(" break");
|
|
12339
|
+
branchLines.push(" ;;");
|
|
12340
|
+
lines.push(...branchLines);
|
|
12341
|
+
}
|
|
12342
|
+
}
|
|
12343
|
+
lines.push(
|
|
12344
|
+
" esac",
|
|
12345
|
+
` done <<<"$sorted_labels"`,
|
|
12346
|
+
` printf 'MATCHED_LABEL=%s\\n' "$matched_label"`,
|
|
12347
|
+
` printf '_EFFECTIVE_AC_SMALL_MAX=%s\\n' "$ac_small"`,
|
|
12348
|
+
` printf '_EFFECTIVE_AC_MEDIUM_MAX=%s\\n' "$ac_medium"`,
|
|
12349
|
+
` printf '_EFFECTIVE_SOURCES_SMALL_MAX=%s\\n' "$sources_small"`,
|
|
12350
|
+
` printf '_EFFECTIVE_SOURCES_MEDIUM_MAX=%s\\n' "$sources_medium"`,
|
|
12499
12351
|
"}"
|
|
12500
|
-
|
|
12352
|
+
);
|
|
12353
|
+
return lines.join("\n");
|
|
12501
12354
|
}
|
|
12502
12355
|
function resolveThresholds(supplied, defaults, field) {
|
|
12503
12356
|
const merged = {
|
|
@@ -12507,6 +12360,42 @@ function resolveThresholds(supplied, defaults, field) {
|
|
|
12507
12360
|
assertValidThresholds(merged, field);
|
|
12508
12361
|
return merged;
|
|
12509
12362
|
}
|
|
12363
|
+
function resolveBundleOverrides(supplied, defaults) {
|
|
12364
|
+
const out = {};
|
|
12365
|
+
for (const [label, override] of Object.entries(defaults)) {
|
|
12366
|
+
out[label] = override;
|
|
12367
|
+
}
|
|
12368
|
+
if (supplied !== void 0) {
|
|
12369
|
+
for (const label of Object.keys(supplied)) {
|
|
12370
|
+
const value = supplied[label];
|
|
12371
|
+
if (value === void 0) {
|
|
12372
|
+
delete out[label];
|
|
12373
|
+
continue;
|
|
12374
|
+
}
|
|
12375
|
+
const resolved = {};
|
|
12376
|
+
if (value.acceptanceCriteria !== void 0) {
|
|
12377
|
+
resolved.acceptanceCriteria = resolveThresholds(
|
|
12378
|
+
value.acceptanceCriteria,
|
|
12379
|
+
DEFAULT_AC_THRESHOLDS,
|
|
12380
|
+
"acceptanceCriteria"
|
|
12381
|
+
);
|
|
12382
|
+
}
|
|
12383
|
+
if (value.sources !== void 0) {
|
|
12384
|
+
resolved.sources = resolveThresholds(
|
|
12385
|
+
value.sources,
|
|
12386
|
+
DEFAULT_SOURCES_THRESHOLDS,
|
|
12387
|
+
"sources"
|
|
12388
|
+
);
|
|
12389
|
+
}
|
|
12390
|
+
if (resolved.acceptanceCriteria === void 0 && resolved.sources === void 0) {
|
|
12391
|
+
delete out[label];
|
|
12392
|
+
continue;
|
|
12393
|
+
}
|
|
12394
|
+
out[label] = resolved;
|
|
12395
|
+
}
|
|
12396
|
+
}
|
|
12397
|
+
return out;
|
|
12398
|
+
}
|
|
12510
12399
|
function assertValidThresholds(thresholds, field) {
|
|
12511
12400
|
const { smallMax, mediumMax } = thresholds;
|
|
12512
12401
|
for (const [key, value] of [
|
|
@@ -12530,8 +12419,8 @@ function assertValidThresholds(thresholds, field) {
|
|
|
12530
12419
|
);
|
|
12531
12420
|
}
|
|
12532
12421
|
}
|
|
12533
|
-
function resolveScopeClass(acCount, sourcesCount,
|
|
12534
|
-
const { acceptanceCriteria: ac, sources } =
|
|
12422
|
+
function resolveScopeClass(acCount, sourcesCount, thresholds) {
|
|
12423
|
+
const { acceptanceCriteria: ac, sources } = thresholds;
|
|
12535
12424
|
if (acCount <= ac.smallMax && sourcesCount <= sources.smallMax) {
|
|
12536
12425
|
return "small";
|
|
12537
12426
|
}
|
|
@@ -12778,9 +12667,6 @@ function renderUnblockDependentsSection(ud) {
|
|
|
12778
12667
|
"",
|
|
12779
12668
|
"- The `pr-reviewer` sub-agent, Phase 5 (branch cleanup), after it",
|
|
12780
12669
|
" applies `status:done` to the linked issue on a successful merge.",
|
|
12781
|
-
"- The `orchestrator` sub-agent, Phase B0 (pre-flight PR merge)",
|
|
12782
|
-
" post-merge verification, after it force-closes an issue whose",
|
|
12783
|
-
" merge commit dropped its closing keyword.",
|
|
12784
12670
|
"",
|
|
12785
12671
|
"Any other agent that manually applies `status:done` should call",
|
|
12786
12672
|
"`unblock-dependents.sh <closed-issue>` as its last step. The",
|
|
@@ -13086,12 +12972,11 @@ function shellSingleQuote(value) {
|
|
|
13086
12972
|
}
|
|
13087
12973
|
|
|
13088
12974
|
// src/agent/bundles/orchestrator.ts
|
|
13089
|
-
function buildCheckBlockedScript(tiers, scopeGate, runRatio
|
|
12975
|
+
function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
|
|
13090
12976
|
const tierCase = renderAgentTierCaseStatement(tiers);
|
|
13091
12977
|
const scopeHelper = renderScopeGateShellHelpers(scopeGate);
|
|
13092
12978
|
const scopeHelperIndented = scopeHelper.split("\n").map((line) => line.length > 0 ? line : "").join("\n");
|
|
13093
12979
|
const runRatioHelper = renderRunRatioShellHelpers(runRatio);
|
|
13094
|
-
const preflightHelper = renderPreflightPrShellHelpers(preflight);
|
|
13095
12980
|
return [
|
|
13096
12981
|
"#!/usr/bin/env bash",
|
|
13097
12982
|
"# check-blocked.sh \u2014 Token-efficient issue triage for agent loops.",
|
|
@@ -13106,7 +12991,6 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
|
|
|
13106
12991
|
"# .claude/procedures/check-blocked.sh prs",
|
|
13107
12992
|
"# .claude/procedures/check-blocked.sh scope <issue-number>",
|
|
13108
12993
|
"# .claude/procedures/check-blocked.sh tick",
|
|
13109
|
-
"# .claude/procedures/check-blocked.sh preflight",
|
|
13110
12994
|
"",
|
|
13111
12995
|
"set -uo pipefail",
|
|
13112
12996
|
"",
|
|
@@ -13116,15 +13000,13 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
|
|
|
13116
13000
|
"STALE_BLOCKED_HOURS=168",
|
|
13117
13001
|
`SCOPE_GATE_ENABLED=${scopeGate.enabled ? "1" : "0"}`,
|
|
13118
13002
|
`SCOPE_GATE_AUTO_FILE=${scopeGate.autoFile ? "1" : "0"}`,
|
|
13003
|
+
`SCOPE_AC_SMALL_MAX=${scopeGate.acceptanceCriteria.smallMax}`,
|
|
13119
13004
|
`SCOPE_AC_MEDIUM_MAX=${scopeGate.acceptanceCriteria.mediumMax}`,
|
|
13005
|
+
`SCOPE_SOURCES_SMALL_MAX=${scopeGate.sources.smallMax}`,
|
|
13120
13006
|
`SCOPE_SOURCES_MEDIUM_MAX=${scopeGate.sources.mediumMax}`,
|
|
13121
13007
|
`RUN_RATIO_ENABLED=${runRatio.enabled ? "1" : "0"}`,
|
|
13122
13008
|
`RUN_RATIO_DISPATCH_PER_HOUSEKEEPING=${runRatio.ratio}`,
|
|
13123
13009
|
`ORCHESTRATOR_STATE_FILE="${runRatio.stateFilePath}"`,
|
|
13124
|
-
`PREFLIGHT_ENABLED=${preflight.enabled ? "1" : "0"}`,
|
|
13125
|
-
`PREFLIGHT_REQUIRE_LINKED_ISSUE=${preflight.requireLinkedIssue ? "1" : "0"}`,
|
|
13126
|
-
`PREFLIGHT_MERGE_METHOD="${preflight.mergeMethod}"`,
|
|
13127
|
-
`PREFLIGHT_DELEGATE_TO_PR_REVIEWER=${preflight.delegateToPrReviewer ? "1" : "0"}`,
|
|
13128
13010
|
"",
|
|
13129
13011
|
"# \u2500\u2500 helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
13130
13012
|
"",
|
|
@@ -13157,8 +13039,6 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
|
|
|
13157
13039
|
"",
|
|
13158
13040
|
runRatioHelper,
|
|
13159
13041
|
"",
|
|
13160
|
-
preflightHelper,
|
|
13161
|
-
"",
|
|
13162
13042
|
"# \u2500\u2500 subcommands \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
13163
13043
|
"",
|
|
13164
13044
|
"cmd_unblock() {",
|
|
@@ -13476,12 +13356,45 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
|
|
|
13476
13356
|
' echo "SCOPE #${issue_num} disabled"',
|
|
13477
13357
|
" return 0",
|
|
13478
13358
|
" fi",
|
|
13359
|
+
" # Fetch the body and labels in a single API call so we resolve",
|
|
13360
|
+
" # any per-phase-label override against the issue's actual labels.",
|
|
13361
|
+
" local issue_json",
|
|
13362
|
+
' issue_json=$(gh issue view "$issue_num" --json body,labels 2>/dev/null || echo "")',
|
|
13363
|
+
' if [[ -z "$issue_json" ]]; then',
|
|
13364
|
+
' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
|
|
13365
|
+
" return 1",
|
|
13366
|
+
" fi",
|
|
13479
13367
|
" local body",
|
|
13480
|
-
` body=$(
|
|
13368
|
+
` body=$(printf '%s' "$issue_json" | jq -r '.body // ""')`,
|
|
13481
13369
|
' if [[ -z "$body" ]]; then',
|
|
13482
13370
|
' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
|
|
13483
13371
|
" return 1",
|
|
13484
13372
|
" fi",
|
|
13373
|
+
" local labels",
|
|
13374
|
+
` labels=$(printf '%s' "$issue_json" | jq -r '.labels[]?.name // empty')`,
|
|
13375
|
+
" # Resolve the effective thresholds. `bundle_override_for` reads",
|
|
13376
|
+
" # labels on stdin and emits `KEY=VALUE` assignments (one per line)",
|
|
13377
|
+
" # for the four `_EFFECTIVE_*` variables plus `MATCHED_LABEL`.",
|
|
13378
|
+
" local override_output",
|
|
13379
|
+
` override_output=$(printf '%s\\n' "$labels" | bundle_override_for)`,
|
|
13380
|
+
" local matched_label=''",
|
|
13381
|
+
" local _EFFECTIVE_AC_SMALL_MAX=$SCOPE_AC_SMALL_MAX",
|
|
13382
|
+
" local _EFFECTIVE_AC_MEDIUM_MAX=$SCOPE_AC_MEDIUM_MAX",
|
|
13383
|
+
" local _EFFECTIVE_SOURCES_SMALL_MAX=$SCOPE_SOURCES_SMALL_MAX",
|
|
13384
|
+
" local _EFFECTIVE_SOURCES_MEDIUM_MAX=$SCOPE_SOURCES_MEDIUM_MAX",
|
|
13385
|
+
" while IFS= read -r assignment; do",
|
|
13386
|
+
' [ -z "$assignment" ] && continue',
|
|
13387
|
+
' case "$assignment" in',
|
|
13388
|
+
" MATCHED_LABEL=*) matched_label=${assignment#MATCHED_LABEL=} ;;",
|
|
13389
|
+
" _EFFECTIVE_AC_SMALL_MAX=*) _EFFECTIVE_AC_SMALL_MAX=${assignment#_EFFECTIVE_AC_SMALL_MAX=} ;;",
|
|
13390
|
+
" _EFFECTIVE_AC_MEDIUM_MAX=*) _EFFECTIVE_AC_MEDIUM_MAX=${assignment#_EFFECTIVE_AC_MEDIUM_MAX=} ;;",
|
|
13391
|
+
" _EFFECTIVE_SOURCES_SMALL_MAX=*) _EFFECTIVE_SOURCES_SMALL_MAX=${assignment#_EFFECTIVE_SOURCES_SMALL_MAX=} ;;",
|
|
13392
|
+
" _EFFECTIVE_SOURCES_MEDIUM_MAX=*) _EFFECTIVE_SOURCES_MEDIUM_MAX=${assignment#_EFFECTIVE_SOURCES_MEDIUM_MAX=} ;;",
|
|
13393
|
+
" esac",
|
|
13394
|
+
' done <<<"$override_output"',
|
|
13395
|
+
" # Export the effective thresholds so scope_of() picks them up.",
|
|
13396
|
+
" export _EFFECTIVE_AC_SMALL_MAX _EFFECTIVE_AC_MEDIUM_MAX",
|
|
13397
|
+
" export _EFFECTIVE_SOURCES_SMALL_MAX _EFFECTIVE_SOURCES_MEDIUM_MAX",
|
|
13485
13398
|
" local scope counts",
|
|
13486
13399
|
" # scope_of writes counts to stderr (ac=N sources=N); capture both.",
|
|
13487
13400
|
` scope=$(printf '%s' "$body" | scope_of 2> >(read -r counts; echo "$counts" >&2))`,
|
|
@@ -13495,9 +13408,17 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
|
|
|
13495
13408
|
` sources_count=$(echo "$stderr_capture" | grep -oE 'sources=[0-9]+' | head -1 | cut -d= -f2)`,
|
|
13496
13409
|
" ac_count=${ac_count:-0}",
|
|
13497
13410
|
" sources_count=${sources_count:-0}",
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
'
|
|
13411
|
+
' if [ -n "$matched_label" ]; then',
|
|
13412
|
+
" printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s matched_label=%s\\n' \\",
|
|
13413
|
+
' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
|
|
13414
|
+
' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
|
|
13415
|
+
' "$SCOPE_GATE_AUTO_FILE" "$matched_label"',
|
|
13416
|
+
" else",
|
|
13417
|
+
" printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s\\n' \\",
|
|
13418
|
+
' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
|
|
13419
|
+
' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
|
|
13420
|
+
' "$SCOPE_GATE_AUTO_FILE"',
|
|
13421
|
+
" fi",
|
|
13501
13422
|
"}",
|
|
13502
13423
|
"",
|
|
13503
13424
|
"cmd_tick() {",
|
|
@@ -13523,26 +13444,24 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
|
|
|
13523
13444
|
' prs) shift; cmd_prs "$@" ;;',
|
|
13524
13445
|
' scope) shift; cmd_scope "$@" ;;',
|
|
13525
13446
|
' tick) shift; cmd_tick "$@" ;;',
|
|
13526
|
-
' preflight) shift; cmd_preflight "$@" ;;',
|
|
13527
13447
|
" help|*)",
|
|
13528
|
-
' echo "Usage: check-blocked.sh <unblock|eligible|stale|orphaned|prs|scope|tick
|
|
13448
|
+
' echo "Usage: check-blocked.sh <unblock|eligible|stale|orphaned|prs|scope|tick>"',
|
|
13529
13449
|
" exit 1",
|
|
13530
13450
|
" ;;",
|
|
13531
13451
|
"esac"
|
|
13532
13452
|
].join("\n");
|
|
13533
13453
|
}
|
|
13534
|
-
function buildCheckBlockedProcedure(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio()
|
|
13454
|
+
function buildCheckBlockedProcedure(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio()) {
|
|
13535
13455
|
return {
|
|
13536
13456
|
name: "check-blocked.sh",
|
|
13537
|
-
description: "Token-efficient issue triage script with subcommands: eligible, unblock, stale, orphaned, prs, scope,
|
|
13538
|
-
content: buildCheckBlockedScript(tiers, scopeGate, runRatio
|
|
13457
|
+
description: "Token-efficient issue triage script with subcommands: eligible, unblock, stale, orphaned, prs, scope, and (deprecated) tick. Sorts eligible issues by priority desc \u2192 funnel tier asc \u2192 issue number asc; the scope subcommand classifies a single issue against the scope-gate thresholds. The tick subcommand is a deprecation no-op retained for one release (the orchestrator no longer maintains a dispatch/housekeeping run counter).",
|
|
13458
|
+
content: buildCheckBlockedScript(tiers, scopeGate, runRatio)
|
|
13539
13459
|
};
|
|
13540
13460
|
}
|
|
13541
13461
|
var checkBlockedProcedure = buildCheckBlockedProcedure(
|
|
13542
13462
|
DEFAULT_AGENT_TIERS,
|
|
13543
13463
|
resolveScopeGate(),
|
|
13544
|
-
resolveRunRatio()
|
|
13545
|
-
resolvePreflightPr()
|
|
13464
|
+
resolveRunRatio()
|
|
13546
13465
|
);
|
|
13547
13466
|
function buildUnblockDependentsProcedure(unblockDependents = resolveUnblockDependents()) {
|
|
13548
13467
|
return {
|
|
@@ -13556,7 +13475,7 @@ var unblockDependentsProcedure = buildUnblockDependentsProcedure(
|
|
|
13556
13475
|
);
|
|
13557
13476
|
var orchestratorSubAgent = {
|
|
13558
13477
|
name: "orchestrator",
|
|
13559
|
-
description: "End-to-end pipeline manager that runs one full cycle every invocation:
|
|
13478
|
+
description: "End-to-end pipeline manager that runs one full cycle every invocation: triage \u2192 maintenance \u2192 queue scan \u2192 delegate the picked issue to the issue-worker \u2192 cleanup",
|
|
13560
13479
|
model: AGENT_MODEL.POWERFUL,
|
|
13561
13480
|
maxTurns: 100,
|
|
13562
13481
|
platforms: { cursor: { exclude: true } },
|
|
@@ -13564,12 +13483,13 @@ var orchestratorSubAgent = {
|
|
|
13564
13483
|
"# Orchestrator Agent",
|
|
13565
13484
|
"",
|
|
13566
13485
|
"You are the pipeline orchestrator for the **{{repository.owner}}/{{repository.name}}** repository.",
|
|
13567
|
-
"Each invocation runs **one full end-to-end cycle**. The cycle
|
|
13568
|
-
"
|
|
13569
|
-
"
|
|
13570
|
-
"
|
|
13571
|
-
"
|
|
13572
|
-
"
|
|
13486
|
+
"Each invocation runs **one full end-to-end cycle**. The cycle triages",
|
|
13487
|
+
"blocked issues, runs maintenance scans, picks the next ready issue, and",
|
|
13488
|
+
"**delegates implementation to the `issue-worker`** sub-agent in scheduled",
|
|
13489
|
+
"mode. The orchestrator itself never implements code, creates branches, or",
|
|
13490
|
+
"pushes commits \u2014 it routes work to other agents. Approved-PR merging is",
|
|
13491
|
+
"owned by the `pr-reviewer` sub-agent (invoked via `/review-pr` /",
|
|
13492
|
+
"`/review-prs`); the orchestrator does not run a merge sweep.",
|
|
13573
13493
|
"",
|
|
13574
13494
|
"Run the phases below in order on every invocation. There is no",
|
|
13575
13495
|
"dispatch/housekeeping fork \u2014 every phase runs every time.",
|
|
@@ -13577,20 +13497,23 @@ var orchestratorSubAgent = {
|
|
|
13577
13497
|
"Phase ordering (each runs exactly once per invocation):",
|
|
13578
13498
|
"",
|
|
13579
13499
|
"1. **Phase A \u2014 Startup.** Pull the default branch.",
|
|
13580
|
-
"2. **Phase
|
|
13581
|
-
" CI-green PR before triage reads dependency state.",
|
|
13582
|
-
"3. **Phase C \u2014 Triage / Unblock.** Flip dependents that have all",
|
|
13500
|
+
"2. **Phase C \u2014 Triage / Unblock.** Flip dependents that have all",
|
|
13583
13501
|
" their dependencies closed back to `status:ready`.",
|
|
13584
|
-
"
|
|
13502
|
+
"3. **Phase D \u2014 Maintenance.** Stale-issue and orphaned-resource scan",
|
|
13585
13503
|
" plus a needs-attention summary.",
|
|
13586
|
-
"
|
|
13504
|
+
"4. **Phase E \u2014 Queue Scan.** Pick the highest-priority `PICK` line",
|
|
13587
13505
|
" and run the scope gate. If the result is `large`, flag and stop.",
|
|
13588
|
-
"
|
|
13506
|
+
"5. **Phase G \u2014 Delegate Implementation.** Hand the picked issue off",
|
|
13589
13507
|
" to the `issue-worker` sub-agent in scheduled mode. The worker",
|
|
13590
13508
|
" handles claim \u2192 branch \u2192 implement \u2192 commit \u2192 PR autonomously.",
|
|
13591
|
-
"
|
|
13509
|
+
"6. **Phase F \u2014 Cleanup.** Return to the default branch and log the",
|
|
13592
13510
|
" run summary.",
|
|
13593
13511
|
"",
|
|
13512
|
+
"Phase letters are stable identifiers \u2014 letters skipped in the list",
|
|
13513
|
+
"above were used by earlier revisions of this pipeline and the gap",
|
|
13514
|
+
"is left intentionally so existing references in commit history and",
|
|
13515
|
+
"docs remain unambiguous.",
|
|
13516
|
+
"",
|
|
13594
13517
|
"---",
|
|
13595
13518
|
"",
|
|
13596
13519
|
...PROJECT_CONTEXT_READER_SECTION,
|
|
@@ -13602,100 +13525,6 @@ var orchestratorSubAgent = {
|
|
|
13602
13525
|
"git checkout main && git pull origin main",
|
|
13603
13526
|
"```",
|
|
13604
13527
|
"",
|
|
13605
|
-
"## Phase B0: Pre-flight PR Merge",
|
|
13606
|
-
"",
|
|
13607
|
-
"Before any other phase reads dependency state, land every",
|
|
13608
|
-
"eligible PR. The sweep is idempotent \u2014 `gh pr merge` on an",
|
|
13609
|
-
"already-merged PR is a no-op \u2014 so re-running on back-to-back",
|
|
13610
|
-
"invocations is safe.",
|
|
13611
|
-
"",
|
|
13612
|
-
"List eligibility:",
|
|
13613
|
-
"",
|
|
13614
|
-
"```bash",
|
|
13615
|
-
".claude/procedures/check-blocked.sh preflight",
|
|
13616
|
-
"```",
|
|
13617
|
-
"",
|
|
13618
|
-
"Expect one line per open PR:",
|
|
13619
|
-
"",
|
|
13620
|
-
"```",
|
|
13621
|
-
'PREFLIGHT_MERGE PR #<n> issue:#<m> branch:<b> \u2014 "<title>"',
|
|
13622
|
-
'PREFLIGHT_SKIP PR #<n> \u2014 <reason> \u2014 "<title>"',
|
|
13623
|
-
"NO_PREFLIGHT_PRS",
|
|
13624
|
-
"```",
|
|
13625
|
-
"",
|
|
13626
|
-
"If the output is `NO_PREFLIGHT_PRS`, continue to Phase B / C.",
|
|
13627
|
-
"",
|
|
13628
|
-
"For each `PREFLIGHT_MERGE` line, either **delegate** to the",
|
|
13629
|
-
"`pr-reviewer` sub-agent (default) or **merge inline** depending",
|
|
13630
|
-
"on the rendered pre-flight config. See the **Pre-flight PR",
|
|
13631
|
-
"merge** section in `CLAUDE.md` for the project's mode.",
|
|
13632
|
-
"",
|
|
13633
|
-
"**Delegate mode (default).** Invoke the `pr-reviewer` sub-agent",
|
|
13634
|
-
"with a single-pass brief covering every `PREFLIGHT_MERGE` line:",
|
|
13635
|
-
"",
|
|
13636
|
-
"> Run your merge pipeline once across these PRs: <list of",
|
|
13637
|
-
"> `#<n>` numbers>. Merge every eligible PR (linked issue still",
|
|
13638
|
-
"> open, AC satisfied, CI green, approval present) and comment on",
|
|
13639
|
-
"> any PR you skip. Return a one-line summary as the final line of",
|
|
13640
|
-
"> your response, e.g. `Merged 2 (#123, #124), skipped 1 (#125 \u2014",
|
|
13641
|
-
"> AC not met), 0 eligible left.` or `No eligible PRs.`",
|
|
13642
|
-
"",
|
|
13643
|
-
"If the sub-agent's final line is missing, malformed, or indicates",
|
|
13644
|
-
"an error, **do not retry** \u2014 proceed to the next phase anyway.",
|
|
13645
|
-
"The next orchestrator session will re-run pre-flight and pick up",
|
|
13646
|
-
"anything left behind.",
|
|
13647
|
-
"",
|
|
13648
|
-
"**Inline mode.** For each `PREFLIGHT_MERGE PR #<n> issue:#<m>`",
|
|
13649
|
-
"line, merge the PR with the configured method (default",
|
|
13650
|
-
"`--squash`) and `--delete-branch`:",
|
|
13651
|
-
"",
|
|
13652
|
-
"```bash",
|
|
13653
|
-
'gh pr merge <n> --squash --delete-branch --subject "<conventional-commit-title>" --body "<extended-description>"',
|
|
13654
|
-
"```",
|
|
13655
|
-
"",
|
|
13656
|
-
"After every successful merge \u2014 whether delegated or inline \u2014 run",
|
|
13657
|
-
"the post-merge verification step to close the stale-dependency",
|
|
13658
|
-
"loop:",
|
|
13659
|
-
"",
|
|
13660
|
-
"```bash",
|
|
13661
|
-
"gh issue view <m> --json state --jq '.state'",
|
|
13662
|
-
"```",
|
|
13663
|
-
"",
|
|
13664
|
-
"If the linked issue's state is still `OPEN`, close it explicitly",
|
|
13665
|
-
"and flip its status label to `status:done`:",
|
|
13666
|
-
"",
|
|
13667
|
-
"```bash",
|
|
13668
|
-
"gh issue close <m>",
|
|
13669
|
-
'gh issue edit <m> --remove-label "status:ready-for-review" --add-label "status:done"',
|
|
13670
|
-
"```",
|
|
13671
|
-
"",
|
|
13672
|
-
"After applying `status:done` \u2014 whether the auto-close fired or",
|
|
13673
|
-
"you force-closed above \u2014 run the targeted unblock sweep so any",
|
|
13674
|
-
"dependents that were only waiting on issue `<m>` flip to",
|
|
13675
|
-
"`status:ready` immediately:",
|
|
13676
|
-
"",
|
|
13677
|
-
"```bash",
|
|
13678
|
-
".claude/procedures/unblock-dependents.sh <m>",
|
|
13679
|
-
"```",
|
|
13680
|
-
"",
|
|
13681
|
-
"The script emits one line per processed dependent",
|
|
13682
|
-
"(`UNBLOCKED #<n>` / `STILL_BLOCKED #<n>` / `NO_DEPENDENTS #<m>`)",
|
|
13683
|
-
"so the run summary records what transitioned. See the",
|
|
13684
|
-
"**Agent-driven unblocking** section in `CLAUDE.md` for the full",
|
|
13685
|
-
"contract.",
|
|
13686
|
-
"",
|
|
13687
|
-
"This step is the reason pre-flight exists: it prevents the",
|
|
13688
|
-
"orchestrator from reading a merged-but-not-auto-closed issue as",
|
|
13689
|
-
"still blocking on the next unblock sweep.",
|
|
13690
|
-
"",
|
|
13691
|
-
"Log the pre-flight outcome (number merged, PRs skipped with",
|
|
13692
|
-
"reasons, and any issues force-closed) so the run summary records",
|
|
13693
|
-
"what landed.",
|
|
13694
|
-
"",
|
|
13695
|
-
"Skip lines starting with `PREFLIGHT_SKIP` \u2014 those PRs failed one",
|
|
13696
|
-
"or more eligibility checks. The `pr-reviewer` or a human",
|
|
13697
|
-
"operator picks them up on the normal review path.",
|
|
13698
|
-
"",
|
|
13699
13528
|
"## Phase C: Triage \u2014 Unblock",
|
|
13700
13529
|
"",
|
|
13701
13530
|
"Check for blocked issues whose dependencies have resolved:",
|
|
@@ -13794,9 +13623,16 @@ var orchestratorSubAgent = {
|
|
|
13794
13623
|
"The output is one line of the form:",
|
|
13795
13624
|
"",
|
|
13796
13625
|
"```",
|
|
13797
|
-
"SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1>",
|
|
13626
|
+
"SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1> [matched_label=<label>]",
|
|
13798
13627
|
"```",
|
|
13799
13628
|
"",
|
|
13629
|
+
"When `matched_label=<label>` is present, the scope gate applied a",
|
|
13630
|
+
"per-phase-label override (e.g. `req:write` raises the AC ceiling to",
|
|
13631
|
+
"20 for content-spec workflows whose AC list is the per-section",
|
|
13632
|
+
"checklist for one document). The `ac_limit` / `sources_limit`",
|
|
13633
|
+
"values reflect the override; the global thresholds are documented",
|
|
13634
|
+
"in the **Scope gate** section in `CLAUDE.md`.",
|
|
13635
|
+
"",
|
|
13800
13636
|
"If the scope is `small` or `medium`, continue \u2014 the issue is",
|
|
13801
13637
|
"dispatchable. Record the first `PICK` line as the next work item",
|
|
13802
13638
|
"and proceed to Phase G:",
|
|
@@ -13891,40 +13727,31 @@ var orchestratorSubAgent = {
|
|
|
13891
13727
|
"git fetch --prune origin",
|
|
13892
13728
|
"```",
|
|
13893
13729
|
"",
|
|
13894
|
-
"Log completion: phases executed,
|
|
13895
|
-
"
|
|
13896
|
-
"
|
|
13897
|
-
"
|
|
13730
|
+
"Log completion: phases executed, issues unblocked, stale issues",
|
|
13731
|
+
"flagged, the next work item picked, and the outcome of Phase G's",
|
|
13732
|
+
"delegation (worker success / failure / skipped because the queue",
|
|
13733
|
+
"was empty).",
|
|
13898
13734
|
"",
|
|
13899
13735
|
"---",
|
|
13900
13736
|
"",
|
|
13901
13737
|
"## Rules",
|
|
13902
13738
|
"",
|
|
13903
|
-
"1. **Never implement code.** You
|
|
13739
|
+
"1. **Never implement code.** You triage, scan, and delegate \u2014 you do not code.",
|
|
13904
13740
|
"2. **Never claim issues.** Do not add `status:in-progress` or create branches for issues. Phase G's `issue-worker` delegation is the only path that flips an issue's claim labels.",
|
|
13905
|
-
"3. **
|
|
13906
|
-
"4. **
|
|
13907
|
-
"5. **
|
|
13908
|
-
"6. **
|
|
13741
|
+
"3. **Never merge PRs.** Approved-PR merging is owned by the `pr-reviewer` sub-agent (invoked via `/review-pr` / `/review-prs`). The orchestrator does not run a merge sweep, does not call `gh pr merge`, and does not enable auto-merge.",
|
|
13742
|
+
"4. **Always use check-blocked.sh.** All triage queries go through the shell script for token efficiency.",
|
|
13743
|
+
"5. **Follow CLAUDE.md conventions** for all git and gh operations.",
|
|
13744
|
+
"6. **Priority order:** critical > high > medium > low > trivial, then **funnel tier asc** (lower tier wins ties), then FIFO by issue number.",
|
|
13745
|
+
"7. **Never dispatch a `large` issue.** Always run the scope check",
|
|
13909
13746
|
" on the top `PICK` line before reporting `NEXT_WORK_ITEM`. A",
|
|
13910
13747
|
" `large` issue must be flagged with `status:needs-attention` and",
|
|
13911
13748
|
" handed a decomposition proposal \u2014 never claimed, never branched,",
|
|
13912
13749
|
" never delegated to the `issue-worker`.",
|
|
13913
|
-
"7. **Always run pre-flight before reading dependency state.**",
|
|
13914
|
-
" Phase B0 (Pre-flight PR Merge) runs on every invocation,",
|
|
13915
|
-
" immediately after Phase A. Never skip Phase B0: without it, an",
|
|
13916
|
-
" issue whose only remaining blocker is an approved-but-not-yet-",
|
|
13917
|
-
" merged PR reads as blocked on every cycle and the pipeline",
|
|
13918
|
-
" thrashes. See the **Pre-flight PR merge** section in",
|
|
13919
|
-
" `CLAUDE.md` for eligibility rules and the delegate-vs-inline",
|
|
13920
|
-
" modes.",
|
|
13921
13750
|
"8. **Sweep dependents whenever you apply `status:done`.** Every",
|
|
13922
13751
|
" `status:done` transition must be immediately followed by",
|
|
13923
13752
|
" `.claude/procedures/unblock-dependents.sh <n>` so downstream",
|
|
13924
13753
|
" `Depends on: #<n>` issues flip to `status:ready` without",
|
|
13925
|
-
" waiting for the next cycle.
|
|
13926
|
-
" verification (and any inline-mode merge) already calls the",
|
|
13927
|
-
" sweep; don't skip it. See the **Agent-driven unblocking**",
|
|
13754
|
+
" waiting for the next cycle. See the **Agent-driven unblocking**",
|
|
13928
13755
|
" section in `CLAUDE.md` for the contract.",
|
|
13929
13756
|
"9. **Always invoke `issue-worker` in scheduled mode.** Phase G's",
|
|
13930
13757
|
" delegation prompt must contain the literal phrase",
|
|
@@ -13938,7 +13765,7 @@ var issueWorkerSubAgent = {
|
|
|
13938
13765
|
name: "issue-worker",
|
|
13939
13766
|
description: "Selects the next ready issue from the queue, claims it, and implements the change end-to-end following repository conventions",
|
|
13940
13767
|
model: AGENT_MODEL.POWERFUL,
|
|
13941
|
-
maxTurns:
|
|
13768
|
+
maxTurns: 150,
|
|
13942
13769
|
platforms: { cursor: { exclude: true } },
|
|
13943
13770
|
prompt: [
|
|
13944
13771
|
"# Issue Worker",
|
|
@@ -14167,6 +13994,35 @@ var issueWorkerSubAgent = {
|
|
|
14167
13994
|
" `file` (and optional `line`). Track `comment_id` per item so you can",
|
|
14168
13995
|
" report which items were handled and which (if any) failed.",
|
|
14169
13996
|
"",
|
|
13997
|
+
" **Synthetic rebase items.** A `comment_id` of",
|
|
13998
|
+
" `synthetic:rebase-behind-main` is the reviewer's signal that the",
|
|
13999
|
+
" PR's head branch is BEHIND the default branch with merge conflicts",
|
|
14000
|
+
" that `gh pr update-branch` could not resolve. For this item only,",
|
|
14001
|
+
" the work is **not** an editorial change \u2014 it is a rebase plus",
|
|
14002
|
+
" conflict resolution. Run the following sequence:",
|
|
14003
|
+
"",
|
|
14004
|
+
" ```bash",
|
|
14005
|
+
" git fetch origin",
|
|
14006
|
+
" git pull --rebase origin {{repository.defaultBranch}}",
|
|
14007
|
+
" ```",
|
|
14008
|
+
"",
|
|
14009
|
+
" When the rebase produces conflicts, resolve each conflicting file",
|
|
14010
|
+
" in turn (read both sides, reconcile, stage), then run",
|
|
14011
|
+
" `git rebase --continue` until the rebase completes. Never run",
|
|
14012
|
+
" `git rebase --abort` on a synthetic-rebase item \u2014 aborting drops",
|
|
14013
|
+
" the work the reviewer delegated. If you cannot resolve a conflict",
|
|
14014
|
+
" (the change is editorial enough that it requires human judgement,",
|
|
14015
|
+
" or the conflict touches a generated file you should not hand-edit),",
|
|
14016
|
+
" record the item as `failed` with a clear reason, run",
|
|
14017
|
+
" `git rebase --abort`, and proceed to the report step. Do not",
|
|
14018
|
+
" force-push.",
|
|
14019
|
+
"",
|
|
14020
|
+
" When the rebase completes cleanly, treat the rebased commits",
|
|
14021
|
+
" themselves as the deliverable. Skip the conventional commit step",
|
|
14022
|
+
" in Phase 6 for this item \u2014 the rebase already produced the commit",
|
|
14023
|
+
" history. Push with a regular non-force `git push origin <branch>`",
|
|
14024
|
+
" and report the rebased head SHA as the worker's commit.",
|
|
14025
|
+
"",
|
|
14170
14026
|
"4. When complete, prepare a short structured report (PR number, commit",
|
|
14171
14027
|
" SHAs you will push, items handled by `comment_id`, items that failed",
|
|
14172
14028
|
" to apply) \u2014 you will return this after Phase 6.",
|
|
@@ -14228,6 +14084,22 @@ var issueWorkerSubAgent = {
|
|
|
14228
14084
|
"git push origin <branch-name>",
|
|
14229
14085
|
"```",
|
|
14230
14086
|
"",
|
|
14087
|
+
"**Synthetic-rebase items skip the `fix(review)` commit.** When the",
|
|
14088
|
+
"fix-list contained a `comment_id` of `synthetic:rebase-behind-main`",
|
|
14089
|
+
"and you completed the rebase in Phase 4, the rebased commit history",
|
|
14090
|
+
"is itself the deliverable \u2014 there is no per-item editorial change to",
|
|
14091
|
+
"wrap in a `fix(review)` commit. Skip `git add` / `git commit` /",
|
|
14092
|
+
"`git pull --rebase` for that item and push the rebased branch",
|
|
14093
|
+
"directly:",
|
|
14094
|
+
"",
|
|
14095
|
+
"```bash",
|
|
14096
|
+
"git push origin <branch-name>",
|
|
14097
|
+
"```",
|
|
14098
|
+
"",
|
|
14099
|
+
"If the fix-list mixed a synthetic-rebase item with editorial items,",
|
|
14100
|
+
"perform the editorial commit first (the rebase already preceded it",
|
|
14101
|
+
"in Phase 4), then push once at the end.",
|
|
14102
|
+
"",
|
|
14231
14103
|
"**Normal mode:** Use conventional commit messages and push with `-u`",
|
|
14232
14104
|
"to set up branch tracking:",
|
|
14233
14105
|
"",
|
|
@@ -14361,8 +14233,8 @@ var ORCHESTRATOR_CONVENTIONS_PREAMBLE = [
|
|
|
14361
14233
|
"",
|
|
14362
14234
|
"When running the orchestrator agent (`.claude/agents/orchestrator.md`):",
|
|
14363
14235
|
"",
|
|
14364
|
-
"- The orchestrator runs **one full end-to-end cycle every invocation**:
|
|
14365
|
-
"- The orchestrator **never** implements code, creates branches,
|
|
14236
|
+
"- The orchestrator runs **one full end-to-end cycle every invocation**: triage / unblock \u2192 maintenance \u2192 queue scan \u2192 delegate to `issue-worker` \u2192 cleanup",
|
|
14237
|
+
"- The orchestrator **never** implements code, creates branches, pushes commits, **or merges PRs** \u2014 it triages issues, picks the next work item, and delegates implementation to other sub-agents. Approved-PR merging is owned by the `pr-reviewer` sub-agent (invoked via `/review-pr` / `/review-prs`).",
|
|
14366
14238
|
"- All triage queries use `.claude/procedures/check-blocked.sh` for token efficiency",
|
|
14367
14239
|
"- The queue scan reads only `priority:*` and `status:*` labels \u2014 type-routing (which typed agent handles a given `type:*` label) is the `issue-worker`'s concern, not the orchestrator's. The orchestrator's funnel-tier sort is a tie-breaker on `priority:*`, not a routing decision.",
|
|
14368
14240
|
"- Priority order: critical > high > medium > low > trivial, then **funnel tier asc** (lower tier wins ties), then FIFO by issue number",
|
|
@@ -14375,7 +14247,7 @@ var ORCHESTRATOR_CONVENTIONS_PREAMBLE = [
|
|
|
14375
14247
|
"",
|
|
14376
14248
|
'Practical implication for scheduled-task wiring: the `worker-orchestrator` scheduled task\'s `SKILL.md` instructs the **scheduled-task session itself** to read `.claude/agents/orchestrator.md` and execute its phase pipeline in-session, then use the `Agent` tool to delegate the picked work item to `issue-worker` (depth-1). The scheduled task does **not** call `Agent(subagent_type: "orchestrator")` \u2014 that would put the orchestrator at depth-1 and break the chain.'
|
|
14377
14249
|
].join("\n");
|
|
14378
|
-
function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio(),
|
|
14250
|
+
function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio(), scheduledTasks = resolveScheduledTasks(), unblockDependents = resolveUnblockDependents()) {
|
|
14379
14251
|
return [
|
|
14380
14252
|
ORCHESTRATOR_CONVENTIONS_PREAMBLE,
|
|
14381
14253
|
"",
|
|
@@ -14385,18 +14257,15 @@ function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate
|
|
|
14385
14257
|
"",
|
|
14386
14258
|
renderRunRatioSection(runRatio),
|
|
14387
14259
|
"",
|
|
14388
|
-
renderPreflightPrSection(preflight),
|
|
14389
|
-
"",
|
|
14390
14260
|
renderScheduledTasksSection(scheduledTasks),
|
|
14391
14261
|
"",
|
|
14392
14262
|
renderUnblockDependentsSection(unblockDependents)
|
|
14393
14263
|
].join("\n");
|
|
14394
14264
|
}
|
|
14395
|
-
function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig,
|
|
14265
|
+
function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig, scheduledTasksConfig, unblockDependentsConfig) {
|
|
14396
14266
|
const tiers = resolveAgentTiers(tierConfig);
|
|
14397
14267
|
const scopeGate = resolveScopeGate(scopeGateConfig);
|
|
14398
14268
|
const runRatio = resolveRunRatio(runRatioConfig);
|
|
14399
|
-
const preflight = resolvePreflightPr(preflightPrConfig);
|
|
14400
14269
|
const scheduledTasks = resolveScheduledTasks(scheduledTasksConfig);
|
|
14401
14270
|
const unblockDependentsResolved = resolveUnblockDependents(
|
|
14402
14271
|
unblockDependentsConfig
|
|
@@ -14405,23 +14274,16 @@ function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig,
|
|
|
14405
14274
|
tiers,
|
|
14406
14275
|
scopeGate,
|
|
14407
14276
|
runRatio,
|
|
14408
|
-
preflight,
|
|
14409
14277
|
scheduledTasks,
|
|
14410
14278
|
unblockDependents: unblockDependentsResolved,
|
|
14411
14279
|
conventionsContent: buildOrchestratorConventionsContent(
|
|
14412
14280
|
tiers,
|
|
14413
14281
|
scopeGate,
|
|
14414
14282
|
runRatio,
|
|
14415
|
-
preflight,
|
|
14416
14283
|
scheduledTasks,
|
|
14417
14284
|
unblockDependentsResolved
|
|
14418
14285
|
),
|
|
14419
|
-
procedure: buildCheckBlockedProcedure(
|
|
14420
|
-
tiers,
|
|
14421
|
-
scopeGate,
|
|
14422
|
-
runRatio,
|
|
14423
|
-
preflight
|
|
14424
|
-
),
|
|
14286
|
+
procedure: buildCheckBlockedProcedure(tiers, scopeGate, runRatio),
|
|
14425
14287
|
unblockDependentsProcedure: buildUnblockDependentsProcedure(
|
|
14426
14288
|
unblockDependentsResolved
|
|
14427
14289
|
)
|
|
@@ -14435,13 +14297,12 @@ var orchestratorBundle = {
|
|
|
14435
14297
|
rules: [
|
|
14436
14298
|
{
|
|
14437
14299
|
name: "orchestrator-conventions",
|
|
14438
|
-
description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate,
|
|
14300
|
+
description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, and per-agent scheduled-task layout",
|
|
14439
14301
|
scope: AGENT_RULE_SCOPE.ALWAYS,
|
|
14440
14302
|
content: buildOrchestratorConventionsContent(
|
|
14441
14303
|
DEFAULT_AGENT_TIERS,
|
|
14442
14304
|
resolveScopeGate(),
|
|
14443
14305
|
resolveRunRatio(),
|
|
14444
|
-
resolvePreflightPr(),
|
|
14445
14306
|
resolveScheduledTasks(),
|
|
14446
14307
|
resolveUnblockDependents()
|
|
14447
14308
|
),
|
|
@@ -16178,6 +16039,162 @@ var prReviewerSubAgent = {
|
|
|
16178
16039
|
"`feat(scope): description`). The body should bullet the changes and end",
|
|
16179
16040
|
"with `Closes #<issue-number>`.",
|
|
16180
16041
|
"",
|
|
16042
|
+
"##### Update the branch when `mergeStateStatus` is `BEHIND`",
|
|
16043
|
+
"",
|
|
16044
|
+
"After enabling auto-merge, re-read the PR's `mergeStateStatus` so a",
|
|
16045
|
+
"branch that fell behind the default branch lands on the next CI tick",
|
|
16046
|
+
"instead of sitting in the auto-merge queue indefinitely:",
|
|
16047
|
+
"",
|
|
16048
|
+
"```bash",
|
|
16049
|
+
"gh pr view <pr-number> --json mergeStateStatus --jq '.mergeStateStatus'",
|
|
16050
|
+
"```",
|
|
16051
|
+
"",
|
|
16052
|
+
"When the value is `BEHIND`, attempt to bring the head branch current with",
|
|
16053
|
+
"the default branch via `gh pr update-branch` (default merge strategy \u2014",
|
|
16054
|
+
"**never** `--rebase`, which would rewrite commits on a published branch):",
|
|
16055
|
+
"",
|
|
16056
|
+
"```bash",
|
|
16057
|
+
"gh pr update-branch <pr-number>",
|
|
16058
|
+
"```",
|
|
16059
|
+
"",
|
|
16060
|
+
"Branch on the outcome:",
|
|
16061
|
+
"",
|
|
16062
|
+
"- **Success** \u2014 record `Branch updated: yes` for the per-PR report and",
|
|
16063
|
+
" stop. Auto-merge will fire when required checks pass on the new head",
|
|
16064
|
+
" SHA. Do **not** poll for the merge here \u2014 Phase 5 owns polling.",
|
|
16065
|
+
"- **Failure for reasons other than a merge conflict** (permission",
|
|
16066
|
+
" denied, branch protection refusing the merge commit, transient",
|
|
16067
|
+
" network error) \u2014 record `Branch updated: failed (<reason>)`, post a",
|
|
16068
|
+
" short comment explaining the failure, and stop. Do not retry.",
|
|
16069
|
+
"- **Failure because the merge would conflict** \u2014 proceed to the",
|
|
16070
|
+
" conflict-resolution delegation flow below.",
|
|
16071
|
+
"",
|
|
16072
|
+
"When `mergeStateStatus` is **not** `BEHIND` (`CLEAN`, `BLOCKED`,",
|
|
16073
|
+
"`UNSTABLE`, `HAS_HOOKS`, `UNKNOWN`), record `Branch updated: not needed`",
|
|
16074
|
+
"and skip the rest of this sub-section. Only the `BEHIND` state triggers",
|
|
16075
|
+
"an `update-branch` attempt \u2014 every other state either has nothing to do",
|
|
16076
|
+
"or is already gated on a different signal that Phase 5 picks up.",
|
|
16077
|
+
"",
|
|
16078
|
+
"Never run `gh pr update-branch` on a PR whose review mode is",
|
|
16079
|
+
"`human-required`. Pushing main into a human-required PR expands the",
|
|
16080
|
+
"scope of the diff the human is reviewing without their consent. The",
|
|
16081
|
+
"`update-branch` step lives **only** under the `Mode auto-merge` branch",
|
|
16082
|
+
"of this phase \u2014 `human-required` skips straight to its hand-off block",
|
|
16083
|
+
"below.",
|
|
16084
|
+
"",
|
|
16085
|
+
"##### Conflict-resolution delegation (BEHIND + conflicts)",
|
|
16086
|
+
"",
|
|
16087
|
+
"When `gh pr update-branch <pr-number>` fails because the merge would",
|
|
16088
|
+
"produce conflicts, the reviewer **may** delegate conflict resolution to",
|
|
16089
|
+
"`issue-worker` via the existing feedback-mode delegation contract (the",
|
|
16090
|
+
"same path Phase 4's in-scope-fix delegation uses). The reviewer never",
|
|
16091
|
+
"hand-resolves conflicts itself \u2014 branch mutations always belong to",
|
|
16092
|
+
"`issue-worker`.",
|
|
16093
|
+
"",
|
|
16094
|
+
"Delegate **only when all** of the following hold. If any guard fails,",
|
|
16095
|
+
"fall through to the fallback at the end of this sub-section instead.",
|
|
16096
|
+
"",
|
|
16097
|
+
"1. **Review mode is `auto-merge`.** Never delegate conflict",
|
|
16098
|
+
" resolution on `human-required` PRs \u2014 pushing main resolutions into",
|
|
16099
|
+
" them expands the diff the human is reviewing. (This is the same",
|
|
16100
|
+
" reason the `update-branch` step itself is auto-merge-only.)",
|
|
16101
|
+
"2. **Delegation invocation guard permits the hand-off** \u2014 the PR",
|
|
16102
|
+
" carries the `origin:issue-worker` label, **or** the reviewer was",
|
|
16103
|
+
" invoked with `--allow-human-author`. The same guard used for the",
|
|
16104
|
+
" in-scope-fix delegation flow above applies here unchanged.",
|
|
16105
|
+
"3. **The `review:fixing` lease is currently free.** If",
|
|
16106
|
+
" `review:fixing` is already on the PR, another delegation is",
|
|
16107
|
+
" in-flight; skip conflict-resolution delegation, log the contention",
|
|
16108
|
+
" to the sticky summary, and fall through to the fallback.",
|
|
16109
|
+
"4. **No conflicting file is generated or projen-managed.** Run",
|
|
16110
|
+
" `gh pr view <pr-number> --json files --jq '.files[].path'` and",
|
|
16111
|
+
" inspect the conflicting paths reported by the failed",
|
|
16112
|
+
" `update-branch`. If **any** conflicting path matches one of the",
|
|
16113
|
+
" following globs, the conflict is unsafe to resolve mechanically",
|
|
16114
|
+
" and the reviewer must skip delegation:",
|
|
16115
|
+
" - `**/*.lock` and `pnpm-lock.yaml` / `yarn.lock` / `package-lock.json`",
|
|
16116
|
+
" - `**/.projen/**`",
|
|
16117
|
+
" - any file whose first 5 lines contain the marker",
|
|
16118
|
+
" `~~ Generated by projen` or `// Generated by`.",
|
|
16119
|
+
" The lockfile guard exists because lockfile conflicts often need a",
|
|
16120
|
+
" regen (`pnpm i`) rather than a textual merge; the projen / generated",
|
|
16121
|
+
" guard exists because those files are derived from `.projenrc.ts`",
|
|
16122
|
+
" and conflicts there should be resolved by re-running synth, not by",
|
|
16123
|
+
" merging the conflict markers.",
|
|
16124
|
+
"",
|
|
16125
|
+
"When every guard above passes, hand off to `issue-worker` with a",
|
|
16126
|
+
"**single synthetic fix-list item** that describes the rebase work:",
|
|
16127
|
+
"",
|
|
16128
|
+
"1. **Disable auto-merge** so a fast CI pass cannot land the PR",
|
|
16129
|
+
" mid-delegation (idempotent \u2014 safe no-op when auto-merge was never",
|
|
16130
|
+
" enabled). This mirrors step (b) of the in-scope-fix flow:",
|
|
16131
|
+
"",
|
|
16132
|
+
" ```bash",
|
|
16133
|
+
" gh pr merge <pr-number> --disable-auto",
|
|
16134
|
+
" ```",
|
|
16135
|
+
"",
|
|
16136
|
+
"2. **Acquire the `review:fixing` lease** by adding the label",
|
|
16137
|
+
" (mirrors step (c) of the in-scope-fix flow). On contention, abort",
|
|
16138
|
+
" the delegation and fall through to the fallback:",
|
|
16139
|
+
"",
|
|
16140
|
+
" ```bash",
|
|
16141
|
+
" gh pr edit <pr-number> --add-label 'review:fixing'",
|
|
16142
|
+
" ```",
|
|
16143
|
+
"",
|
|
16144
|
+
"3. **Post a fix-list comment** containing exactly one synthetic item",
|
|
16145
|
+
" describing the rebase. The `comment_id` is the literal string",
|
|
16146
|
+
" `synthetic:rebase-behind-main` so the worker recognises the",
|
|
16147
|
+
" special case and the next reviewer pass can identify the item:",
|
|
16148
|
+
"",
|
|
16149
|
+
" ```markdown",
|
|
16150
|
+
" ## Reviewer: fix list for @issue-worker",
|
|
16151
|
+
"",
|
|
16152
|
+
" - [ ] @reviewer \u2014 rebase onto origin/<default-branch> and resolve conflicts in: <space-separated list of conflicting files>",
|
|
16153
|
+
"",
|
|
16154
|
+
" ```json fix-list",
|
|
16155
|
+
" {",
|
|
16156
|
+
' "pr": <pr-number>,',
|
|
16157
|
+
' "branch": "<head-ref-name>",',
|
|
16158
|
+
' "generated_at": "<ISO-8601 timestamp>",',
|
|
16159
|
+
' "items": [',
|
|
16160
|
+
' {"comment_id": "synthetic:rebase-behind-main", "author": "reviewer", "file": "<first-conflicting-file>", "instruction": "Branch is BEHIND default-branch with merge conflicts. Pull and rebase onto origin/<default-branch>, resolve conflicts in <conflicting-files>, push the resolved branch (no force-push), and report success."}',
|
|
16161
|
+
" ]",
|
|
16162
|
+
" }",
|
|
16163
|
+
" ```",
|
|
16164
|
+
" ```",
|
|
16165
|
+
"",
|
|
16166
|
+
"4. **Invoke `issue-worker` in feedback mode** with the same prompt",
|
|
16167
|
+
" shape used by the in-scope-fix flow: include the literal phrase",
|
|
16168
|
+
" `feedback mode: PR #<n>` plus the repository identifier",
|
|
16169
|
+
" (`{{repository.owner}}/{{repository.name}}`).",
|
|
16170
|
+
"",
|
|
16171
|
+
"5. **Process the worker's report** for the synthetic item using the",
|
|
16172
|
+
" same logic as step (f) of the in-scope-fix flow \u2014 `handled` reacts",
|
|
16173
|
+
" `rocket` on the fix-list comment; `failed` reacts `thinking_face`",
|
|
16174
|
+
" and posts a reply citing the worker's failure reason.",
|
|
16175
|
+
"",
|
|
16176
|
+
"6. **Release the `review:fixing` lease** with",
|
|
16177
|
+
" `gh pr edit <pr-number> --remove-label 'review:fixing'`. Always",
|
|
16178
|
+
" run this step.",
|
|
16179
|
+
"",
|
|
16180
|
+
"7. **Do not re-enable auto-merge in the same pass.** After delegation,",
|
|
16181
|
+
" exit and let a human re-invoke the reviewer. The next pass will",
|
|
16182
|
+
" re-evaluate `mergeStateStatus` against the rebased branch and",
|
|
16183
|
+
" re-enable auto-merge through the normal flow above.",
|
|
16184
|
+
"",
|
|
16185
|
+
"Record `Branch updated: delegated (PR #<n>)` for the per-PR report",
|
|
16186
|
+
"when delegation completes (regardless of the worker's outcome \u2014 the",
|
|
16187
|
+
"delegation itself is the action taken).",
|
|
16188
|
+
"",
|
|
16189
|
+
"**Fallback when delegation is not permitted or guards fail.** Post a",
|
|
16190
|
+
"short comment explaining why the branch could not be updated",
|
|
16191
|
+
"automatically and stop. Do not push commits, do not force, do not",
|
|
16192
|
+
"retry. Record `Branch updated: failed (conflicts; <short reason>)`.",
|
|
16193
|
+
"",
|
|
16194
|
+
"```bash",
|
|
16195
|
+
"gh pr comment <pr-number> --body 'Auto-merge queued; branch is BEHIND <default-branch> with conflicts. <short reason delegation was skipped \u2014 e.g. human-required mode, generated-file conflicts, or in-flight review:fixing lease>. A human (or the next reviewer pass after rebase) will need to resolve.'",
|
|
16196
|
+
"```",
|
|
16197
|
+
"",
|
|
16181
16198
|
"#### Mode `human-required`",
|
|
16182
16199
|
"",
|
|
16183
16200
|
"Do **not** run `gh pr merge --auto`. Instead, hand the PR off to a",
|
|
@@ -16368,6 +16385,7 @@ var prReviewerSubAgent = {
|
|
|
16368
16385
|
" - Nitpick: <items>",
|
|
16369
16386
|
"",
|
|
16370
16387
|
"Action taken: <enable-auto-merge | label-awaiting-human | commented-on-the-pr | none>",
|
|
16388
|
+
"Branch updated: <yes | not needed | failed (<reason>) | delegated (PR #<n>) | n/a>",
|
|
16371
16389
|
"Branch state: <merged | open | closed>",
|
|
16372
16390
|
"Issue state: <closed | open>",
|
|
16373
16391
|
"```",
|
|
@@ -16433,7 +16451,23 @@ var prReviewerSubAgent = {
|
|
|
16433
16451
|
" `review:awaiting-human` label is **not** present on the PR. Any",
|
|
16434
16452
|
" AC-drift pushback, any failed-fix pushback, and any human-required",
|
|
16435
16453
|
" hand-off all keep auto-merge disabled until the human resolves",
|
|
16436
|
-
" the underlying state."
|
|
16454
|
+
" the underlying state.",
|
|
16455
|
+
"15. **Never run `gh pr update-branch` on a `human-required` PR.**",
|
|
16456
|
+
" Pushing main into a human-required PR expands the scope of the",
|
|
16457
|
+
" diff the human is reviewing without their consent. The",
|
|
16458
|
+
" `update-branch` step is gated to the `Mode auto-merge` branch of",
|
|
16459
|
+
" Phase 4 only. The same restriction applies to delegating",
|
|
16460
|
+
" conflict resolution to `issue-worker`: never delegate a rebase",
|
|
16461
|
+
" on a `human-required` PR.",
|
|
16462
|
+
"16. **Never delegate conflict resolution involving generated or",
|
|
16463
|
+
" projen-managed files.** When `gh pr update-branch` fails on a",
|
|
16464
|
+
" BEHIND PR with conflicts and any conflicting path is a lockfile,",
|
|
16465
|
+
" a `**/.projen/**` file, or a file marked",
|
|
16466
|
+
" `~~ Generated by projen` / `// Generated by`, the reviewer must",
|
|
16467
|
+
" skip the `issue-worker` delegation and fall back to the comment",
|
|
16468
|
+
" pathway. Mechanical merge of generated content is unsafe \u2014 those",
|
|
16469
|
+
" files are derived from `.projenrc.ts` and must be regenerated,",
|
|
16470
|
+
" not merged."
|
|
16437
16471
|
].join("\n")
|
|
16438
16472
|
};
|
|
16439
16473
|
var reviewPrSkill = {
|
|
@@ -28008,7 +28042,6 @@ var AgentConfig = class _AgentConfig extends import_projen8.Component {
|
|
|
28008
28042
|
if (resolvedRunRatio.enabled) {
|
|
28009
28043
|
this.project.gitignore.addPatterns(`/${resolvedRunRatio.stateFilePath}`);
|
|
28010
28044
|
}
|
|
28011
|
-
validatePreflightPrConfig(this.options.preflightPr);
|
|
28012
28045
|
validateUnblockDependentsConfig(this.options.unblockDependents);
|
|
28013
28046
|
const resolvedProgressFiles = validateProgressFilesConfig(
|
|
28014
28047
|
this.options.progressFiles
|
|
@@ -28225,14 +28258,13 @@ ${section}`
|
|
|
28225
28258
|
}
|
|
28226
28259
|
}
|
|
28227
28260
|
}
|
|
28228
|
-
if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.
|
|
28261
|
+
if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.scheduledTasks || this.options.unblockDependents) {
|
|
28229
28262
|
const orchestratorRule = ruleMap.get("orchestrator-conventions");
|
|
28230
28263
|
if (orchestratorRule) {
|
|
28231
28264
|
const { conventionsContent } = resolveOrchestratorAssets(
|
|
28232
28265
|
this.options.tiers,
|
|
28233
28266
|
this.options.scopeGate,
|
|
28234
28267
|
this.options.runRatio,
|
|
28235
|
-
this.options.preflightPr,
|
|
28236
28268
|
this.options.scheduledTasks,
|
|
28237
28269
|
this.options.unblockDependents
|
|
28238
28270
|
);
|
|
@@ -28547,14 +28579,13 @@ ${hook}`
|
|
|
28547
28579
|
procMap.set(proc.name, proc);
|
|
28548
28580
|
}
|
|
28549
28581
|
}
|
|
28550
|
-
if (this.options.tiers || this.options.scopeGate || this.options.runRatio
|
|
28582
|
+
if (this.options.tiers || this.options.scopeGate || this.options.runRatio) {
|
|
28551
28583
|
const existing = procMap.get("check-blocked.sh");
|
|
28552
28584
|
if (existing) {
|
|
28553
28585
|
const { procedure } = resolveOrchestratorAssets(
|
|
28554
28586
|
this.options.tiers,
|
|
28555
28587
|
this.options.scopeGate,
|
|
28556
|
-
this.options.runRatio
|
|
28557
|
-
this.options.preflightPr
|
|
28588
|
+
this.options.runRatio
|
|
28558
28589
|
);
|
|
28559
28590
|
if (procedure.content !== existing.content) {
|
|
28560
28591
|
procMap.set("check-blocked.sh", procedure);
|
|
@@ -28569,7 +28600,6 @@ ${hook}`
|
|
|
28569
28600
|
void 0,
|
|
28570
28601
|
void 0,
|
|
28571
28602
|
void 0,
|
|
28572
|
-
void 0,
|
|
28573
28603
|
this.options.unblockDependents
|
|
28574
28604
|
);
|
|
28575
28605
|
if (unblockDependentsProcedure2.content !== existing.content) {
|
|
@@ -30997,7 +31027,7 @@ var DEFAULT_AUTO_APPROVE_LABEL = "auto-approve";
|
|
|
30997
31027
|
var DEFAULT_HEAD_BRANCH = "github-actions/upgrade";
|
|
30998
31028
|
var DEFAULT_APPROVAL_APP_ID_SECRET = "APPROVAL_APP_ID";
|
|
30999
31029
|
var DEFAULT_APPROVAL_APP_PRIVATE_KEY_SECRET = "APPROVAL_APP_PRIVATE_KEY";
|
|
31000
|
-
var
|
|
31030
|
+
var DEFAULT_MERGE_METHOD = MERGE_METHODS.SQUASH;
|
|
31001
31031
|
var PULL_REQUEST_TARGET_TYPES = [
|
|
31002
31032
|
"labeled",
|
|
31003
31033
|
"synchronize",
|
|
@@ -31011,7 +31041,7 @@ function addApproveMergeUpgradeWorkflow(project, options) {
|
|
|
31011
31041
|
const allowedUsernames = options.allowedUsernames;
|
|
31012
31042
|
const appIdSecret = options.approvalAppIdSecret ?? DEFAULT_APPROVAL_APP_ID_SECRET;
|
|
31013
31043
|
const privateKeySecret = options.approvalAppPrivateKeySecret ?? DEFAULT_APPROVAL_APP_PRIVATE_KEY_SECRET;
|
|
31014
|
-
const mergeMethod = options.mergeMethod ??
|
|
31044
|
+
const mergeMethod = options.mergeMethod ?? DEFAULT_MERGE_METHOD;
|
|
31015
31045
|
const workflow = project.github?.addWorkflow(workflowName);
|
|
31016
31046
|
if (!workflow) return;
|
|
31017
31047
|
workflow.on({
|
|
@@ -32883,8 +32913,8 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
32883
32913
|
DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
|
|
32884
32914
|
DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
|
|
32885
32915
|
DEFAULT_AUDIT_REPORT_DIR,
|
|
32916
|
+
DEFAULT_BUNDLE_OVERRIDES,
|
|
32886
32917
|
DEFAULT_DECOMPOSITION_TEMPLATE,
|
|
32887
|
-
DEFAULT_DELEGATE_TO_PR_REVIEWER,
|
|
32888
32918
|
DEFAULT_DISPATCH_MODEL,
|
|
32889
32919
|
DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
|
|
32890
32920
|
DEFAULT_HOUSEKEEPING_MODEL,
|
|
@@ -32894,7 +32924,6 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
32894
32924
|
DEFAULT_ISSUE_TEMPLATES_ENABLED,
|
|
32895
32925
|
DEFAULT_ISSUE_TEMPLATES_PATH,
|
|
32896
32926
|
DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE,
|
|
32897
|
-
DEFAULT_MERGE_METHOD,
|
|
32898
32927
|
DEFAULT_OFF_PEAK_CRON_EXAMPLE,
|
|
32899
32928
|
DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE,
|
|
32900
32929
|
DEFAULT_PRIORITY_LABELS,
|
|
@@ -32904,7 +32933,6 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
32904
32933
|
DEFAULT_PROGRESS_FILES_FORMAT,
|
|
32905
32934
|
DEFAULT_PROGRESS_FILES_STALE_AFTER_HOURS,
|
|
32906
32935
|
DEFAULT_PROGRESS_FILES_STATE_DIR,
|
|
32907
|
-
DEFAULT_REQUIRE_LINKED_ISSUE,
|
|
32908
32936
|
DEFAULT_REQUIRE_PRODUCT_CONTEXT,
|
|
32909
32937
|
DEFAULT_SAMPLE_COMPILER_OPTIONS,
|
|
32910
32938
|
DEFAULT_SCHEDULED_TASKS_ROOT,
|
|
@@ -32936,7 +32964,6 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
32936
32964
|
MINIMUM_RELEASE_AGE,
|
|
32937
32965
|
MONOREPO_LAYOUT,
|
|
32938
32966
|
MonorepoProject,
|
|
32939
|
-
PREFLIGHT_MERGE_METHOD_VALUES,
|
|
32940
32967
|
PROD_DEPLOY_NAME,
|
|
32941
32968
|
PROGRESS_FILES_FORMAT_VALUES,
|
|
32942
32969
|
PnpmWorkspace,
|
|
@@ -33040,8 +33067,6 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
33040
33067
|
renderIssueTemplatesRuleContent,
|
|
33041
33068
|
renderIssueTemplatesStarterPage,
|
|
33042
33069
|
renderMeetingTypesSection,
|
|
33043
|
-
renderPreflightPrSection,
|
|
33044
|
-
renderPreflightPrShellHelpers,
|
|
33045
33070
|
renderPriorityRulesSection,
|
|
33046
33071
|
renderProgressFileName,
|
|
33047
33072
|
renderProgressFilePath,
|
|
@@ -33074,7 +33099,7 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
33074
33099
|
resolveModelAlias,
|
|
33075
33100
|
resolveOrchestratorAssets,
|
|
33076
33101
|
resolveOutdirFromPackageName,
|
|
33077
|
-
|
|
33102
|
+
resolveOverrideForLabels,
|
|
33078
33103
|
resolveProgressFiles,
|
|
33079
33104
|
resolveRunRatio,
|
|
33080
33105
|
resolveScheduledTasks,
|
|
@@ -33095,7 +33120,6 @@ var TypeScriptConfig = class extends import_projen23.Component {
|
|
|
33095
33120
|
validateAgentTierConfig,
|
|
33096
33121
|
validateIssueTemplatesConfig,
|
|
33097
33122
|
validateMonorepoLayout,
|
|
33098
|
-
validatePreflightPrConfig,
|
|
33099
33123
|
validateProgressFilesConfig,
|
|
33100
33124
|
validateRunRatioConfig,
|
|
33101
33125
|
validateScheduledTasksConfig,
|