@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.mjs CHANGED
@@ -10911,291 +10911,6 @@ var meetingAnalysisBundle = {
10911
10911
  ]
10912
10912
  };
10913
10913
 
10914
- // src/agent/bundles/preflight-pr.ts
10915
- var DEFAULT_DELEGATE_TO_PR_REVIEWER = true;
10916
- var DEFAULT_MERGE_METHOD = "squash";
10917
- var DEFAULT_REQUIRE_LINKED_ISSUE = true;
10918
- var PREFLIGHT_MERGE_METHOD_VALUES = [
10919
- "squash",
10920
- "merge",
10921
- "rebase"
10922
- ];
10923
- function resolvePreflightPr(config) {
10924
- const mergeMethod = config?.mergeMethod ?? DEFAULT_MERGE_METHOD;
10925
- assertValidMergeMethod(mergeMethod);
10926
- const eligibleLabels = config?.eligibleLabels ?? [];
10927
- assertValidEligibleLabels(eligibleLabels);
10928
- return {
10929
- enabled: config?.enabled ?? true,
10930
- delegateToPrReviewer: config?.delegateToPrReviewer ?? DEFAULT_DELEGATE_TO_PR_REVIEWER,
10931
- mergeMethod,
10932
- requireLinkedIssue: config?.requireLinkedIssue ?? DEFAULT_REQUIRE_LINKED_ISSUE,
10933
- eligibleLabels
10934
- };
10935
- }
10936
- function validatePreflightPrConfig(config) {
10937
- return resolvePreflightPr(config);
10938
- }
10939
- function renderPreflightPrSection(pf) {
10940
- const lines = [
10941
- "## Pre-flight PR merge",
10942
- "",
10943
- "The orchestrator runs a **pre-flight PR merge sweep** before its",
10944
- "triage walk so already-approved, CI-green PRs land before the",
10945
- "unblock and queue-scan phases read dependency state. Without",
10946
- "pre-flight, an issue whose only remaining blocker is an approved",
10947
- "PR stuck behind a branch-protection delay shows up as blocked on",
10948
- "every dispatch cycle \u2014 the pipeline thrashes on stale dependency",
10949
- "state.",
10950
- ""
10951
- ];
10952
- if (!pf.enabled) {
10953
- lines.push(
10954
- "**Pre-flight is disabled for this project.** The orchestrator",
10955
- "skips the pre-flight sweep entirely; PR merges land via the",
10956
- "existing batch PR review step on housekeeping runs or via a",
10957
- "human operator. Enable pre-flight via",
10958
- "`AgentConfigOptions.preflightPr.enabled = true` once the repo",
10959
- "is comfortable with automated merges.",
10960
- ""
10961
- );
10962
- return lines.join("\n");
10963
- }
10964
- lines.push(
10965
- "### When pre-flight runs",
10966
- "",
10967
- "Pre-flight runs on **every orchestrator invocation** \u2014",
10968
- "both dispatch and housekeeping runs \u2014 immediately after Phase A",
10969
- "(Startup) and before any other phase reads issue or PR state. The",
10970
- "sweep is idempotent: `gh pr merge` on an already-merged PR is a",
10971
- "no-op, so re-running pre-flight on back-to-back invocations is",
10972
- "safe and cheap.",
10973
- "",
10974
- "### Eligibility",
10975
- "",
10976
- "A PR is eligible for a pre-flight merge when **all** of the",
10977
- "following hold:",
10978
- "",
10979
- "- PR is not draft and not closed.",
10980
- "- PR is `MERGEABLE` (no branch conflicts).",
10981
- "- Every required CI check has passed (`SUCCESS` or `SKIPPED`).",
10982
- "- PR carries at least one `APPROVED` review **or** auto-merge is",
10983
- " already enabled on the PR.",
10984
- "- PR is not carrying `status:needs-attention`."
10985
- );
10986
- if (pf.requireLinkedIssue) {
10987
- lines.push(
10988
- "- PR body contains a `Closes #<n>` / `Fixes #<n>` / `Resolves`",
10989
- " `#<n>` keyword referencing an open issue."
10990
- );
10991
- } else {
10992
- lines.push(
10993
- "- Linked-issue check is **disabled** for this project \u2014 PRs",
10994
- " without a `Closes/Fixes/Resolves` keyword are eligible."
10995
- );
10996
- }
10997
- if (pf.eligibleLabels.length > 0) {
10998
- lines.push(
10999
- "- PR carries at least one of the following consumer-declared",
11000
- " eligibility labels:",
11001
- "",
11002
- ...pf.eligibleLabels.map((label) => ` - \`${label}\``)
11003
- );
11004
- }
11005
- lines.push(
11006
- "",
11007
- "PRs that fail any criterion are skipped with a one-line",
11008
- "diagnostic and left for the `pr-reviewer` or a human operator to",
11009
- "handle on the normal review path.",
11010
- "",
11011
- "### Execution mode",
11012
- ""
11013
- );
11014
- if (pf.delegateToPrReviewer) {
11015
- lines.push(
11016
- "**Delegate mode (default).** The orchestrator invokes the",
11017
- "`pr-reviewer` sub-agent to run the pre-flight sweep. This",
11018
- "mirrors vortex's step 0b contract: the merge workflow runs on",
11019
- "its own (cheaper) model rather than consuming the orchestrator's",
11020
- "more expensive model budget for mechanical merges.",
11021
- "",
11022
- "The sub-agent returns a one-line structured summary as the last",
11023
- "line of its response:",
11024
- "",
11025
- "```",
11026
- "Merged <n> (#<list>), skipped <m> (#<pr> \u2014 <reason>), <k> eligible left.",
11027
- "```",
11028
- "",
11029
- "If the sub-agent's final line is missing, malformed, or",
11030
- "indicates an error, the orchestrator **does not retry** \u2014 it",
11031
- "proceeds to the next phase anyway. PR merges are idempotent at",
11032
- "GitHub, so the next orchestrator session re-runs pre-flight.",
11033
- "The only cost of a skipped sub-agent pass is that dependency",
11034
- "issues stay open one more cycle."
11035
- );
11036
- } else {
11037
- lines.push(
11038
- "**Inline mode.** The orchestrator performs the pre-flight merge",
11039
- "itself \u2014 it does **not** delegate to the `pr-reviewer`",
11040
- "sub-agent. Use this mode in repos that prefer a single-process",
11041
- "pipeline or that have not wired a `pr-reviewer` sub-agent.",
11042
- "",
11043
- `For each eligible PR, the orchestrator runs \`gh pr merge --${pf.mergeMethod}\``,
11044
- "with `--delete-branch` and re-confirms that the linked issue",
11045
- "closes (applying `status:done` if the auto-close did not fire)."
11046
- );
11047
- }
11048
- lines.push(
11049
- "",
11050
- "### Post-merge verification",
11051
- "",
11052
- "After each successful merge, the orchestrator re-reads the linked",
11053
- "issue state. When the merge commit did **not** auto-close the",
11054
- "issue (closing-keyword typos, linked-issue keyword dropped during",
11055
- "a rebase), the orchestrator closes the issue explicitly and",
11056
- "applies `status:done` so the downstream unblock loop sees the",
11057
- "dependency as resolved. This is the only way pre-flight prevents",
11058
- "the stale-dependency thrash \u2014 without the verification step a",
11059
- "merged-but-not-closed issue still reads as blocking to",
11060
- "`check-blocked.sh unblock`."
11061
- );
11062
- return lines.join("\n");
11063
- }
11064
- function renderPreflightPrShellHelpers(pf) {
11065
- const requireIssueFlag = pf.requireLinkedIssue ? "1" : "0";
11066
- const hasLabelFilter = pf.eligibleLabels.length > 0;
11067
- const lines = [
11068
- "# Scan open PRs and emit one eligibility line per PR in the",
11069
- "# canonical `PREFLIGHT_MERGE PR #<n> issue:#<m> branch:<b> \u2014",
11070
- '# "<title>"` / `PREFLIGHT_SKIP PR #<n> \u2014 <reason> \u2014 "<title>"` /',
11071
- "# `NO_PREFLIGHT_PRS` format. Orchestrator-side loops read this",
11072
- "# output and either merge the PR inline or delegate the sweep to",
11073
- "# the pr-reviewer sub-agent.",
11074
- "cmd_preflight() {",
11075
- ' if [[ "$PREFLIGHT_ENABLED" != "1" ]]; then',
11076
- ' echo "NO_PREFLIGHT_PRS"',
11077
- " return 0",
11078
- " fi",
11079
- " local prs",
11080
- " prs=$(gh pr list --state open --json number,title,isDraft,mergeable,headRefName,labels,body,reviewDecision,autoMergeRequest \\",
11081
- ' --limit 50 2>/dev/null || echo "[]")',
11082
- "",
11083
- " local count",
11084
- ` count=$(echo "$prs" | jq 'length')`,
11085
- ' if [[ "$count" -eq 0 ]]; then',
11086
- ' echo "NO_PREFLIGHT_PRS"',
11087
- " return 0",
11088
- " fi",
11089
- "",
11090
- " # Stage 1: filter in jq on purely structural fields (draft,",
11091
- " # labels, linked-issue keyword). CI state lives outside the",
11092
- " # list endpoint so it is checked per-PR below.",
11093
- " local candidates",
11094
- ` candidates=$(echo "$prs" | jq -r --arg require_issue "$PREFLIGHT_REQUIRE_LINKED_ISSUE" '`,
11095
- " .[] |",
11096
- " select(.isDraft == false) |",
11097
- ' select(.labels | map(.name) | index("status:needs-attention") | not) |'
11098
- ];
11099
- if (hasLabelFilter) {
11100
- const jqArray = pf.eligibleLabels.map((label) => `"${label.replace(/"/g, '\\"')}"`).join(",");
11101
- lines.push(
11102
- ` select((.labels | map(.name)) as $names | [${jqArray}] | any(. as $l | $names | index($l))) |`
11103
- );
11104
- }
11105
- lines.push(
11106
- ' (.body | capture("(?:Closes|Fixes|Resolves) #(?<num>[0-9]+)"; "i") | .num) as $issue |',
11107
- ' select($require_issue == "0" or ($issue != null and $issue != "")) |',
11108
- ' "\\(.number)\\t\\(.title)\\t\\(.headRefName)\\t\\($issue // "")\\t\\(.mergeable // "UNKNOWN")\\t\\(.reviewDecision // "")\\t\\((.autoMergeRequest // null) != null)"',
11109
- " ' 2>/dev/null)",
11110
- "",
11111
- ' if [[ -z "$candidates" ]]; then',
11112
- ' echo "NO_PREFLIGHT_PRS"',
11113
- " return 0",
11114
- " fi",
11115
- "",
11116
- " local found_eligible=false",
11117
- " while IFS=$'\\t' read -r pr_num title branch issue_num mergeable review_decision auto_merge; do",
11118
- ' [[ -z "$pr_num" ]] && continue',
11119
- "",
11120
- " # Mergeable gate.",
11121
- ' if [[ "$mergeable" != "MERGEABLE" ]]; then',
11122
- ' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 not mergeable (${mergeable}) \u2014 \\"${title}\\""',
11123
- " continue",
11124
- " fi",
11125
- "",
11126
- " # Approval gate: APPROVED review OR auto-merge already",
11127
- " # enabled. Either signal means a human has signed off on the",
11128
- " # change; pre-flight only needs to wait for CI + branch",
11129
- " # protection to clear.",
11130
- ' if [[ "$review_decision" != "APPROVED" && "$auto_merge" != "true" ]]; then',
11131
- ' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 not approved and auto-merge not set \u2014 \\"${title}\\""',
11132
- " continue",
11133
- " fi",
11134
- "",
11135
- " # CI gate. `gh pr checks` returns one row per check; anything",
11136
- " # that is neither SUCCESS nor SKIPPED counts as failing or",
11137
- " # pending.",
11138
- " local failing_checks",
11139
- ' failing_checks=$(gh pr checks "$pr_num" --json name,state \\',
11140
- ` --jq '[.[] | select(.state != "SUCCESS" and .state != "SKIPPED")] | length' 2>/dev/null || echo "-1")`,
11141
- "",
11142
- ' if [[ "$failing_checks" == "-1" ]]; then',
11143
- ' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 could not read CI status \u2014 \\"${title}\\""',
11144
- " continue",
11145
- " fi",
11146
- "",
11147
- ' if [[ "$failing_checks" -gt 0 ]]; then',
11148
- ' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 ${failing_checks} CI check(s) not passing \u2014 \\"${title}\\""',
11149
- " continue",
11150
- " fi",
11151
- "",
11152
- " # Linked-issue state gate. A PR whose linked issue is already",
11153
- " # closed should not block pre-flight \u2014 skip it so the unblock",
11154
- " # loop picks up the dependency. This path also fires when the",
11155
- " # PR was merged but the closing keyword was dropped.",
11156
- ' if [[ -n "$issue_num" ]]; then',
11157
- " local issue_state",
11158
- ` issue_state=$(gh issue view "$issue_num" --json state --jq '.state' 2>/dev/null || echo "UNKNOWN")`,
11159
- ' if [[ "$issue_state" == "CLOSED" ]]; then',
11160
- ' echo "PREFLIGHT_SKIP PR #${pr_num} \u2014 linked issue #${issue_num} already closed \u2014 \\"${title}\\""',
11161
- " continue",
11162
- " fi",
11163
- " fi",
11164
- "",
11165
- " found_eligible=true",
11166
- ' echo "PREFLIGHT_MERGE PR #${pr_num} issue:#${issue_num:-none} branch:${branch} \u2014 \\"${title}\\""',
11167
- ' done <<< "$candidates"',
11168
- "",
11169
- " if ! $found_eligible; then",
11170
- ' echo "NO_PREFLIGHT_PRS"',
11171
- " fi",
11172
- "}"
11173
- );
11174
- void requireIssueFlag;
11175
- return lines.join("\n");
11176
- }
11177
- function assertValidMergeMethod(value) {
11178
- if (!PREFLIGHT_MERGE_METHOD_VALUES.includes(value)) {
11179
- throw new Error(
11180
- `PreflightPrConfig.mergeMethod must be one of ${PREFLIGHT_MERGE_METHOD_VALUES.join(
11181
- " | "
11182
- )}; got ${JSON.stringify(value)}`
11183
- );
11184
- }
11185
- }
11186
- function assertValidEligibleLabels(labels) {
11187
- for (let i = 0; i < labels.length; i += 1) {
11188
- const label = labels[i];
11189
- if (typeof label !== "string" || label.trim().length === 0) {
11190
- throw new Error(
11191
- `PreflightPrConfig.eligibleLabels[${i}] must be a non-empty string; got ${JSON.stringify(
11192
- label
11193
- )}`
11194
- );
11195
- }
11196
- }
11197
- }
11198
-
11199
10914
  // src/agent/bundles/run-ratio.ts
11200
10915
  var DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO = 4;
11201
10916
  var DEFAULT_STATE_FILE_PATH = ".state/orchestrator-runs.json";
@@ -11282,23 +10997,18 @@ function renderRunRatioSection(ratio) {
11282
10997
  "### Dispatch-run pipeline",
11283
10998
  "",
11284
10999
  "1. Phase A \u2014 startup (fetch + checkout default branch).",
11285
- "2. Phase B0 \u2014 pre-flight PR merge sweep (land approved,",
11286
- " CI-green PRs before triage reads dependency state).",
11287
- "3. Phase C \u2014 triage unblock (resolve `Depends on:` chains).",
11288
- "4. Phase E \u2014 queue scan (pick the top `PICK` line, run the scope",
11000
+ "2. Phase C \u2014 triage unblock (resolve `Depends on:` chains).",
11001
+ "3. Phase E \u2014 queue scan (pick the top `PICK` line, run the scope",
11289
11002
  " gate, emit `NEXT_WORK_ITEM`).",
11290
- "5. Phase F \u2014 cleanup.",
11003
+ "4. Phase F \u2014 cleanup.",
11291
11004
  "",
11292
11005
  "### Housekeeping-run pipeline",
11293
11006
  "",
11294
11007
  "1. Phase A \u2014 startup.",
11295
- "2. Phase B0 \u2014 pre-flight PR merge sweep (same mechanics as the",
11296
- " dispatch run; runs before the batch review so already-approved",
11297
- " PRs land cleanly).",
11298
- "3. Phase B \u2014 batch PR review across every eligible open PR.",
11299
- "4. Phase D \u2014 maintenance scan (stale detection, orphaned branches,",
11008
+ "2. Phase B \u2014 batch PR review across every eligible open PR.",
11009
+ "3. Phase D \u2014 maintenance scan (stale detection, orphaned branches,",
11300
11010
  " needs-attention summary).",
11301
- "5. Phase F \u2014 cleanup.",
11011
+ "4. Phase F \u2014 cleanup.",
11302
11012
  "",
11303
11013
  "### Model recommendations",
11304
11014
  "",
@@ -11425,7 +11135,7 @@ var DEFAULT_SCHEDULED_TASK_ENTRIES = [
11425
11135
  recommendedModel: "sonnet",
11426
11136
  enabled: false,
11427
11137
  cron: null,
11428
- description: "End-to-end pipeline manager: pre-flight PR merge, triage, maintenance, queue scan, delegate the picked issue to the issue-worker, then cleanup."
11138
+ description: "End-to-end pipeline manager: triage, maintenance, queue scan, delegate the picked issue to the issue-worker, then cleanup."
11429
11139
  },
11430
11140
  // Tier 1 — research.
11431
11141
  {
@@ -12051,6 +11761,14 @@ var DEFAULT_SOURCES_THRESHOLDS = {
12051
11761
  smallMax: 2,
12052
11762
  mediumMax: 5
12053
11763
  };
11764
+ var DEFAULT_BUNDLE_OVERRIDES = {
11765
+ "req:write": {
11766
+ acceptanceCriteria: { smallMax: 3, mediumMax: 20 }
11767
+ },
11768
+ "bcm:scaffold": {
11769
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
11770
+ }
11771
+ };
12054
11772
  var DEFAULT_DECOMPOSITION_TEMPLATE = [
12055
11773
  "## Scope gate: issue is too large to dispatch",
12056
11774
  "",
@@ -12090,22 +11808,58 @@ function resolveScopeGate(config) {
12090
11808
  DEFAULT_SOURCES_THRESHOLDS,
12091
11809
  "sources"
12092
11810
  );
11811
+ const bundleOverrides = resolveBundleOverrides(
11812
+ config?.bundleOverrides,
11813
+ DEFAULT_BUNDLE_OVERRIDES
11814
+ );
12093
11815
  return {
12094
11816
  enabled: config?.enabled ?? true,
12095
11817
  acceptanceCriteria: ac,
12096
11818
  sources,
12097
11819
  autoFile: config?.autoFile ?? false,
12098
- decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE
11820
+ decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE,
11821
+ bundleOverrides
11822
+ };
11823
+ }
11824
+ function resolveOverrideForLabels(gate, labels) {
11825
+ const overrideKeys = Object.keys(gate.bundleOverrides);
11826
+ if (overrideKeys.length === 0 || labels.length === 0) {
11827
+ return {
11828
+ acceptanceCriteria: gate.acceptanceCriteria,
11829
+ sources: gate.sources
11830
+ };
11831
+ }
11832
+ const sortedLabels = [...labels].sort((a, b) => a.localeCompare(b));
11833
+ const matchedLabel = sortedLabels.find(
11834
+ (label) => Object.prototype.hasOwnProperty.call(gate.bundleOverrides, label)
11835
+ );
11836
+ if (matchedLabel === void 0) {
11837
+ return {
11838
+ acceptanceCriteria: gate.acceptanceCriteria,
11839
+ sources: gate.sources
11840
+ };
11841
+ }
11842
+ const override = gate.bundleOverrides[matchedLabel];
11843
+ return {
11844
+ acceptanceCriteria: override.acceptanceCriteria ?? gate.acceptanceCriteria,
11845
+ sources: override.sources ?? gate.sources,
11846
+ matchedLabel
12099
11847
  };
12100
11848
  }
12101
11849
  function validateScopeGateConfig(config) {
12102
11850
  return resolveScopeGate(config);
12103
11851
  }
12104
- function classifyIssueScope(body, gate) {
11852
+ function classifyIssueScope(body, gate, labels = []) {
12105
11853
  const acCount = countAcceptanceCriteria(body);
12106
11854
  const sourcesCount = countSources(body);
12107
- const scope = resolveScopeClass(acCount, sourcesCount, gate);
12108
- return { scope, acCount, sourcesCount };
11855
+ const effective = resolveOverrideForLabels(gate, labels);
11856
+ const scope = resolveScopeClass(acCount, sourcesCount, effective);
11857
+ return {
11858
+ scope,
11859
+ acCount,
11860
+ sourcesCount,
11861
+ matchedLabel: effective.matchedLabel
11862
+ };
12109
11863
  }
12110
11864
  function renderScopeGateSection(gate) {
12111
11865
  const { acceptanceCriteria: ac, sources } = gate;
@@ -12181,6 +11935,38 @@ function renderScopeGateSection(gate) {
12181
11935
  " is in place."
12182
11936
  );
12183
11937
  }
11938
+ const overrideEntries = Object.entries(gate.bundleOverrides);
11939
+ if (overrideEntries.length > 0) {
11940
+ lines.push(
11941
+ "",
11942
+ "### Per-phase-label thresholds",
11943
+ "",
11944
+ "Issues whose `type:*` or phase label matches an entry below are",
11945
+ "classified against the override's thresholds instead of the",
11946
+ "global `acceptance-criteria` / `sources` defaults. This",
11947
+ "accommodates **content-spec workflows** \u2014 issues whose AC list",
11948
+ "is the per-section content checklist for one cohesive document,",
11949
+ "not a phase-completion checklist that can be decomposed.",
11950
+ "",
11951
+ "| Phase label | AC `mediumMax` | Sources `mediumMax` |",
11952
+ "|-------------|----------------|---------------------|"
11953
+ );
11954
+ const sortedKeys = overrideEntries.map(([k]) => k).sort();
11955
+ for (const label of sortedKeys) {
11956
+ const override = gate.bundleOverrides[label];
11957
+ const acMax = override.acceptanceCriteria?.mediumMax;
11958
+ const sourcesMax = override.sources?.mediumMax;
11959
+ const acCell = acMax === void 0 ? "_(global)_" : `${acMax}`;
11960
+ const sourcesCell = sourcesMax === void 0 ? "_(global)_" : `${sourcesMax}`;
11961
+ lines.push(`| \`${label}\` | ${acCell} | ${sourcesCell} |`);
11962
+ }
11963
+ lines.push(
11964
+ "",
11965
+ "When an issue carries multiple labels that each match an entry",
11966
+ "in this table, the orchestrator picks the **first match in",
11967
+ "alphabetical order on the label name** \u2014 first-wins, deterministic."
11968
+ );
11969
+ }
12184
11970
  lines.push(
12185
11971
  "",
12186
11972
  "### Decomposition-proposal template",
@@ -12197,12 +11983,21 @@ function renderScopeGateSection(gate) {
12197
11983
  }
12198
11984
  function renderScopeGateShellHelpers(gate) {
12199
11985
  const { acceptanceCriteria: ac, sources } = gate;
12200
- return [
11986
+ const lines = [
12201
11987
  "# Classify an issue body against the scope-gate thresholds.",
12202
11988
  "# Reads the issue body from stdin. Echoes one of 'small',",
12203
11989
  "# 'medium', or 'large' on stdout. Writes the observed counts to",
12204
11990
  "# stderr as `ac=<n> sources=<n>` so the caller can embed them in",
12205
11991
  "# the decomposition-proposal comment.",
11992
+ "#",
11993
+ "# Effective per-issue thresholds are read from the four",
11994
+ "# `_EFFECTIVE_*` shell variables. The caller is expected to set",
11995
+ "# them via `bundle_override_for()` before invoking scope_of();",
11996
+ "# defaults preserve the global thresholds.",
11997
+ `_EFFECTIVE_AC_SMALL_MAX="\${_EFFECTIVE_AC_SMALL_MAX:-${ac.smallMax}}"`,
11998
+ `_EFFECTIVE_AC_MEDIUM_MAX="\${_EFFECTIVE_AC_MEDIUM_MAX:-${ac.mediumMax}}"`,
11999
+ `_EFFECTIVE_SOURCES_SMALL_MAX="\${_EFFECTIVE_SOURCES_SMALL_MAX:-${sources.smallMax}}"`,
12000
+ `_EFFECTIVE_SOURCES_MEDIUM_MAX="\${_EFFECTIVE_SOURCES_MEDIUM_MAX:-${sources.mediumMax}}"`,
12206
12001
  "scope_of() {",
12207
12002
  " local body",
12208
12003
  " body=$(cat)",
@@ -12231,15 +12026,79 @@ function renderScopeGateShellHelpers(gate) {
12231
12026
  " END { print count }",
12232
12027
  " ')",
12233
12028
  ` printf 'ac=%s sources=%s\\n' "$ac_count" "$sources_count" >&2`,
12234
- ` if [ "$ac_count" -le ${ac.smallMax} ] && [ "$sources_count" -le ${sources.smallMax} ]; then`,
12029
+ ' if [ "$ac_count" -le "$_EFFECTIVE_AC_SMALL_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_SMALL_MAX" ]; then',
12235
12030
  " echo small",
12236
- ` elif [ "$ac_count" -le ${ac.mediumMax} ] && [ "$sources_count" -le ${sources.mediumMax} ]; then`,
12031
+ ' elif [ "$ac_count" -le "$_EFFECTIVE_AC_MEDIUM_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_MEDIUM_MAX" ]; then',
12237
12032
  " echo medium",
12238
12033
  " else",
12239
12034
  " echo large",
12240
12035
  " fi",
12036
+ "}",
12037
+ "",
12038
+ "# Resolve per-phase-label threshold overrides. Reads a newline-",
12039
+ "# separated list of label names on stdin and echoes effective",
12040
+ "# threshold variable assignments (one per line) on stdout. The",
12041
+ "# caller `eval`s the output to set the four `_EFFECTIVE_*`",
12042
+ "# variables before calling `scope_of()`. Also echoes",
12043
+ "# `MATCHED_LABEL=<label>` (or `MATCHED_LABEL=`) so the caller can",
12044
+ "# report which override fired.",
12045
+ "#",
12046
+ "# Tie-breaking: when more than one label matches an override key,",
12047
+ "# the alphabetical first-wins rule applies \u2014 the same rule the",
12048
+ "# TypeScript classifier uses.",
12049
+ "bundle_override_for() {",
12050
+ ` local default_ac_small=${ac.smallMax}`,
12051
+ ` local default_ac_medium=${ac.mediumMax}`,
12052
+ ` local default_sources_small=${sources.smallMax}`,
12053
+ ` local default_sources_medium=${sources.mediumMax}`,
12054
+ " # Sort labels alphabetically so the first match is deterministic.",
12055
+ " local sorted_labels",
12056
+ " sorted_labels=$(LC_ALL=C sort)",
12057
+ " local matched_label=''",
12058
+ " local ac_small=$default_ac_small",
12059
+ " local ac_medium=$default_ac_medium",
12060
+ " local sources_small=$default_sources_small",
12061
+ " local sources_medium=$default_sources_medium",
12062
+ " while IFS= read -r label; do",
12063
+ ' [ -z "$label" ] && continue',
12064
+ ` case "$label" in`
12065
+ ];
12066
+ const sortedKeys = Object.keys(gate.bundleOverrides).sort();
12067
+ if (sortedKeys.length === 0) {
12068
+ lines.push(" *) ;;");
12069
+ } else {
12070
+ for (const label of sortedKeys) {
12071
+ const override = gate.bundleOverrides[label];
12072
+ const acThresholds = override.acceptanceCriteria;
12073
+ const sourcesThresholds = override.sources;
12074
+ const branchLines = [` '${label}')`];
12075
+ if (acThresholds !== void 0) {
12076
+ branchLines.push(` ac_small=${acThresholds.smallMax}`);
12077
+ branchLines.push(` ac_medium=${acThresholds.mediumMax}`);
12078
+ }
12079
+ if (sourcesThresholds !== void 0) {
12080
+ branchLines.push(` sources_small=${sourcesThresholds.smallMax}`);
12081
+ branchLines.push(
12082
+ ` sources_medium=${sourcesThresholds.mediumMax}`
12083
+ );
12084
+ }
12085
+ branchLines.push(` matched_label='${label}'`);
12086
+ branchLines.push(" break");
12087
+ branchLines.push(" ;;");
12088
+ lines.push(...branchLines);
12089
+ }
12090
+ }
12091
+ lines.push(
12092
+ " esac",
12093
+ ` done <<<"$sorted_labels"`,
12094
+ ` printf 'MATCHED_LABEL=%s\\n' "$matched_label"`,
12095
+ ` printf '_EFFECTIVE_AC_SMALL_MAX=%s\\n' "$ac_small"`,
12096
+ ` printf '_EFFECTIVE_AC_MEDIUM_MAX=%s\\n' "$ac_medium"`,
12097
+ ` printf '_EFFECTIVE_SOURCES_SMALL_MAX=%s\\n' "$sources_small"`,
12098
+ ` printf '_EFFECTIVE_SOURCES_MEDIUM_MAX=%s\\n' "$sources_medium"`,
12241
12099
  "}"
12242
- ].join("\n");
12100
+ );
12101
+ return lines.join("\n");
12243
12102
  }
12244
12103
  function resolveThresholds(supplied, defaults, field) {
12245
12104
  const merged = {
@@ -12249,6 +12108,42 @@ function resolveThresholds(supplied, defaults, field) {
12249
12108
  assertValidThresholds(merged, field);
12250
12109
  return merged;
12251
12110
  }
12111
+ function resolveBundleOverrides(supplied, defaults) {
12112
+ const out = {};
12113
+ for (const [label, override] of Object.entries(defaults)) {
12114
+ out[label] = override;
12115
+ }
12116
+ if (supplied !== void 0) {
12117
+ for (const label of Object.keys(supplied)) {
12118
+ const value = supplied[label];
12119
+ if (value === void 0) {
12120
+ delete out[label];
12121
+ continue;
12122
+ }
12123
+ const resolved = {};
12124
+ if (value.acceptanceCriteria !== void 0) {
12125
+ resolved.acceptanceCriteria = resolveThresholds(
12126
+ value.acceptanceCriteria,
12127
+ DEFAULT_AC_THRESHOLDS,
12128
+ "acceptanceCriteria"
12129
+ );
12130
+ }
12131
+ if (value.sources !== void 0) {
12132
+ resolved.sources = resolveThresholds(
12133
+ value.sources,
12134
+ DEFAULT_SOURCES_THRESHOLDS,
12135
+ "sources"
12136
+ );
12137
+ }
12138
+ if (resolved.acceptanceCriteria === void 0 && resolved.sources === void 0) {
12139
+ delete out[label];
12140
+ continue;
12141
+ }
12142
+ out[label] = resolved;
12143
+ }
12144
+ }
12145
+ return out;
12146
+ }
12252
12147
  function assertValidThresholds(thresholds, field) {
12253
12148
  const { smallMax, mediumMax } = thresholds;
12254
12149
  for (const [key, value] of [
@@ -12272,8 +12167,8 @@ function assertValidThresholds(thresholds, field) {
12272
12167
  );
12273
12168
  }
12274
12169
  }
12275
- function resolveScopeClass(acCount, sourcesCount, gate) {
12276
- const { acceptanceCriteria: ac, sources } = gate;
12170
+ function resolveScopeClass(acCount, sourcesCount, thresholds) {
12171
+ const { acceptanceCriteria: ac, sources } = thresholds;
12277
12172
  if (acCount <= ac.smallMax && sourcesCount <= sources.smallMax) {
12278
12173
  return "small";
12279
12174
  }
@@ -12520,9 +12415,6 @@ function renderUnblockDependentsSection(ud) {
12520
12415
  "",
12521
12416
  "- The `pr-reviewer` sub-agent, Phase 5 (branch cleanup), after it",
12522
12417
  " applies `status:done` to the linked issue on a successful merge.",
12523
- "- The `orchestrator` sub-agent, Phase B0 (pre-flight PR merge)",
12524
- " post-merge verification, after it force-closes an issue whose",
12525
- " merge commit dropped its closing keyword.",
12526
12418
  "",
12527
12419
  "Any other agent that manually applies `status:done` should call",
12528
12420
  "`unblock-dependents.sh <closed-issue>` as its last step. The",
@@ -12828,12 +12720,11 @@ function shellSingleQuote(value) {
12828
12720
  }
12829
12721
 
12830
12722
  // src/agent/bundles/orchestrator.ts
12831
- function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
12723
+ function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
12832
12724
  const tierCase = renderAgentTierCaseStatement(tiers);
12833
12725
  const scopeHelper = renderScopeGateShellHelpers(scopeGate);
12834
12726
  const scopeHelperIndented = scopeHelper.split("\n").map((line) => line.length > 0 ? line : "").join("\n");
12835
12727
  const runRatioHelper = renderRunRatioShellHelpers(runRatio);
12836
- const preflightHelper = renderPreflightPrShellHelpers(preflight);
12837
12728
  return [
12838
12729
  "#!/usr/bin/env bash",
12839
12730
  "# check-blocked.sh \u2014 Token-efficient issue triage for agent loops.",
@@ -12848,7 +12739,6 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
12848
12739
  "# .claude/procedures/check-blocked.sh prs",
12849
12740
  "# .claude/procedures/check-blocked.sh scope <issue-number>",
12850
12741
  "# .claude/procedures/check-blocked.sh tick",
12851
- "# .claude/procedures/check-blocked.sh preflight",
12852
12742
  "",
12853
12743
  "set -uo pipefail",
12854
12744
  "",
@@ -12858,15 +12748,13 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
12858
12748
  "STALE_BLOCKED_HOURS=168",
12859
12749
  `SCOPE_GATE_ENABLED=${scopeGate.enabled ? "1" : "0"}`,
12860
12750
  `SCOPE_GATE_AUTO_FILE=${scopeGate.autoFile ? "1" : "0"}`,
12751
+ `SCOPE_AC_SMALL_MAX=${scopeGate.acceptanceCriteria.smallMax}`,
12861
12752
  `SCOPE_AC_MEDIUM_MAX=${scopeGate.acceptanceCriteria.mediumMax}`,
12753
+ `SCOPE_SOURCES_SMALL_MAX=${scopeGate.sources.smallMax}`,
12862
12754
  `SCOPE_SOURCES_MEDIUM_MAX=${scopeGate.sources.mediumMax}`,
12863
12755
  `RUN_RATIO_ENABLED=${runRatio.enabled ? "1" : "0"}`,
12864
12756
  `RUN_RATIO_DISPATCH_PER_HOUSEKEEPING=${runRatio.ratio}`,
12865
12757
  `ORCHESTRATOR_STATE_FILE="${runRatio.stateFilePath}"`,
12866
- `PREFLIGHT_ENABLED=${preflight.enabled ? "1" : "0"}`,
12867
- `PREFLIGHT_REQUIRE_LINKED_ISSUE=${preflight.requireLinkedIssue ? "1" : "0"}`,
12868
- `PREFLIGHT_MERGE_METHOD="${preflight.mergeMethod}"`,
12869
- `PREFLIGHT_DELEGATE_TO_PR_REVIEWER=${preflight.delegateToPrReviewer ? "1" : "0"}`,
12870
12758
  "",
12871
12759
  "# \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",
12872
12760
  "",
@@ -12899,8 +12787,6 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
12899
12787
  "",
12900
12788
  runRatioHelper,
12901
12789
  "",
12902
- preflightHelper,
12903
- "",
12904
12790
  "# \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",
12905
12791
  "",
12906
12792
  "cmd_unblock() {",
@@ -13218,12 +13104,45 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
13218
13104
  ' echo "SCOPE #${issue_num} disabled"',
13219
13105
  " return 0",
13220
13106
  " fi",
13107
+ " # Fetch the body and labels in a single API call so we resolve",
13108
+ " # any per-phase-label override against the issue's actual labels.",
13109
+ " local issue_json",
13110
+ ' issue_json=$(gh issue view "$issue_num" --json body,labels 2>/dev/null || echo "")',
13111
+ ' if [[ -z "$issue_json" ]]; then',
13112
+ ' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
13113
+ " return 1",
13114
+ " fi",
13221
13115
  " local body",
13222
- ` body=$(gh issue view "$issue_num" --json body --jq '.body' 2>/dev/null || echo "")`,
13116
+ ` body=$(printf '%s' "$issue_json" | jq -r '.body // ""')`,
13223
13117
  ' if [[ -z "$body" ]]; then',
13224
13118
  ' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
13225
13119
  " return 1",
13226
13120
  " fi",
13121
+ " local labels",
13122
+ ` labels=$(printf '%s' "$issue_json" | jq -r '.labels[]?.name // empty')`,
13123
+ " # Resolve the effective thresholds. `bundle_override_for` reads",
13124
+ " # labels on stdin and emits `KEY=VALUE` assignments (one per line)",
13125
+ " # for the four `_EFFECTIVE_*` variables plus `MATCHED_LABEL`.",
13126
+ " local override_output",
13127
+ ` override_output=$(printf '%s\\n' "$labels" | bundle_override_for)`,
13128
+ " local matched_label=''",
13129
+ " local _EFFECTIVE_AC_SMALL_MAX=$SCOPE_AC_SMALL_MAX",
13130
+ " local _EFFECTIVE_AC_MEDIUM_MAX=$SCOPE_AC_MEDIUM_MAX",
13131
+ " local _EFFECTIVE_SOURCES_SMALL_MAX=$SCOPE_SOURCES_SMALL_MAX",
13132
+ " local _EFFECTIVE_SOURCES_MEDIUM_MAX=$SCOPE_SOURCES_MEDIUM_MAX",
13133
+ " while IFS= read -r assignment; do",
13134
+ ' [ -z "$assignment" ] && continue',
13135
+ ' case "$assignment" in',
13136
+ " MATCHED_LABEL=*) matched_label=${assignment#MATCHED_LABEL=} ;;",
13137
+ " _EFFECTIVE_AC_SMALL_MAX=*) _EFFECTIVE_AC_SMALL_MAX=${assignment#_EFFECTIVE_AC_SMALL_MAX=} ;;",
13138
+ " _EFFECTIVE_AC_MEDIUM_MAX=*) _EFFECTIVE_AC_MEDIUM_MAX=${assignment#_EFFECTIVE_AC_MEDIUM_MAX=} ;;",
13139
+ " _EFFECTIVE_SOURCES_SMALL_MAX=*) _EFFECTIVE_SOURCES_SMALL_MAX=${assignment#_EFFECTIVE_SOURCES_SMALL_MAX=} ;;",
13140
+ " _EFFECTIVE_SOURCES_MEDIUM_MAX=*) _EFFECTIVE_SOURCES_MEDIUM_MAX=${assignment#_EFFECTIVE_SOURCES_MEDIUM_MAX=} ;;",
13141
+ " esac",
13142
+ ' done <<<"$override_output"',
13143
+ " # Export the effective thresholds so scope_of() picks them up.",
13144
+ " export _EFFECTIVE_AC_SMALL_MAX _EFFECTIVE_AC_MEDIUM_MAX",
13145
+ " export _EFFECTIVE_SOURCES_SMALL_MAX _EFFECTIVE_SOURCES_MEDIUM_MAX",
13227
13146
  " local scope counts",
13228
13147
  " # scope_of writes counts to stderr (ac=N sources=N); capture both.",
13229
13148
  ` scope=$(printf '%s' "$body" | scope_of 2> >(read -r counts; echo "$counts" >&2))`,
@@ -13237,9 +13156,17 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
13237
13156
  ` sources_count=$(echo "$stderr_capture" | grep -oE 'sources=[0-9]+' | head -1 | cut -d= -f2)`,
13238
13157
  " ac_count=${ac_count:-0}",
13239
13158
  " sources_count=${sources_count:-0}",
13240
- " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s\\n' \\",
13241
- ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13242
- ' "$SCOPE_AC_MEDIUM_MAX" "$SCOPE_SOURCES_MEDIUM_MAX" "$SCOPE_GATE_AUTO_FILE"',
13159
+ ' if [ -n "$matched_label" ]; then',
13160
+ " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s matched_label=%s\\n' \\",
13161
+ ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13162
+ ' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
13163
+ ' "$SCOPE_GATE_AUTO_FILE" "$matched_label"',
13164
+ " else",
13165
+ " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s\\n' \\",
13166
+ ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13167
+ ' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
13168
+ ' "$SCOPE_GATE_AUTO_FILE"',
13169
+ " fi",
13243
13170
  "}",
13244
13171
  "",
13245
13172
  "cmd_tick() {",
@@ -13265,26 +13192,24 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight) {
13265
13192
  ' prs) shift; cmd_prs "$@" ;;',
13266
13193
  ' scope) shift; cmd_scope "$@" ;;',
13267
13194
  ' tick) shift; cmd_tick "$@" ;;',
13268
- ' preflight) shift; cmd_preflight "$@" ;;',
13269
13195
  " help|*)",
13270
- ' echo "Usage: check-blocked.sh <unblock|eligible|stale|orphaned|prs|scope|tick|preflight>"',
13196
+ ' echo "Usage: check-blocked.sh <unblock|eligible|stale|orphaned|prs|scope|tick>"',
13271
13197
  " exit 1",
13272
13198
  " ;;",
13273
13199
  "esac"
13274
13200
  ].join("\n");
13275
13201
  }
13276
- function buildCheckBlockedProcedure(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio(), preflight = resolvePreflightPr()) {
13202
+ function buildCheckBlockedProcedure(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio()) {
13277
13203
  return {
13278
13204
  name: "check-blocked.sh",
13279
- description: "Token-efficient issue triage script with subcommands: eligible, unblock, stale, orphaned, prs, scope, preflight, 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 preflight subcommand emits one eligibility line per open PR that is mergeable, CI-green, approved (or auto-merge already enabled), and linked to an open issue. The tick subcommand is a deprecation no-op retained for one release (the orchestrator no longer maintains a dispatch/housekeeping run counter).",
13280
- content: buildCheckBlockedScript(tiers, scopeGate, runRatio, preflight)
13205
+ 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).",
13206
+ content: buildCheckBlockedScript(tiers, scopeGate, runRatio)
13281
13207
  };
13282
13208
  }
13283
13209
  var checkBlockedProcedure = buildCheckBlockedProcedure(
13284
13210
  DEFAULT_AGENT_TIERS,
13285
13211
  resolveScopeGate(),
13286
- resolveRunRatio(),
13287
- resolvePreflightPr()
13212
+ resolveRunRatio()
13288
13213
  );
13289
13214
  function buildUnblockDependentsProcedure(unblockDependents = resolveUnblockDependents()) {
13290
13215
  return {
@@ -13298,7 +13223,7 @@ var unblockDependentsProcedure = buildUnblockDependentsProcedure(
13298
13223
  );
13299
13224
  var orchestratorSubAgent = {
13300
13225
  name: "orchestrator",
13301
- description: "End-to-end pipeline manager that runs one full cycle every invocation: pre-flight PR merge \u2192 triage \u2192 maintenance \u2192 queue scan \u2192 delegate the picked issue to the issue-worker \u2192 cleanup",
13226
+ 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",
13302
13227
  model: AGENT_MODEL.POWERFUL,
13303
13228
  maxTurns: 100,
13304
13229
  platforms: { cursor: { exclude: true } },
@@ -13306,12 +13231,13 @@ var orchestratorSubAgent = {
13306
13231
  "# Orchestrator Agent",
13307
13232
  "",
13308
13233
  "You are the pipeline orchestrator for the **{{repository.owner}}/{{repository.name}}** repository.",
13309
- "Each invocation runs **one full end-to-end cycle**. The cycle merges",
13310
- "approved PRs, triages blocked issues, runs maintenance scans, picks the",
13311
- "next ready issue, and **delegates implementation to the `issue-worker`**",
13312
- "sub-agent in scheduled mode. The orchestrator itself never implements",
13313
- "code, creates branches, or pushes commits \u2014 it routes work to other",
13314
- "agents.",
13234
+ "Each invocation runs **one full end-to-end cycle**. The cycle triages",
13235
+ "blocked issues, runs maintenance scans, picks the next ready issue, and",
13236
+ "**delegates implementation to the `issue-worker`** sub-agent in scheduled",
13237
+ "mode. The orchestrator itself never implements code, creates branches, or",
13238
+ "pushes commits \u2014 it routes work to other agents. Approved-PR merging is",
13239
+ "owned by the `pr-reviewer` sub-agent (invoked via `/review-pr` /",
13240
+ "`/review-prs`); the orchestrator does not run a merge sweep.",
13315
13241
  "",
13316
13242
  "Run the phases below in order on every invocation. There is no",
13317
13243
  "dispatch/housekeeping fork \u2014 every phase runs every time.",
@@ -13319,20 +13245,23 @@ var orchestratorSubAgent = {
13319
13245
  "Phase ordering (each runs exactly once per invocation):",
13320
13246
  "",
13321
13247
  "1. **Phase A \u2014 Startup.** Pull the default branch.",
13322
- "2. **Phase B0 \u2014 Pre-flight PR Merge.** Land every eligible approved /",
13323
- " CI-green PR before triage reads dependency state.",
13324
- "3. **Phase C \u2014 Triage / Unblock.** Flip dependents that have all",
13248
+ "2. **Phase C \u2014 Triage / Unblock.** Flip dependents that have all",
13325
13249
  " their dependencies closed back to `status:ready`.",
13326
- "4. **Phase D \u2014 Maintenance.** Stale-issue and orphaned-resource scan",
13250
+ "3. **Phase D \u2014 Maintenance.** Stale-issue and orphaned-resource scan",
13327
13251
  " plus a needs-attention summary.",
13328
- "5. **Phase E \u2014 Queue Scan.** Pick the highest-priority `PICK` line",
13252
+ "4. **Phase E \u2014 Queue Scan.** Pick the highest-priority `PICK` line",
13329
13253
  " and run the scope gate. If the result is `large`, flag and stop.",
13330
- "6. **Phase G \u2014 Delegate Implementation.** Hand the picked issue off",
13254
+ "5. **Phase G \u2014 Delegate Implementation.** Hand the picked issue off",
13331
13255
  " to the `issue-worker` sub-agent in scheduled mode. The worker",
13332
13256
  " handles claim \u2192 branch \u2192 implement \u2192 commit \u2192 PR autonomously.",
13333
- "7. **Phase F \u2014 Cleanup.** Return to the default branch and log the",
13257
+ "6. **Phase F \u2014 Cleanup.** Return to the default branch and log the",
13334
13258
  " run summary.",
13335
13259
  "",
13260
+ "Phase letters are stable identifiers \u2014 letters skipped in the list",
13261
+ "above were used by earlier revisions of this pipeline and the gap",
13262
+ "is left intentionally so existing references in commit history and",
13263
+ "docs remain unambiguous.",
13264
+ "",
13336
13265
  "---",
13337
13266
  "",
13338
13267
  ...PROJECT_CONTEXT_READER_SECTION,
@@ -13344,100 +13273,6 @@ var orchestratorSubAgent = {
13344
13273
  "git checkout main && git pull origin main",
13345
13274
  "```",
13346
13275
  "",
13347
- "## Phase B0: Pre-flight PR Merge",
13348
- "",
13349
- "Before any other phase reads dependency state, land every",
13350
- "eligible PR. The sweep is idempotent \u2014 `gh pr merge` on an",
13351
- "already-merged PR is a no-op \u2014 so re-running on back-to-back",
13352
- "invocations is safe.",
13353
- "",
13354
- "List eligibility:",
13355
- "",
13356
- "```bash",
13357
- ".claude/procedures/check-blocked.sh preflight",
13358
- "```",
13359
- "",
13360
- "Expect one line per open PR:",
13361
- "",
13362
- "```",
13363
- 'PREFLIGHT_MERGE PR #<n> issue:#<m> branch:<b> \u2014 "<title>"',
13364
- 'PREFLIGHT_SKIP PR #<n> \u2014 <reason> \u2014 "<title>"',
13365
- "NO_PREFLIGHT_PRS",
13366
- "```",
13367
- "",
13368
- "If the output is `NO_PREFLIGHT_PRS`, continue to Phase B / C.",
13369
- "",
13370
- "For each `PREFLIGHT_MERGE` line, either **delegate** to the",
13371
- "`pr-reviewer` sub-agent (default) or **merge inline** depending",
13372
- "on the rendered pre-flight config. See the **Pre-flight PR",
13373
- "merge** section in `CLAUDE.md` for the project's mode.",
13374
- "",
13375
- "**Delegate mode (default).** Invoke the `pr-reviewer` sub-agent",
13376
- "with a single-pass brief covering every `PREFLIGHT_MERGE` line:",
13377
- "",
13378
- "> Run your merge pipeline once across these PRs: <list of",
13379
- "> `#<n>` numbers>. Merge every eligible PR (linked issue still",
13380
- "> open, AC satisfied, CI green, approval present) and comment on",
13381
- "> any PR you skip. Return a one-line summary as the final line of",
13382
- "> your response, e.g. `Merged 2 (#123, #124), skipped 1 (#125 \u2014",
13383
- "> AC not met), 0 eligible left.` or `No eligible PRs.`",
13384
- "",
13385
- "If the sub-agent's final line is missing, malformed, or indicates",
13386
- "an error, **do not retry** \u2014 proceed to the next phase anyway.",
13387
- "The next orchestrator session will re-run pre-flight and pick up",
13388
- "anything left behind.",
13389
- "",
13390
- "**Inline mode.** For each `PREFLIGHT_MERGE PR #<n> issue:#<m>`",
13391
- "line, merge the PR with the configured method (default",
13392
- "`--squash`) and `--delete-branch`:",
13393
- "",
13394
- "```bash",
13395
- 'gh pr merge <n> --squash --delete-branch --subject "<conventional-commit-title>" --body "<extended-description>"',
13396
- "```",
13397
- "",
13398
- "After every successful merge \u2014 whether delegated or inline \u2014 run",
13399
- "the post-merge verification step to close the stale-dependency",
13400
- "loop:",
13401
- "",
13402
- "```bash",
13403
- "gh issue view <m> --json state --jq '.state'",
13404
- "```",
13405
- "",
13406
- "If the linked issue's state is still `OPEN`, close it explicitly",
13407
- "and flip its status label to `status:done`:",
13408
- "",
13409
- "```bash",
13410
- "gh issue close <m>",
13411
- 'gh issue edit <m> --remove-label "status:ready-for-review" --add-label "status:done"',
13412
- "```",
13413
- "",
13414
- "After applying `status:done` \u2014 whether the auto-close fired or",
13415
- "you force-closed above \u2014 run the targeted unblock sweep so any",
13416
- "dependents that were only waiting on issue `<m>` flip to",
13417
- "`status:ready` immediately:",
13418
- "",
13419
- "```bash",
13420
- ".claude/procedures/unblock-dependents.sh <m>",
13421
- "```",
13422
- "",
13423
- "The script emits one line per processed dependent",
13424
- "(`UNBLOCKED #<n>` / `STILL_BLOCKED #<n>` / `NO_DEPENDENTS #<m>`)",
13425
- "so the run summary records what transitioned. See the",
13426
- "**Agent-driven unblocking** section in `CLAUDE.md` for the full",
13427
- "contract.",
13428
- "",
13429
- "This step is the reason pre-flight exists: it prevents the",
13430
- "orchestrator from reading a merged-but-not-auto-closed issue as",
13431
- "still blocking on the next unblock sweep.",
13432
- "",
13433
- "Log the pre-flight outcome (number merged, PRs skipped with",
13434
- "reasons, and any issues force-closed) so the run summary records",
13435
- "what landed.",
13436
- "",
13437
- "Skip lines starting with `PREFLIGHT_SKIP` \u2014 those PRs failed one",
13438
- "or more eligibility checks. The `pr-reviewer` or a human",
13439
- "operator picks them up on the normal review path.",
13440
- "",
13441
13276
  "## Phase C: Triage \u2014 Unblock",
13442
13277
  "",
13443
13278
  "Check for blocked issues whose dependencies have resolved:",
@@ -13536,9 +13371,16 @@ var orchestratorSubAgent = {
13536
13371
  "The output is one line of the form:",
13537
13372
  "",
13538
13373
  "```",
13539
- "SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1>",
13374
+ "SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1> [matched_label=<label>]",
13540
13375
  "```",
13541
13376
  "",
13377
+ "When `matched_label=<label>` is present, the scope gate applied a",
13378
+ "per-phase-label override (e.g. `req:write` raises the AC ceiling to",
13379
+ "20 for content-spec workflows whose AC list is the per-section",
13380
+ "checklist for one document). The `ac_limit` / `sources_limit`",
13381
+ "values reflect the override; the global thresholds are documented",
13382
+ "in the **Scope gate** section in `CLAUDE.md`.",
13383
+ "",
13542
13384
  "If the scope is `small` or `medium`, continue \u2014 the issue is",
13543
13385
  "dispatchable. Record the first `PICK` line as the next work item",
13544
13386
  "and proceed to Phase G:",
@@ -13633,40 +13475,31 @@ var orchestratorSubAgent = {
13633
13475
  "git fetch --prune origin",
13634
13476
  "```",
13635
13477
  "",
13636
- "Log completion: phases executed, PRs merged in pre-flight, issues",
13637
- "unblocked, stale issues flagged, the next work item picked, and",
13638
- "the outcome of Phase G's delegation (worker success / failure /",
13639
- "skipped because the queue was empty).",
13478
+ "Log completion: phases executed, issues unblocked, stale issues",
13479
+ "flagged, the next work item picked, and the outcome of Phase G's",
13480
+ "delegation (worker success / failure / skipped because the queue",
13481
+ "was empty).",
13640
13482
  "",
13641
13483
  "---",
13642
13484
  "",
13643
13485
  "## Rules",
13644
13486
  "",
13645
- "1. **Never implement code.** You merge, triage, scan, and delegate \u2014 you do not code.",
13487
+ "1. **Never implement code.** You triage, scan, and delegate \u2014 you do not code.",
13646
13488
  "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.",
13647
- "3. **Always use check-blocked.sh.** All triage queries go through the shell script for token efficiency.",
13648
- "4. **Follow CLAUDE.md conventions** for all git and gh operations.",
13649
- "5. **Priority order:** critical > high > medium > low > trivial, then **funnel tier asc** (lower tier wins ties), then FIFO by issue number.",
13650
- "6. **Never dispatch a `large` issue.** Always run the scope check",
13489
+ "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.",
13490
+ "4. **Always use check-blocked.sh.** All triage queries go through the shell script for token efficiency.",
13491
+ "5. **Follow CLAUDE.md conventions** for all git and gh operations.",
13492
+ "6. **Priority order:** critical > high > medium > low > trivial, then **funnel tier asc** (lower tier wins ties), then FIFO by issue number.",
13493
+ "7. **Never dispatch a `large` issue.** Always run the scope check",
13651
13494
  " on the top `PICK` line before reporting `NEXT_WORK_ITEM`. A",
13652
13495
  " `large` issue must be flagged with `status:needs-attention` and",
13653
13496
  " handed a decomposition proposal \u2014 never claimed, never branched,",
13654
13497
  " never delegated to the `issue-worker`.",
13655
- "7. **Always run pre-flight before reading dependency state.**",
13656
- " Phase B0 (Pre-flight PR Merge) runs on every invocation,",
13657
- " immediately after Phase A. Never skip Phase B0: without it, an",
13658
- " issue whose only remaining blocker is an approved-but-not-yet-",
13659
- " merged PR reads as blocked on every cycle and the pipeline",
13660
- " thrashes. See the **Pre-flight PR merge** section in",
13661
- " `CLAUDE.md` for eligibility rules and the delegate-vs-inline",
13662
- " modes.",
13663
13498
  "8. **Sweep dependents whenever you apply `status:done`.** Every",
13664
13499
  " `status:done` transition must be immediately followed by",
13665
13500
  " `.claude/procedures/unblock-dependents.sh <n>` so downstream",
13666
13501
  " `Depends on: #<n>` issues flip to `status:ready` without",
13667
- " waiting for the next cycle. Phase B0's post-merge",
13668
- " verification (and any inline-mode merge) already calls the",
13669
- " sweep; don't skip it. See the **Agent-driven unblocking**",
13502
+ " waiting for the next cycle. See the **Agent-driven unblocking**",
13670
13503
  " section in `CLAUDE.md` for the contract.",
13671
13504
  "9. **Always invoke `issue-worker` in scheduled mode.** Phase G's",
13672
13505
  " delegation prompt must contain the literal phrase",
@@ -13680,7 +13513,7 @@ var issueWorkerSubAgent = {
13680
13513
  name: "issue-worker",
13681
13514
  description: "Selects the next ready issue from the queue, claims it, and implements the change end-to-end following repository conventions",
13682
13515
  model: AGENT_MODEL.POWERFUL,
13683
- maxTurns: 100,
13516
+ maxTurns: 150,
13684
13517
  platforms: { cursor: { exclude: true } },
13685
13518
  prompt: [
13686
13519
  "# Issue Worker",
@@ -13909,6 +13742,35 @@ var issueWorkerSubAgent = {
13909
13742
  " `file` (and optional `line`). Track `comment_id` per item so you can",
13910
13743
  " report which items were handled and which (if any) failed.",
13911
13744
  "",
13745
+ " **Synthetic rebase items.** A `comment_id` of",
13746
+ " `synthetic:rebase-behind-main` is the reviewer's signal that the",
13747
+ " PR's head branch is BEHIND the default branch with merge conflicts",
13748
+ " that `gh pr update-branch` could not resolve. For this item only,",
13749
+ " the work is **not** an editorial change \u2014 it is a rebase plus",
13750
+ " conflict resolution. Run the following sequence:",
13751
+ "",
13752
+ " ```bash",
13753
+ " git fetch origin",
13754
+ " git pull --rebase origin {{repository.defaultBranch}}",
13755
+ " ```",
13756
+ "",
13757
+ " When the rebase produces conflicts, resolve each conflicting file",
13758
+ " in turn (read both sides, reconcile, stage), then run",
13759
+ " `git rebase --continue` until the rebase completes. Never run",
13760
+ " `git rebase --abort` on a synthetic-rebase item \u2014 aborting drops",
13761
+ " the work the reviewer delegated. If you cannot resolve a conflict",
13762
+ " (the change is editorial enough that it requires human judgement,",
13763
+ " or the conflict touches a generated file you should not hand-edit),",
13764
+ " record the item as `failed` with a clear reason, run",
13765
+ " `git rebase --abort`, and proceed to the report step. Do not",
13766
+ " force-push.",
13767
+ "",
13768
+ " When the rebase completes cleanly, treat the rebased commits",
13769
+ " themselves as the deliverable. Skip the conventional commit step",
13770
+ " in Phase 6 for this item \u2014 the rebase already produced the commit",
13771
+ " history. Push with a regular non-force `git push origin <branch>`",
13772
+ " and report the rebased head SHA as the worker's commit.",
13773
+ "",
13912
13774
  "4. When complete, prepare a short structured report (PR number, commit",
13913
13775
  " SHAs you will push, items handled by `comment_id`, items that failed",
13914
13776
  " to apply) \u2014 you will return this after Phase 6.",
@@ -13970,6 +13832,22 @@ var issueWorkerSubAgent = {
13970
13832
  "git push origin <branch-name>",
13971
13833
  "```",
13972
13834
  "",
13835
+ "**Synthetic-rebase items skip the `fix(review)` commit.** When the",
13836
+ "fix-list contained a `comment_id` of `synthetic:rebase-behind-main`",
13837
+ "and you completed the rebase in Phase 4, the rebased commit history",
13838
+ "is itself the deliverable \u2014 there is no per-item editorial change to",
13839
+ "wrap in a `fix(review)` commit. Skip `git add` / `git commit` /",
13840
+ "`git pull --rebase` for that item and push the rebased branch",
13841
+ "directly:",
13842
+ "",
13843
+ "```bash",
13844
+ "git push origin <branch-name>",
13845
+ "```",
13846
+ "",
13847
+ "If the fix-list mixed a synthetic-rebase item with editorial items,",
13848
+ "perform the editorial commit first (the rebase already preceded it",
13849
+ "in Phase 4), then push once at the end.",
13850
+ "",
13973
13851
  "**Normal mode:** Use conventional commit messages and push with `-u`",
13974
13852
  "to set up branch tracking:",
13975
13853
  "",
@@ -14103,8 +13981,8 @@ var ORCHESTRATOR_CONVENTIONS_PREAMBLE = [
14103
13981
  "",
14104
13982
  "When running the orchestrator agent (`.claude/agents/orchestrator.md`):",
14105
13983
  "",
14106
- "- The orchestrator runs **one full end-to-end cycle every invocation**: pre-flight PR merge \u2192 triage / unblock \u2192 maintenance \u2192 queue scan \u2192 delegate to `issue-worker` \u2192 cleanup",
14107
- "- The orchestrator **never** implements code, creates branches, or pushes commits \u2014 it merges PRs, triages issues, picks the next work item, and delegates implementation to other sub-agents",
13984
+ "- 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",
13985
+ "- 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`).",
14108
13986
  "- All triage queries use `.claude/procedures/check-blocked.sh` for token efficiency",
14109
13987
  "- 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.",
14110
13988
  "- Priority order: critical > high > medium > low > trivial, then **funnel tier asc** (lower tier wins ties), then FIFO by issue number",
@@ -14117,7 +13995,7 @@ var ORCHESTRATOR_CONVENTIONS_PREAMBLE = [
14117
13995
  "",
14118
13996
  '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.'
14119
13997
  ].join("\n");
14120
- function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio(), preflight = resolvePreflightPr(), scheduledTasks = resolveScheduledTasks(), unblockDependents = resolveUnblockDependents()) {
13998
+ function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio(), scheduledTasks = resolveScheduledTasks(), unblockDependents = resolveUnblockDependents()) {
14121
13999
  return [
14122
14000
  ORCHESTRATOR_CONVENTIONS_PREAMBLE,
14123
14001
  "",
@@ -14127,18 +14005,15 @@ function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate
14127
14005
  "",
14128
14006
  renderRunRatioSection(runRatio),
14129
14007
  "",
14130
- renderPreflightPrSection(preflight),
14131
- "",
14132
14008
  renderScheduledTasksSection(scheduledTasks),
14133
14009
  "",
14134
14010
  renderUnblockDependentsSection(unblockDependents)
14135
14011
  ].join("\n");
14136
14012
  }
14137
- function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig, preflightPrConfig, scheduledTasksConfig, unblockDependentsConfig) {
14013
+ function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig, scheduledTasksConfig, unblockDependentsConfig) {
14138
14014
  const tiers = resolveAgentTiers(tierConfig);
14139
14015
  const scopeGate = resolveScopeGate(scopeGateConfig);
14140
14016
  const runRatio = resolveRunRatio(runRatioConfig);
14141
- const preflight = resolvePreflightPr(preflightPrConfig);
14142
14017
  const scheduledTasks = resolveScheduledTasks(scheduledTasksConfig);
14143
14018
  const unblockDependentsResolved = resolveUnblockDependents(
14144
14019
  unblockDependentsConfig
@@ -14147,23 +14022,16 @@ function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig,
14147
14022
  tiers,
14148
14023
  scopeGate,
14149
14024
  runRatio,
14150
- preflight,
14151
14025
  scheduledTasks,
14152
14026
  unblockDependents: unblockDependentsResolved,
14153
14027
  conventionsContent: buildOrchestratorConventionsContent(
14154
14028
  tiers,
14155
14029
  scopeGate,
14156
14030
  runRatio,
14157
- preflight,
14158
14031
  scheduledTasks,
14159
14032
  unblockDependentsResolved
14160
14033
  ),
14161
- procedure: buildCheckBlockedProcedure(
14162
- tiers,
14163
- scopeGate,
14164
- runRatio,
14165
- preflight
14166
- ),
14034
+ procedure: buildCheckBlockedProcedure(tiers, scopeGate, runRatio),
14167
14035
  unblockDependentsProcedure: buildUnblockDependentsProcedure(
14168
14036
  unblockDependentsResolved
14169
14037
  )
@@ -14177,13 +14045,12 @@ var orchestratorBundle = {
14177
14045
  rules: [
14178
14046
  {
14179
14047
  name: "orchestrator-conventions",
14180
- description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, pre-flight PR merge contract, and per-agent scheduled-task layout",
14048
+ description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, and per-agent scheduled-task layout",
14181
14049
  scope: AGENT_RULE_SCOPE.ALWAYS,
14182
14050
  content: buildOrchestratorConventionsContent(
14183
14051
  DEFAULT_AGENT_TIERS,
14184
14052
  resolveScopeGate(),
14185
14053
  resolveRunRatio(),
14186
- resolvePreflightPr(),
14187
14054
  resolveScheduledTasks(),
14188
14055
  resolveUnblockDependents()
14189
14056
  ),
@@ -15920,6 +15787,162 @@ var prReviewerSubAgent = {
15920
15787
  "`feat(scope): description`). The body should bullet the changes and end",
15921
15788
  "with `Closes #<issue-number>`.",
15922
15789
  "",
15790
+ "##### Update the branch when `mergeStateStatus` is `BEHIND`",
15791
+ "",
15792
+ "After enabling auto-merge, re-read the PR's `mergeStateStatus` so a",
15793
+ "branch that fell behind the default branch lands on the next CI tick",
15794
+ "instead of sitting in the auto-merge queue indefinitely:",
15795
+ "",
15796
+ "```bash",
15797
+ "gh pr view <pr-number> --json mergeStateStatus --jq '.mergeStateStatus'",
15798
+ "```",
15799
+ "",
15800
+ "When the value is `BEHIND`, attempt to bring the head branch current with",
15801
+ "the default branch via `gh pr update-branch` (default merge strategy \u2014",
15802
+ "**never** `--rebase`, which would rewrite commits on a published branch):",
15803
+ "",
15804
+ "```bash",
15805
+ "gh pr update-branch <pr-number>",
15806
+ "```",
15807
+ "",
15808
+ "Branch on the outcome:",
15809
+ "",
15810
+ "- **Success** \u2014 record `Branch updated: yes` for the per-PR report and",
15811
+ " stop. Auto-merge will fire when required checks pass on the new head",
15812
+ " SHA. Do **not** poll for the merge here \u2014 Phase 5 owns polling.",
15813
+ "- **Failure for reasons other than a merge conflict** (permission",
15814
+ " denied, branch protection refusing the merge commit, transient",
15815
+ " network error) \u2014 record `Branch updated: failed (<reason>)`, post a",
15816
+ " short comment explaining the failure, and stop. Do not retry.",
15817
+ "- **Failure because the merge would conflict** \u2014 proceed to the",
15818
+ " conflict-resolution delegation flow below.",
15819
+ "",
15820
+ "When `mergeStateStatus` is **not** `BEHIND` (`CLEAN`, `BLOCKED`,",
15821
+ "`UNSTABLE`, `HAS_HOOKS`, `UNKNOWN`), record `Branch updated: not needed`",
15822
+ "and skip the rest of this sub-section. Only the `BEHIND` state triggers",
15823
+ "an `update-branch` attempt \u2014 every other state either has nothing to do",
15824
+ "or is already gated on a different signal that Phase 5 picks up.",
15825
+ "",
15826
+ "Never run `gh pr update-branch` on a PR whose review mode is",
15827
+ "`human-required`. Pushing main into a human-required PR expands the",
15828
+ "scope of the diff the human is reviewing without their consent. The",
15829
+ "`update-branch` step lives **only** under the `Mode auto-merge` branch",
15830
+ "of this phase \u2014 `human-required` skips straight to its hand-off block",
15831
+ "below.",
15832
+ "",
15833
+ "##### Conflict-resolution delegation (BEHIND + conflicts)",
15834
+ "",
15835
+ "When `gh pr update-branch <pr-number>` fails because the merge would",
15836
+ "produce conflicts, the reviewer **may** delegate conflict resolution to",
15837
+ "`issue-worker` via the existing feedback-mode delegation contract (the",
15838
+ "same path Phase 4's in-scope-fix delegation uses). The reviewer never",
15839
+ "hand-resolves conflicts itself \u2014 branch mutations always belong to",
15840
+ "`issue-worker`.",
15841
+ "",
15842
+ "Delegate **only when all** of the following hold. If any guard fails,",
15843
+ "fall through to the fallback at the end of this sub-section instead.",
15844
+ "",
15845
+ "1. **Review mode is `auto-merge`.** Never delegate conflict",
15846
+ " resolution on `human-required` PRs \u2014 pushing main resolutions into",
15847
+ " them expands the diff the human is reviewing. (This is the same",
15848
+ " reason the `update-branch` step itself is auto-merge-only.)",
15849
+ "2. **Delegation invocation guard permits the hand-off** \u2014 the PR",
15850
+ " carries the `origin:issue-worker` label, **or** the reviewer was",
15851
+ " invoked with `--allow-human-author`. The same guard used for the",
15852
+ " in-scope-fix delegation flow above applies here unchanged.",
15853
+ "3. **The `review:fixing` lease is currently free.** If",
15854
+ " `review:fixing` is already on the PR, another delegation is",
15855
+ " in-flight; skip conflict-resolution delegation, log the contention",
15856
+ " to the sticky summary, and fall through to the fallback.",
15857
+ "4. **No conflicting file is generated or projen-managed.** Run",
15858
+ " `gh pr view <pr-number> --json files --jq '.files[].path'` and",
15859
+ " inspect the conflicting paths reported by the failed",
15860
+ " `update-branch`. If **any** conflicting path matches one of the",
15861
+ " following globs, the conflict is unsafe to resolve mechanically",
15862
+ " and the reviewer must skip delegation:",
15863
+ " - `**/*.lock` and `pnpm-lock.yaml` / `yarn.lock` / `package-lock.json`",
15864
+ " - `**/.projen/**`",
15865
+ " - any file whose first 5 lines contain the marker",
15866
+ " `~~ Generated by projen` or `// Generated by`.",
15867
+ " The lockfile guard exists because lockfile conflicts often need a",
15868
+ " regen (`pnpm i`) rather than a textual merge; the projen / generated",
15869
+ " guard exists because those files are derived from `.projenrc.ts`",
15870
+ " and conflicts there should be resolved by re-running synth, not by",
15871
+ " merging the conflict markers.",
15872
+ "",
15873
+ "When every guard above passes, hand off to `issue-worker` with a",
15874
+ "**single synthetic fix-list item** that describes the rebase work:",
15875
+ "",
15876
+ "1. **Disable auto-merge** so a fast CI pass cannot land the PR",
15877
+ " mid-delegation (idempotent \u2014 safe no-op when auto-merge was never",
15878
+ " enabled). This mirrors step (b) of the in-scope-fix flow:",
15879
+ "",
15880
+ " ```bash",
15881
+ " gh pr merge <pr-number> --disable-auto",
15882
+ " ```",
15883
+ "",
15884
+ "2. **Acquire the `review:fixing` lease** by adding the label",
15885
+ " (mirrors step (c) of the in-scope-fix flow). On contention, abort",
15886
+ " the delegation and fall through to the fallback:",
15887
+ "",
15888
+ " ```bash",
15889
+ " gh pr edit <pr-number> --add-label 'review:fixing'",
15890
+ " ```",
15891
+ "",
15892
+ "3. **Post a fix-list comment** containing exactly one synthetic item",
15893
+ " describing the rebase. The `comment_id` is the literal string",
15894
+ " `synthetic:rebase-behind-main` so the worker recognises the",
15895
+ " special case and the next reviewer pass can identify the item:",
15896
+ "",
15897
+ " ```markdown",
15898
+ " ## Reviewer: fix list for @issue-worker",
15899
+ "",
15900
+ " - [ ] @reviewer \u2014 rebase onto origin/<default-branch> and resolve conflicts in: <space-separated list of conflicting files>",
15901
+ "",
15902
+ " ```json fix-list",
15903
+ " {",
15904
+ ' "pr": <pr-number>,',
15905
+ ' "branch": "<head-ref-name>",',
15906
+ ' "generated_at": "<ISO-8601 timestamp>",',
15907
+ ' "items": [',
15908
+ ' {"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."}',
15909
+ " ]",
15910
+ " }",
15911
+ " ```",
15912
+ " ```",
15913
+ "",
15914
+ "4. **Invoke `issue-worker` in feedback mode** with the same prompt",
15915
+ " shape used by the in-scope-fix flow: include the literal phrase",
15916
+ " `feedback mode: PR #<n>` plus the repository identifier",
15917
+ " (`{{repository.owner}}/{{repository.name}}`).",
15918
+ "",
15919
+ "5. **Process the worker's report** for the synthetic item using the",
15920
+ " same logic as step (f) of the in-scope-fix flow \u2014 `handled` reacts",
15921
+ " `rocket` on the fix-list comment; `failed` reacts `thinking_face`",
15922
+ " and posts a reply citing the worker's failure reason.",
15923
+ "",
15924
+ "6. **Release the `review:fixing` lease** with",
15925
+ " `gh pr edit <pr-number> --remove-label 'review:fixing'`. Always",
15926
+ " run this step.",
15927
+ "",
15928
+ "7. **Do not re-enable auto-merge in the same pass.** After delegation,",
15929
+ " exit and let a human re-invoke the reviewer. The next pass will",
15930
+ " re-evaluate `mergeStateStatus` against the rebased branch and",
15931
+ " re-enable auto-merge through the normal flow above.",
15932
+ "",
15933
+ "Record `Branch updated: delegated (PR #<n>)` for the per-PR report",
15934
+ "when delegation completes (regardless of the worker's outcome \u2014 the",
15935
+ "delegation itself is the action taken).",
15936
+ "",
15937
+ "**Fallback when delegation is not permitted or guards fail.** Post a",
15938
+ "short comment explaining why the branch could not be updated",
15939
+ "automatically and stop. Do not push commits, do not force, do not",
15940
+ "retry. Record `Branch updated: failed (conflicts; <short reason>)`.",
15941
+ "",
15942
+ "```bash",
15943
+ "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.'",
15944
+ "```",
15945
+ "",
15923
15946
  "#### Mode `human-required`",
15924
15947
  "",
15925
15948
  "Do **not** run `gh pr merge --auto`. Instead, hand the PR off to a",
@@ -16110,6 +16133,7 @@ var prReviewerSubAgent = {
16110
16133
  " - Nitpick: <items>",
16111
16134
  "",
16112
16135
  "Action taken: <enable-auto-merge | label-awaiting-human | commented-on-the-pr | none>",
16136
+ "Branch updated: <yes | not needed | failed (<reason>) | delegated (PR #<n>) | n/a>",
16113
16137
  "Branch state: <merged | open | closed>",
16114
16138
  "Issue state: <closed | open>",
16115
16139
  "```",
@@ -16175,7 +16199,23 @@ var prReviewerSubAgent = {
16175
16199
  " `review:awaiting-human` label is **not** present on the PR. Any",
16176
16200
  " AC-drift pushback, any failed-fix pushback, and any human-required",
16177
16201
  " hand-off all keep auto-merge disabled until the human resolves",
16178
- " the underlying state."
16202
+ " the underlying state.",
16203
+ "15. **Never run `gh pr update-branch` on a `human-required` PR.**",
16204
+ " Pushing main into a human-required PR expands the scope of the",
16205
+ " diff the human is reviewing without their consent. The",
16206
+ " `update-branch` step is gated to the `Mode auto-merge` branch of",
16207
+ " Phase 4 only. The same restriction applies to delegating",
16208
+ " conflict resolution to `issue-worker`: never delegate a rebase",
16209
+ " on a `human-required` PR.",
16210
+ "16. **Never delegate conflict resolution involving generated or",
16211
+ " projen-managed files.** When `gh pr update-branch` fails on a",
16212
+ " BEHIND PR with conflicts and any conflicting path is a lockfile,",
16213
+ " a `**/.projen/**` file, or a file marked",
16214
+ " `~~ Generated by projen` / `// Generated by`, the reviewer must",
16215
+ " skip the `issue-worker` delegation and fall back to the comment",
16216
+ " pathway. Mechanical merge of generated content is unsafe \u2014 those",
16217
+ " files are derived from `.projenrc.ts` and must be regenerated,",
16218
+ " not merged."
16179
16219
  ].join("\n")
16180
16220
  };
16181
16221
  var reviewPrSkill = {
@@ -27750,7 +27790,6 @@ var AgentConfig = class _AgentConfig extends Component8 {
27750
27790
  if (resolvedRunRatio.enabled) {
27751
27791
  this.project.gitignore.addPatterns(`/${resolvedRunRatio.stateFilePath}`);
27752
27792
  }
27753
- validatePreflightPrConfig(this.options.preflightPr);
27754
27793
  validateUnblockDependentsConfig(this.options.unblockDependents);
27755
27794
  const resolvedProgressFiles = validateProgressFilesConfig(
27756
27795
  this.options.progressFiles
@@ -27967,14 +28006,13 @@ ${section}`
27967
28006
  }
27968
28007
  }
27969
28008
  }
27970
- if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.preflightPr || this.options.scheduledTasks || this.options.unblockDependents) {
28009
+ if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.scheduledTasks || this.options.unblockDependents) {
27971
28010
  const orchestratorRule = ruleMap.get("orchestrator-conventions");
27972
28011
  if (orchestratorRule) {
27973
28012
  const { conventionsContent } = resolveOrchestratorAssets(
27974
28013
  this.options.tiers,
27975
28014
  this.options.scopeGate,
27976
28015
  this.options.runRatio,
27977
- this.options.preflightPr,
27978
28016
  this.options.scheduledTasks,
27979
28017
  this.options.unblockDependents
27980
28018
  );
@@ -28289,14 +28327,13 @@ ${hook}`
28289
28327
  procMap.set(proc.name, proc);
28290
28328
  }
28291
28329
  }
28292
- if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.preflightPr) {
28330
+ if (this.options.tiers || this.options.scopeGate || this.options.runRatio) {
28293
28331
  const existing = procMap.get("check-blocked.sh");
28294
28332
  if (existing) {
28295
28333
  const { procedure } = resolveOrchestratorAssets(
28296
28334
  this.options.tiers,
28297
28335
  this.options.scopeGate,
28298
- this.options.runRatio,
28299
- this.options.preflightPr
28336
+ this.options.runRatio
28300
28337
  );
28301
28338
  if (procedure.content !== existing.content) {
28302
28339
  procMap.set("check-blocked.sh", procedure);
@@ -28311,7 +28348,6 @@ ${hook}`
28311
28348
  void 0,
28312
28349
  void 0,
28313
28350
  void 0,
28314
- void 0,
28315
28351
  this.options.unblockDependents
28316
28352
  );
28317
28353
  if (unblockDependentsProcedure2.content !== existing.content) {
@@ -30748,7 +30784,7 @@ var DEFAULT_AUTO_APPROVE_LABEL = "auto-approve";
30748
30784
  var DEFAULT_HEAD_BRANCH = "github-actions/upgrade";
30749
30785
  var DEFAULT_APPROVAL_APP_ID_SECRET = "APPROVAL_APP_ID";
30750
30786
  var DEFAULT_APPROVAL_APP_PRIVATE_KEY_SECRET = "APPROVAL_APP_PRIVATE_KEY";
30751
- var DEFAULT_MERGE_METHOD2 = MERGE_METHODS.SQUASH;
30787
+ var DEFAULT_MERGE_METHOD = MERGE_METHODS.SQUASH;
30752
30788
  var PULL_REQUEST_TARGET_TYPES = [
30753
30789
  "labeled",
30754
30790
  "synchronize",
@@ -30762,7 +30798,7 @@ function addApproveMergeUpgradeWorkflow(project, options) {
30762
30798
  const allowedUsernames = options.allowedUsernames;
30763
30799
  const appIdSecret = options.approvalAppIdSecret ?? DEFAULT_APPROVAL_APP_ID_SECRET;
30764
30800
  const privateKeySecret = options.approvalAppPrivateKeySecret ?? DEFAULT_APPROVAL_APP_PRIVATE_KEY_SECRET;
30765
- const mergeMethod = options.mergeMethod ?? DEFAULT_MERGE_METHOD2;
30801
+ const mergeMethod = options.mergeMethod ?? DEFAULT_MERGE_METHOD;
30766
30802
  const workflow = project.github?.addWorkflow(workflowName);
30767
30803
  if (!workflow) return;
30768
30804
  workflow.on({
@@ -32637,8 +32673,8 @@ export {
32637
32673
  DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
32638
32674
  DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
32639
32675
  DEFAULT_AUDIT_REPORT_DIR,
32676
+ DEFAULT_BUNDLE_OVERRIDES,
32640
32677
  DEFAULT_DECOMPOSITION_TEMPLATE,
32641
- DEFAULT_DELEGATE_TO_PR_REVIEWER,
32642
32678
  DEFAULT_DISPATCH_MODEL,
32643
32679
  DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
32644
32680
  DEFAULT_HOUSEKEEPING_MODEL,
@@ -32648,7 +32684,6 @@ export {
32648
32684
  DEFAULT_ISSUE_TEMPLATES_ENABLED,
32649
32685
  DEFAULT_ISSUE_TEMPLATES_PATH,
32650
32686
  DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE,
32651
- DEFAULT_MERGE_METHOD,
32652
32687
  DEFAULT_OFF_PEAK_CRON_EXAMPLE,
32653
32688
  DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE,
32654
32689
  DEFAULT_PRIORITY_LABELS,
@@ -32658,7 +32693,6 @@ export {
32658
32693
  DEFAULT_PROGRESS_FILES_FORMAT,
32659
32694
  DEFAULT_PROGRESS_FILES_STALE_AFTER_HOURS,
32660
32695
  DEFAULT_PROGRESS_FILES_STATE_DIR,
32661
- DEFAULT_REQUIRE_LINKED_ISSUE,
32662
32696
  DEFAULT_REQUIRE_PRODUCT_CONTEXT,
32663
32697
  DEFAULT_SAMPLE_COMPILER_OPTIONS,
32664
32698
  DEFAULT_SCHEDULED_TASKS_ROOT,
@@ -32690,7 +32724,6 @@ export {
32690
32724
  MINIMUM_RELEASE_AGE,
32691
32725
  MONOREPO_LAYOUT,
32692
32726
  MonorepoProject,
32693
- PREFLIGHT_MERGE_METHOD_VALUES,
32694
32727
  PROD_DEPLOY_NAME,
32695
32728
  PROGRESS_FILES_FORMAT_VALUES,
32696
32729
  PnpmWorkspace,
@@ -32794,8 +32827,6 @@ export {
32794
32827
  renderIssueTemplatesRuleContent,
32795
32828
  renderIssueTemplatesStarterPage,
32796
32829
  renderMeetingTypesSection,
32797
- renderPreflightPrSection,
32798
- renderPreflightPrShellHelpers,
32799
32830
  renderPriorityRulesSection,
32800
32831
  renderProgressFileName,
32801
32832
  renderProgressFilePath,
@@ -32828,7 +32859,7 @@ export {
32828
32859
  resolveModelAlias,
32829
32860
  resolveOrchestratorAssets,
32830
32861
  resolveOutdirFromPackageName,
32831
- resolvePreflightPr,
32862
+ resolveOverrideForLabels,
32832
32863
  resolveProgressFiles,
32833
32864
  resolveRunRatio,
32834
32865
  resolveScheduledTasks,
@@ -32849,7 +32880,6 @@ export {
32849
32880
  validateAgentTierConfig,
32850
32881
  validateIssueTemplatesConfig,
32851
32882
  validateMonorepoLayout,
32852
- validatePreflightPrConfig,
32853
32883
  validateProgressFilesConfig,
32854
32884
  validateRunRatioConfig,
32855
32885
  validateScheduledTasksConfig,