@codedrifters/configulator 0.0.406 → 0.0.408

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
@@ -1830,12 +1830,111 @@ var awsCdkBundle = {
1830
1830
  ]
1831
1831
  };
1832
1832
 
1833
+ // src/agent/bundles/issue-defaults.ts
1834
+ var VALID_STATUS_VALUES = [
1835
+ "ready",
1836
+ "blocked",
1837
+ "in-progress",
1838
+ "ready-for-review",
1839
+ "needs-attention",
1840
+ "done",
1841
+ "deferred"
1842
+ ];
1843
+ var VALID_PRIORITY_VALUES = [
1844
+ "critical",
1845
+ "high",
1846
+ "medium",
1847
+ "low",
1848
+ "trivial"
1849
+ ];
1850
+ var DEFAULT_ISSUE_STATUS = "ready";
1851
+ var DEFAULT_ISSUE_PRIORITY = "medium";
1852
+ var DEFAULT_RESOLVED_ISSUE_DEFAULTS = {
1853
+ defaults: {
1854
+ status: DEFAULT_ISSUE_STATUS,
1855
+ priority: DEFAULT_ISSUE_PRIORITY
1856
+ },
1857
+ overrides: {}
1858
+ };
1859
+ function resolveIssueDefaults(config) {
1860
+ if (config === void 0) {
1861
+ return DEFAULT_RESOLVED_ISSUE_DEFAULTS;
1862
+ }
1863
+ const overrides = {};
1864
+ for (const [phaseLabel, override] of Object.entries(config)) {
1865
+ assertValidPhaseLabel(phaseLabel);
1866
+ assertValidOverride(phaseLabel, override);
1867
+ overrides[phaseLabel] = {
1868
+ status: override.status ?? DEFAULT_ISSUE_STATUS,
1869
+ priority: override.priority ?? DEFAULT_ISSUE_PRIORITY
1870
+ };
1871
+ }
1872
+ return {
1873
+ defaults: {
1874
+ status: DEFAULT_ISSUE_STATUS,
1875
+ priority: DEFAULT_ISSUE_PRIORITY
1876
+ },
1877
+ overrides
1878
+ };
1879
+ }
1880
+ function validateIssueDefaultsConfig(config) {
1881
+ return resolveIssueDefaults(config);
1882
+ }
1883
+ function labelsForPhase(resolved, phaseLabel) {
1884
+ return resolved.overrides[phaseLabel] ?? resolved.defaults;
1885
+ }
1886
+ function assertValidPhaseLabel(phaseLabel) {
1887
+ if (typeof phaseLabel !== "string" || phaseLabel.trim() === "") {
1888
+ throw new Error(
1889
+ "AgentConfigOptions.issueDefaults: phase-label keys must be non-empty strings (e.g. `people:research`)."
1890
+ );
1891
+ }
1892
+ }
1893
+ function assertValidOverride(phaseLabel, override) {
1894
+ if (override === null || typeof override !== "object" || Array.isArray(override)) {
1895
+ throw new Error(
1896
+ `AgentConfigOptions.issueDefaults["${phaseLabel}"] must be an object with optional \`status\` and \`priority\` fields.`
1897
+ );
1898
+ }
1899
+ const { status, priority } = override;
1900
+ if (status === void 0 && priority === void 0) {
1901
+ throw new Error(
1902
+ `AgentConfigOptions.issueDefaults["${phaseLabel}"] must declare at least one of \`status\` or \`priority\`. Empty entries are rejected because they are almost always a typo on the field name.`
1903
+ );
1904
+ }
1905
+ if (status !== void 0 && !VALID_STATUS_VALUES.includes(status)) {
1906
+ throw new Error(
1907
+ `AgentConfigOptions.issueDefaults["${phaseLabel}"].status="${status}" is not a recognised status value. Allowed values: ${VALID_STATUS_VALUES.join(", ")}.`
1908
+ );
1909
+ }
1910
+ if (priority !== void 0 && !VALID_PRIORITY_VALUES.includes(priority)) {
1911
+ throw new Error(
1912
+ `AgentConfigOptions.issueDefaults["${phaseLabel}"].priority="${priority}" is not a recognised priority value. Allowed values: ${VALID_PRIORITY_VALUES.join(", ")}.`
1913
+ );
1914
+ }
1915
+ }
1916
+
1833
1917
  // src/agent/bundles/issue-templates.ts
1834
1918
  var DEFAULT_ISSUE_TEMPLATES_ENABLED = true;
1835
1919
  var DEFAULT_ISSUE_TEMPLATES_PATH = "docs/src/content/docs/agents/issue-templates.md";
1836
1920
  var DEFAULT_ISSUE_TEMPLATES_BUNDLE_PATH_PATTERNS = [".claude/agents/**/*.md", ".claude/skills/**/*.md"];
1837
1921
  var DEFAULT_ISSUE_TEMPLATES_EMIT_CHECKER = false;
1838
1922
  var DEFAULT_ISSUE_TEMPLATES_EMIT_STARTER = false;
1923
+ var ISSUE_TEMPLATES_GENERATED_SUFFIX = "-generated";
1924
+ function issueTemplatesGeneratedPath(templatesPath) {
1925
+ const slash = templatesPath.lastIndexOf("/");
1926
+ const dot = templatesPath.lastIndexOf(".");
1927
+ if (dot > slash) {
1928
+ return `${templatesPath.slice(0, dot)}${ISSUE_TEMPLATES_GENERATED_SUFFIX}${templatesPath.slice(dot)}`;
1929
+ }
1930
+ return `${templatesPath}${ISSUE_TEMPLATES_GENERATED_SUFFIX}`;
1931
+ }
1932
+ function issueTemplatesChildGlob(templatesPath) {
1933
+ const slash = templatesPath.lastIndexOf("/");
1934
+ const dot = templatesPath.lastIndexOf(".");
1935
+ const stem = dot > slash ? templatesPath.slice(0, dot) : templatesPath;
1936
+ return `${stem}/*.md`;
1937
+ }
1839
1938
  var DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE = true;
1840
1939
  function resolveIssueTemplates(config) {
1841
1940
  const templatesPath = config?.templatesPath ?? DEFAULT_ISSUE_TEMPLATES_PATH;
@@ -1919,7 +2018,40 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
1919
2018
  "issue kind appends one section; retiring a kind removes one. The",
1920
2019
  "label taxonomy, body template shape, and placeholder conventions",
1921
2020
  "are shared across every section.",
1922
- "",
2021
+ ""
2022
+ ];
2023
+ if (it.emitStarterDoc) {
2024
+ lines.push(
2025
+ "## Scaffolded Starting Point",
2026
+ "",
2027
+ "The page is hand-authored, but it does not start empty. Two files",
2028
+ "ship together:",
2029
+ "",
2030
+ `1. \`${it.templatesPath}\` \u2014 **written once**, then yours. Titles,`,
2031
+ " bodies, and the callers list are hand-authored here and never",
2032
+ " overwritten.",
2033
+ `2. \`${issueTemplatesGeneratedPath(it.templatesPath)}\` \u2014`,
2034
+ " **regenerated on every synth**. One",
2035
+ " `## Template: <phase-label>` stub per phase label the active",
2036
+ " bundles contribute to the label registry, with the phase label,",
2037
+ " the `type:<bundle>` label its owning bundle declares, and the",
2038
+ " configured `status:*` / `priority:*` defaults pre-filled.",
2039
+ "",
2040
+ "Only the **label set** is generated. Title and body stay",
2041
+ "angle-bracket placeholders, so the scaffold answers *which labels*",
2042
+ "without dictating *what the issue says*. Copy a stub into the",
2043
+ "hand-authored page, flesh out its body, and leave the label block",
2044
+ "as generated.",
2045
+ "",
2046
+ "The split is load-bearing. A write-once scaffold alone freezes",
2047
+ "every consumer on whatever skeleton shipped the day they adopted",
2048
+ "the convention \u2014 a new phase label minted upstream never reaches",
2049
+ "them. Regenerating the label half fixes that without ever",
2050
+ "clobbering an authored body.",
2051
+ ""
2052
+ );
2053
+ }
2054
+ lines.push(
1923
2055
  "## Reference-Don't-Inline Rule",
1924
2056
  "",
1925
2057
  `Every bundle rule, agent prompt, or skill instruction that files a`,
@@ -1990,8 +2122,12 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
1990
2122
  " placeholders (`<Organization Name>`, `<slug>`) that match",
1991
2123
  " existing templates. Consistency across sections lets callers",
1992
2124
  " substitute without re-reading the body every time.",
2125
+ "5. **Does the recipe assign a GitHub issue type?** `gh issue",
2126
+ " create` cannot set it and the `type:*` label does not either, so",
2127
+ " every section ends with the follow-up call. An issue that skips",
2128
+ " it stays untyped forever.",
1993
2129
  ""
1994
- ];
2130
+ );
1995
2131
  if (it.emitChecker) {
1996
2132
  lines.push(
1997
2133
  "## Automated Lint",
@@ -2022,6 +2158,33 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
2022
2158
  "(which documents the conventional `gh issue create` call shape",
2023
2159
  "as taxonomy, not a downstream template). All other bundle",
2024
2160
  "files in the pattern set are in scope.",
2161
+ "",
2162
+ "### Recipe label correctness",
2163
+ "",
2164
+ "A companion lint,",
2165
+ "`.claude/procedures/check-issue-template-labels.sh`, checks the",
2166
+ "recipes themselves rather than where they live. It walks the",
2167
+ "templates page, its router-style child pages, and the generated",
2168
+ "label-set companion, and fails non-zero when a",
2169
+ "`## Template: <phase-label>` section:",
2170
+ "",
2171
+ "- never passes `--label <phase-label>` \u2014 the heading and the",
2172
+ " command disagree;",
2173
+ "- carries no `type:*` label, more than one, or one that is not",
2174
+ " the `type:<bundle>` label the phase label's owning bundle",
2175
+ " declares;",
2176
+ "- has no GitHub issue-type assignment step.",
2177
+ "",
2178
+ "```bash",
2179
+ ".claude/procedures/check-issue-template-labels.sh",
2180
+ "```",
2181
+ "",
2182
+ "The phase-label \u2192 type-label pairing is generated from the same",
2183
+ "bundle-ownership map that drives `.github/labels.yml` and the",
2184
+ "orchestrator's invariant sweep, so a hand-edit can never",
2185
+ "reintroduce a mismatch the registry has already ruled out.",
2186
+ "Sections whose heading matches no bundle-owned phase label are",
2187
+ "skipped \u2014 consumer-specific labels are deliberately not policed.",
2025
2188
  ""
2026
2189
  );
2027
2190
  }
@@ -2030,8 +2193,10 @@ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
2030
2193
  "",
2031
2194
  "The convention intentionally does not:",
2032
2195
  "",
2033
- "- Auto-generate the templates page \u2014 repo-specific phase labels",
2034
- " and body conventions are too varied for a one-size template.",
2196
+ "- Auto-generate recipe **bodies**. Only the label set is",
2197
+ " scaffolded, because labels are derivable from the bundle",
2198
+ " registry and bodies are not \u2014 repo-specific objectives,",
2199
+ " acceptance criteria, and callers lists stay hand-authored.",
2035
2200
  "- Prescribe a fixed body shape across templates. Each section",
2036
2201
  " may carry its own `## Acceptance Criteria`, `## Scope Size`,",
2037
2202
  " or `## Output Path` fields as the downstream agent requires;",
@@ -2062,7 +2227,8 @@ function renderIssueTemplatesBundleHook(it, bundleLabel) {
2062
2227
  "as an inline template vs. a prose mention."
2063
2228
  ].join("\n");
2064
2229
  }
2065
- function renderIssueTemplatesStarterPage(_it) {
2230
+ function renderIssueTemplatesStarterPage(it) {
2231
+ const generatedLink = `./${basename(issueTemplatesGeneratedPath(it.templatesPath))}`;
2066
2232
  return [
2067
2233
  "---",
2068
2234
  "title: Issue Templates",
@@ -2097,6 +2263,20 @@ function renderIssueTemplatesStarterPage(_it) {
2097
2263
  "bundle's rule \u2014 the issue-templates page is a recipe catalogue,",
2098
2264
  "not a duplicate-check layer.",
2099
2265
  "",
2266
+ "## Generated label sets",
2267
+ "",
2268
+ `[Generated Issue-Template Label Sets](${generatedLink}) carries one`,
2269
+ "correct-by-construction stub per phase label the active bundles",
2270
+ "emit \u2014 the phase label paired with the `type:<bundle>` label its",
2271
+ "owning bundle declares, plus the configured `status:*` /",
2272
+ "`priority:*` defaults. That page regenerates on every `projen` run,",
2273
+ "so it never goes stale as phase labels come and go.",
2274
+ "",
2275
+ "This page is the opposite: written once, then owned by you. Copy a",
2276
+ "stub across, flesh out its title and body here, and leave the label",
2277
+ "block exactly as generated \u2014 the label-consistency lint holds both",
2278
+ "copies to the same pairing.",
2279
+ "",
2100
2280
  "## Template: example-phase",
2101
2281
  "",
2102
2282
  "Replace this example with the real templates for this repo. Each",
@@ -2136,6 +2316,115 @@ function renderIssueTemplatesStarterPage(_it) {
2136
2316
  "```"
2137
2317
  ].join("\n");
2138
2318
  }
2319
+ function collectIssueTemplateRecipeStubs(bundles, issueDefaults = DEFAULT_RESOLVED_ISSUE_DEFAULTS) {
2320
+ const stubs = /* @__PURE__ */ new Map();
2321
+ for (const bundle of bundles) {
2322
+ for (const label of bundle.labels ?? []) {
2323
+ const typeLabel = typeLabelForPhaseLabel(label.name);
2324
+ if (typeLabel === void 0) {
2325
+ continue;
2326
+ }
2327
+ if (stubs.has(label.name)) {
2328
+ continue;
2329
+ }
2330
+ const defaults = labelsForPhase(issueDefaults, label.name);
2331
+ stubs.set(label.name, {
2332
+ phaseLabel: label.name,
2333
+ typeLabel,
2334
+ bundleName: bundle.name,
2335
+ description: label.description ?? "",
2336
+ status: defaults.status,
2337
+ priority: defaults.priority,
2338
+ issueType: githubIssueTypeForTitle(`${label.name}: placeholder`)
2339
+ });
2340
+ }
2341
+ }
2342
+ return [...stubs.values()].sort(
2343
+ (a, b) => a.phaseLabel.localeCompare(b.phaseLabel)
2344
+ );
2345
+ }
2346
+ function renderIssueTemplatesGeneratedPage(it, stubs) {
2347
+ const lines = [
2348
+ "---",
2349
+ "title: Generated Issue-Template Label Sets",
2350
+ "description: Auto-generated label-correct gh issue create stubs \u2014 one per phase label.",
2351
+ "---",
2352
+ "",
2353
+ "**Generated file \u2014 do not edit.** Every section below is",
2354
+ "regenerated from the active bundle set on each `projen` run; hand",
2355
+ "edits are overwritten. The hand-authored recipes live in",
2356
+ `[the issue-templates page](./${basename(it.templatesPath)}) \u2014 this`,
2357
+ "page exists so the **label half** of every recipe stays",
2358
+ "correct-by-construction as phase labels come and go.",
2359
+ "",
2360
+ "## How to use",
2361
+ "",
2362
+ "Each section carries the exact label set the phase-label invariant",
2363
+ "requires: the phase label itself, the `type:<bundle>` label its",
2364
+ "owning bundle declares, and the configured `status:*` / `priority:*`",
2365
+ "defaults for that phase. Title and body are placeholders \u2014 copy the",
2366
+ "stub into the hand-authored templates page and flesh out the body",
2367
+ "there, or run it as-is when no repo-specific body exists yet.",
2368
+ "",
2369
+ "Every stub ends with the **GitHub issue type** assignment. `gh issue",
2370
+ "create` cannot set the issue type and the `type:*` label does not",
2371
+ "set it either, so an issue that skips the follow-up call stays",
2372
+ "untyped forever. See **Assigning the GitHub issue type** in the",
2373
+ "`issue-conventions` rule for the no-helper fallback.",
2374
+ ""
2375
+ ];
2376
+ if (stubs.length === 0) {
2377
+ lines.push(
2378
+ "## No phase labels",
2379
+ "",
2380
+ "No active bundle contributes a phase label, so there is nothing to",
2381
+ "scaffold. Enable a phased-pipeline bundle (or drop it from",
2382
+ "`excludeBundles`) and re-run `projen`."
2383
+ );
2384
+ return lines.join("\n");
2385
+ }
2386
+ for (const stub of stubs) {
2387
+ lines.push(
2388
+ `## Template: ${stub.phaseLabel}`,
2389
+ "",
2390
+ ...stub.description === "" ? [] : [stub.description, ""],
2391
+ `**Owning bundle:** \`${stub.bundleName}\` \u2014 **required type label:**`,
2392
+ `\`${stub.typeLabel}\` (paired by the bundle-ownership map).`,
2393
+ "",
2394
+ "**Callers:** <name the bundles and agents that file this issue>",
2395
+ "",
2396
+ "```bash",
2397
+ "gh issue create \\",
2398
+ ` --title "${stub.phaseLabel}: <short description>" \\`,
2399
+ ` --label "${stub.typeLabel}" \\`,
2400
+ ` --label "${stub.phaseLabel}" \\`,
2401
+ ` --label "priority:${stub.priority}" \\`,
2402
+ ` --label "status:${stub.status}" \\`,
2403
+ ' --body "## Objective',
2404
+ "",
2405
+ "<1-3 sentences describing the work>.",
2406
+ "",
2407
+ "## Context",
2408
+ "",
2409
+ "- **Discovered in:** <source description> (#<parent-issue>)",
2410
+ "",
2411
+ "## Acceptance Criteria",
2412
+ "",
2413
+ "- [ ] <criterion 1>",
2414
+ '"',
2415
+ "```",
2416
+ "",
2417
+ `Then assign the GitHub issue type (\`${stub.issueType}\`):`,
2418
+ "",
2419
+ "```bash",
2420
+ `${SET_ISSUE_TYPE_HELPER_PATH} <issue-number> ${stub.issueType}`,
2421
+ "```",
2422
+ ""
2423
+ );
2424
+ }
2425
+ lines.pop();
2426
+ return lines.join("\n");
2427
+ }
2139
2428
  function renderIssueTemplatesCheckerScript(it) {
2140
2429
  const patternsLiteral = it.bundlePathPatterns.map((p) => ` ${JSON.stringify(p)}`).join("\n");
2141
2430
  return [
@@ -2324,6 +2613,157 @@ function renderIssueTemplatesCheckerScript(it) {
2324
2613
  "exit 0"
2325
2614
  ].join("\n");
2326
2615
  }
2616
+ function renderIssueTemplateLabelsCheckerScript(it) {
2617
+ const generatedPath = issueTemplatesGeneratedPath(it.templatesPath);
2618
+ const childGlob = issueTemplatesChildGlob(it.templatesPath);
2619
+ return [
2620
+ "#!/usr/bin/env bash",
2621
+ "# check-issue-template-labels.sh \u2014 Enforce recipe label correctness.",
2622
+ "#",
2623
+ "# Usage:",
2624
+ "# .claude/procedures/check-issue-template-labels.sh [<file>...]",
2625
+ "#",
2626
+ "# With no arguments the lint walks the templates page, its",
2627
+ "# router-style child pages, and the generated label-set companion.",
2628
+ "# Positional arguments override that default set.",
2629
+ "#",
2630
+ "# For every `## Template: <phase-label>` section it asserts the",
2631
+ "# recipe passes the heading's phase label, carries exactly one",
2632
+ "# `type:*` label matching the phase-label invariant, and assigns a",
2633
+ "# GitHub issue type. Sections whose heading matches no bundle-owned",
2634
+ "# phase label are skipped \u2014 unrecognised labels are consumer-",
2635
+ "# specific and deliberately not policed.",
2636
+ "#",
2637
+ "# The resolver below is generated from the canonical bundle-",
2638
+ "# ownership map \u2014 do not edit by hand; regenerate via",
2639
+ "# `pnpm exec projen`.",
2640
+ "",
2641
+ "set -uo pipefail",
2642
+ "",
2643
+ "err() {",
2644
+ ' printf "check-issue-template-labels.sh: %s\\n" "$*" >&2',
2645
+ "}",
2646
+ "",
2647
+ renderPhaseTypeInvariantShellHelpers(),
2648
+ "",
2649
+ `templates_path=${JSON.stringify(it.templatesPath)}`,
2650
+ `generated_path=${JSON.stringify(generatedPath)}`,
2651
+ `child_glob=${JSON.stringify(childGlob)}`,
2652
+ "",
2653
+ "# Emit one TAB-separated record per `## Template:` section:",
2654
+ "# <file>\\t<line>\\t<phase-label>\\t<comma-joined labels>\\t<0|1 typed>",
2655
+ "# Quote characters are folded to spaces up front so a recipe written",
2656
+ "# with single quotes, double quotes, or none at all parses the same.",
2657
+ "# Fenced blocks are tracked so the `## Objective` heading inside a",
2658
+ "# recipe's --body string never reads as the end of the section.",
2659
+ "scan_file() {",
2660
+ ' local file="$1"',
2661
+ ` tr '\\042\\047' ' ' < "$file" | awk -v FNAME="$file" '`,
2662
+ " function flush() {",
2663
+ ' if (phase != "") {',
2664
+ ' printf "%s\\t%d\\t%s\\t%s\\t%d\\n", FNAME, start, phase, labels, hastype;',
2665
+ " }",
2666
+ ' phase = ""; labels = ""; hastype = 0;',
2667
+ " }",
2668
+ " {",
2669
+ " if ($0 ~ /^[ \\t]*```/) {",
2670
+ " fence = 1 - fence;",
2671
+ ' } else if (fence == 0 && index($0, "## ") == 1) {',
2672
+ " flush();",
2673
+ ' if (index($0, "## Template: ") == 1) {',
2674
+ " phase = substr($0, 14);",
2675
+ ' gsub(/[ \\t`]/, "", phase);',
2676
+ " start = NR;",
2677
+ " }",
2678
+ " next;",
2679
+ " }",
2680
+ ' if (phase == "") next;',
2681
+ " if ($0 ~ /set-issue-type\\.sh/ || $0 ~ /updateIssueIssueType/) {",
2682
+ " hastype = 1;",
2683
+ " }",
2684
+ " line = $0;",
2685
+ " while (match(line, /--label[ \\t]+[^ \\t]+/)) {",
2686
+ " tok = substr(line, RSTART, RLENGTH);",
2687
+ " line = substr(line, RSTART + RLENGTH);",
2688
+ ' sub(/^--label[ \\t]+/, "", tok);',
2689
+ ' gsub(/[\\\\`]/, "", tok);',
2690
+ ' labels = labels tok ",";',
2691
+ " }",
2692
+ " }",
2693
+ " END { flush(); }",
2694
+ " '",
2695
+ "}",
2696
+ "",
2697
+ "# Collect the file list: positional args override the default set.",
2698
+ "files=()",
2699
+ "if [[ $# -gt 0 ]]; then",
2700
+ ' files=("$@")',
2701
+ "else",
2702
+ ' [[ -f "$templates_path" ]] && files+=("$templates_path")',
2703
+ ' [[ -f "$generated_path" ]] && files+=("$generated_path")',
2704
+ " for child in $child_glob; do",
2705
+ ' [[ -f "$child" ]] && files+=("$child")',
2706
+ " done",
2707
+ "fi",
2708
+ "",
2709
+ "if [[ ${#files[@]} -eq 0 ]]; then",
2710
+ " exit 0",
2711
+ "fi",
2712
+ "",
2713
+ "violations=()",
2714
+ "checked=0",
2715
+ "",
2716
+ 'for file in "${files[@]}"; do',
2717
+ ' [[ -f "$file" ]] || continue',
2718
+ " while IFS=$'\\t' read -r rec_file rec_line phase labels hastype; do",
2719
+ ' [[ -z "$phase" ]] && continue',
2720
+ ' required=$(phase_label_type_of "$phase")',
2721
+ ' [[ -z "$required" ]] && continue',
2722
+ " checked=$(( checked + 1 ))",
2723
+ ' case ",${labels}" in',
2724
+ ' *",${phase},"*) ;;',
2725
+ ` *) violations+=("$rec_file:$rec_line: recipe for '$phase' never passes --label $phase") ;;`,
2726
+ " esac",
2727
+ ' type_labels=""',
2728
+ " type_count=0",
2729
+ ' for entry in $(printf "%s" "$labels" | tr , " "); do',
2730
+ ' case "$entry" in',
2731
+ " type:*)",
2732
+ ' type_labels="${type_labels}${entry} "',
2733
+ " type_count=$(( type_count + 1 ))",
2734
+ " ;;",
2735
+ " esac",
2736
+ " done",
2737
+ ' if [[ "$type_count" -eq 0 ]]; then',
2738
+ ` violations+=("$rec_file:$rec_line: recipe for '$phase' carries no type:* label (expected $required)")`,
2739
+ ' elif [[ "$type_count" -gt 1 ]]; then',
2740
+ " violations+=(\"$rec_file:$rec_line: recipe for '$phase' carries ${type_count} type:* labels (${type_labels% }); exactly one is allowed\")",
2741
+ ' elif [[ "${type_labels% }" != "$required" ]]; then',
2742
+ ` violations+=("$rec_file:$rec_line: recipe for '$phase' carries \${type_labels% } but the phase label requires $required")`,
2743
+ " fi",
2744
+ ' if [[ "$hastype" != "1" ]]; then',
2745
+ ` violations+=("$rec_file:$rec_line: recipe for '$phase' has no GitHub issue-type assignment step")`,
2746
+ " fi",
2747
+ ' done < <(scan_file "$file")',
2748
+ "done",
2749
+ "",
2750
+ "if [[ ${#violations[@]} -gt 0 ]]; then",
2751
+ ' err "issue-template recipes disagree with the phase-label invariant:"',
2752
+ ' for v in "${violations[@]}"; do',
2753
+ ' err " - $v"',
2754
+ " done",
2755
+ ` err "each '## Template: <phase-label>' recipe must pass its own phase label, exactly one matching type:<bundle> label, and a set-issue-type.sh (or updateIssueIssueType) step"`,
2756
+ " exit 1",
2757
+ "fi",
2758
+ "",
2759
+ 'printf "check-issue-template-labels.sh: %d recipe(s) OK\\n" "$checked"',
2760
+ "exit 0"
2761
+ ].join("\n");
2762
+ }
2763
+ function basename(path8) {
2764
+ const slash = path8.lastIndexOf("/");
2765
+ return slash === -1 ? path8 : path8.slice(slash + 1);
2766
+ }
2327
2767
  function assertValidTemplatesPath(value) {
2328
2768
  if (typeof value !== "string" || value.trim().length === 0) {
2329
2769
  throw new Error(
@@ -4587,7 +5027,15 @@ var createRuleSkill = {
4587
5027
  "- Use `ruleExtensions` to add project-specific content to existing bundle rules instead of replacing them"
4588
5028
  ].join("\n")
4589
5029
  };
4590
- function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
5030
+ var DEFAULT_BASE_CONVENTIONS = {
5031
+ progressFiles: resolveProgressFiles(),
5032
+ sharedEditing: resolveSharedEditing(),
5033
+ temporalFraming: resolveTemporalFraming(),
5034
+ skillEvals: resolveSkillEvals(),
5035
+ issueTemplates: resolveIssueTemplates(),
5036
+ hasDownstreamIssueKindBundles: true
5037
+ };
5038
+ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS, conventions = DEFAULT_BASE_CONVENTIONS) {
4591
5039
  return {
4592
5040
  name: "base",
4593
5041
  description: "Core rules: project overview, interaction style, and general coding conventions",
@@ -5256,7 +5704,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
5256
5704
  // them via `agentConfig.additionalRulePaths`; other consumers
5257
5705
  // get a clean default.
5258
5706
  filePatterns: [".claude/agents/*.md", ".claude/procedures/**"],
5259
- content: renderProgressFilesRuleContent(resolveProgressFiles()),
5707
+ content: renderProgressFilesRuleContent(conventions.progressFiles),
5260
5708
  platforms: {
5261
5709
  cursor: { exclude: true }
5262
5710
  },
@@ -5267,7 +5715,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
5267
5715
  description: "Shared-editing safety: single-entry deterministic-sort inserts on index files, commit-path verification, and the merge-conflict resolution recipe that keeps concurrent agent sessions from dropping each other's rows on shared registries and feature matrices.",
5268
5716
  scope: AGENT_RULE_SCOPE.FILE_PATTERN,
5269
5717
  filePatterns: DEFAULT_SHARED_INDEX_PATHS,
5270
- content: renderSharedEditingRuleContent(resolveSharedEditing()),
5718
+ content: renderSharedEditingRuleContent(conventions.sharedEditing),
5271
5719
  platforms: {
5272
5720
  cursor: { exclude: true }
5273
5721
  },
@@ -5285,7 +5733,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
5285
5733
  "docs/src/content/docs/standards-research/**/*.md",
5286
5734
  "docs/src/content/docs/customer-research/**/*.md"
5287
5735
  ],
5288
- content: renderTemporalFramingRuleContent(resolveTemporalFraming()),
5736
+ content: renderTemporalFramingRuleContent(conventions.temporalFraming),
5289
5737
  platforms: {
5290
5738
  cursor: { exclude: true }
5291
5739
  },
@@ -5298,7 +5746,7 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
5298
5746
  // Bundle defaults exclude paths into configulator's own
5299
5747
  // source — see comment on progress-file-convention above.
5300
5748
  filePatterns: [".claude/skills/**"],
5301
- content: renderSkillEvalsRuleContent(resolveSkillEvals()),
5749
+ content: renderSkillEvalsRuleContent(conventions.skillEvals),
5302
5750
  platforms: {
5303
5751
  cursor: { exclude: true }
5304
5752
  },
@@ -5315,7 +5763,10 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
5315
5763
  ".claude/agents/*.md",
5316
5764
  `${paths.docsRoot}/agents/issue-templates.md`
5317
5765
  ],
5318
- content: renderIssueTemplatesRuleContent(resolveIssueTemplates()),
5766
+ content: renderIssueTemplatesRuleContent(
5767
+ conventions.issueTemplates,
5768
+ conventions.hasDownstreamIssueKindBundles
5769
+ ),
5319
5770
  platforms: {
5320
5771
  cursor: { exclude: true }
5321
5772
  },
@@ -5326,90 +5777,6 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
5326
5777
  }
5327
5778
  var baseBundle = buildBaseBundle();
5328
5779
 
5329
- // src/agent/bundles/issue-defaults.ts
5330
- var VALID_STATUS_VALUES = [
5331
- "ready",
5332
- "blocked",
5333
- "in-progress",
5334
- "ready-for-review",
5335
- "needs-attention",
5336
- "done",
5337
- "deferred"
5338
- ];
5339
- var VALID_PRIORITY_VALUES = [
5340
- "critical",
5341
- "high",
5342
- "medium",
5343
- "low",
5344
- "trivial"
5345
- ];
5346
- var DEFAULT_ISSUE_STATUS = "ready";
5347
- var DEFAULT_ISSUE_PRIORITY = "medium";
5348
- var DEFAULT_RESOLVED_ISSUE_DEFAULTS = {
5349
- defaults: {
5350
- status: DEFAULT_ISSUE_STATUS,
5351
- priority: DEFAULT_ISSUE_PRIORITY
5352
- },
5353
- overrides: {}
5354
- };
5355
- function resolveIssueDefaults(config) {
5356
- if (config === void 0) {
5357
- return DEFAULT_RESOLVED_ISSUE_DEFAULTS;
5358
- }
5359
- const overrides = {};
5360
- for (const [phaseLabel, override] of Object.entries(config)) {
5361
- assertValidPhaseLabel(phaseLabel);
5362
- assertValidOverride(phaseLabel, override);
5363
- overrides[phaseLabel] = {
5364
- status: override.status ?? DEFAULT_ISSUE_STATUS,
5365
- priority: override.priority ?? DEFAULT_ISSUE_PRIORITY
5366
- };
5367
- }
5368
- return {
5369
- defaults: {
5370
- status: DEFAULT_ISSUE_STATUS,
5371
- priority: DEFAULT_ISSUE_PRIORITY
5372
- },
5373
- overrides
5374
- };
5375
- }
5376
- function validateIssueDefaultsConfig(config) {
5377
- return resolveIssueDefaults(config);
5378
- }
5379
- function labelsForPhase(resolved, phaseLabel) {
5380
- return resolved.overrides[phaseLabel] ?? resolved.defaults;
5381
- }
5382
- function assertValidPhaseLabel(phaseLabel) {
5383
- if (typeof phaseLabel !== "string" || phaseLabel.trim() === "") {
5384
- throw new Error(
5385
- "AgentConfigOptions.issueDefaults: phase-label keys must be non-empty strings (e.g. `people:research`)."
5386
- );
5387
- }
5388
- }
5389
- function assertValidOverride(phaseLabel, override) {
5390
- if (override === null || typeof override !== "object" || Array.isArray(override)) {
5391
- throw new Error(
5392
- `AgentConfigOptions.issueDefaults["${phaseLabel}"] must be an object with optional \`status\` and \`priority\` fields.`
5393
- );
5394
- }
5395
- const { status, priority } = override;
5396
- if (status === void 0 && priority === void 0) {
5397
- throw new Error(
5398
- `AgentConfigOptions.issueDefaults["${phaseLabel}"] must declare at least one of \`status\` or \`priority\`. Empty entries are rejected because they are almost always a typo on the field name.`
5399
- );
5400
- }
5401
- if (status !== void 0 && !VALID_STATUS_VALUES.includes(status)) {
5402
- throw new Error(
5403
- `AgentConfigOptions.issueDefaults["${phaseLabel}"].status="${status}" is not a recognised status value. Allowed values: ${VALID_STATUS_VALUES.join(", ")}.`
5404
- );
5405
- }
5406
- if (priority !== void 0 && !VALID_PRIORITY_VALUES.includes(priority)) {
5407
- throw new Error(
5408
- `AgentConfigOptions.issueDefaults["${phaseLabel}"].priority="${priority}" is not a recognised priority value. Allowed values: ${VALID_PRIORITY_VALUES.join(", ")}.`
5409
- );
5410
- }
5411
- }
5412
-
5413
5780
  // src/agent/bundles/bcm-writer.ts
5414
5781
  function buildBcmWriterSubAgent(paths, issueDefaults) {
5415
5782
  return {
@@ -19106,37 +19473,7 @@ var scanCommand = {
19106
19473
  ""
19107
19474
  ].join("\n")
19108
19475
  };
19109
- var orchestratorBundle = {
19110
- name: "orchestrator",
19111
- description: "Pipeline orchestrator agent for issue triage, PR review, and queue management",
19112
- // Always included by default
19113
- appliesWhen: () => true,
19114
- rules: [
19115
- {
19116
- name: "orchestrator-conventions",
19117
- description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, and per-agent scheduled-task layout",
19118
- scope: AGENT_RULE_SCOPE.FILE_PATTERN,
19119
- // Bundle defaults exclude paths into configulator's own source
19120
- // (only meaningful when configulator is a workspace package).
19121
- // codedrifters/packages restores them via
19122
- // `agentConfig.additionalRulePaths`.
19123
- filePatterns: [
19124
- ".claude/agents/orchestrator.md",
19125
- ".claude/scheduled-tasks/**"
19126
- ],
19127
- content: buildOrchestratorConventionsContent(
19128
- DEFAULT_AGENT_TIERS,
19129
- resolveScopeGate(),
19130
- resolveRunRatio(),
19131
- resolveScheduledTasks(),
19132
- resolveUnblockDependents()
19133
- ),
19134
- platforms: {
19135
- cursor: { exclude: true }
19136
- },
19137
- tags: ["workflow"]
19138
- }
19139
- ],
19476
+ var ORCHESTRATOR_BUNDLE_STATIC = {
19140
19477
  subAgents: [orchestratorSubAgent, issueWorkerSubAgent],
19141
19478
  procedures: [
19142
19479
  checkBlockedProcedure,
@@ -19152,6 +19489,51 @@ var orchestratorBundle = {
19152
19489
  ]
19153
19490
  }
19154
19491
  };
19492
+ var DEFAULT_ORCHESTRATOR_CONVENTIONS = {
19493
+ tiers: DEFAULT_AGENT_TIERS,
19494
+ scopeGate: resolveScopeGate(),
19495
+ runRatio: resolveRunRatio(),
19496
+ scheduledTasks: resolveScheduledTasks(),
19497
+ unblockDependents: resolveUnblockDependents(),
19498
+ excludeBundles: []
19499
+ };
19500
+ function buildOrchestratorBundle(conventions = DEFAULT_ORCHESTRATOR_CONVENTIONS) {
19501
+ return {
19502
+ name: "orchestrator",
19503
+ description: "Pipeline orchestrator agent for issue triage, PR review, and queue management",
19504
+ // Always included by default
19505
+ appliesWhen: () => true,
19506
+ rules: [
19507
+ {
19508
+ name: "orchestrator-conventions",
19509
+ description: "Guidelines for orchestrator agent behavior and pipeline management, including the funnel-tier dispatch sort, scope gate, and per-agent scheduled-task layout",
19510
+ scope: AGENT_RULE_SCOPE.FILE_PATTERN,
19511
+ // Bundle defaults exclude paths into configulator's own source
19512
+ // (only meaningful when configulator is a workspace package).
19513
+ // codedrifters/packages restores them via
19514
+ // `agentConfig.additionalRulePaths`.
19515
+ filePatterns: [
19516
+ ".claude/agents/orchestrator.md",
19517
+ ".claude/scheduled-tasks/**"
19518
+ ],
19519
+ content: buildOrchestratorConventionsContent(
19520
+ conventions.tiers,
19521
+ conventions.scopeGate,
19522
+ conventions.runRatio,
19523
+ conventions.scheduledTasks,
19524
+ conventions.unblockDependents,
19525
+ conventions.excludeBundles
19526
+ ),
19527
+ platforms: {
19528
+ cursor: { exclude: true }
19529
+ },
19530
+ tags: ["workflow"]
19531
+ }
19532
+ ],
19533
+ ...ORCHESTRATOR_BUNDLE_STATIC
19534
+ };
19535
+ }
19536
+ var orchestratorBundle = buildOrchestratorBundle();
19155
19537
 
19156
19538
  // src/agent/bundles/people-profile.ts
19157
19539
  function buildPeopleProfileAnalystSubAgent(paths, issueDefaults, tier) {
@@ -32662,6 +33044,8 @@ var UPSTREAM_CONFIGULATOR_DOCS_DETAIL = [
32662
33044
  " --body $'## Summary\\n\\nThe `software-profile-analyst` Phase 2 prompt does not capture the vendor pricing tier (free / paid / enterprise), which downstream consumers need for segment matching.\\n\\n## Details\\n\\n- Add a `Pricing tier` field to the profile template at `docs/src/content/docs/agents/software-profile-analyst.md`\\n- Update the Phase 2 instructions to require the field before commit\\n- Add a regression test in `bundles.test.ts`'",
32663
33045
  "```",
32664
33046
  "",
33047
+ "The call above does **not** set the GitHub issue type \u2014 `gh issue create` cannot. Assign it immediately, in the same session, with the cross-repo GraphQL flow above (`feat:` title \u2192 **Feature**; every other prefix except `epic:` / `fix:` \u2192 **Task**). Skipping it leaves the upstream issue untyped, and nobody comes back for it later.",
33048
+ "",
32665
33049
  "After filing, reference the upstream issue from any local workaround so it can be reverted once the upstream fix lands:",
32666
33050
  "",
32667
33051
  "```typescript",
@@ -33623,10 +34007,14 @@ function renderPriorityRulesSection(rules) {
33623
34007
  }
33624
34008
 
33625
34009
  // src/agent/bundles/index.ts
33626
- function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAULT_RESOLVED_ISSUE_DEFAULTS, defaultAgentTier = AGENT_MODEL.BALANCED, bundleAgentTiers = /* @__PURE__ */ new Map(), prReviewPolicy = resolvePrReviewPolicy(), buildPolicy = DEFAULT_BUILD_POLICY) {
34010
+ var DEFAULT_RULE_CONVENTIONS = {
34011
+ base: DEFAULT_BASE_CONVENTIONS,
34012
+ orchestrator: DEFAULT_ORCHESTRATOR_CONVENTIONS
34013
+ };
34014
+ function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAULT_RESOLVED_ISSUE_DEFAULTS, defaultAgentTier = AGENT_MODEL.BALANCED, bundleAgentTiers = /* @__PURE__ */ new Map(), prReviewPolicy = resolvePrReviewPolicy(), buildPolicy = DEFAULT_BUILD_POLICY, conventions = DEFAULT_RULE_CONVENTIONS) {
33627
34015
  const tierFor = (bundle) => bundleAgentTiers.get(bundle) ?? defaultAgentTier;
33628
34016
  return [
33629
- buildBaseBundle(paths),
34017
+ buildBaseBundle(paths, conventions.base),
33630
34018
  upstreamConfigulatorDocsBundle,
33631
34019
  typescriptBundle,
33632
34020
  vitestBundle,
@@ -33639,7 +34027,7 @@ function buildBuiltInBundles(paths = DEFAULT_AGENT_PATHS, issueDefaults = DEFAUL
33639
34027
  slackBundle,
33640
34028
  buildMeetingAnalysisBundle(tierFor("meeting-analysis")),
33641
34029
  agendaBundle,
33642
- orchestratorBundle,
34030
+ buildOrchestratorBundle(conventions.orchestrator),
33643
34031
  buildPrReviewBundle(prReviewPolicy),
33644
34032
  buildRequirementsAnalystBundle(paths, issueDefaults),
33645
34033
  buildRequirementsWriterBundle(paths, issueDefaults),
@@ -34727,6 +35115,13 @@ var AgentConfig = class _AgentConfig extends Component8 {
34727
35115
  * credential requirement when a remote cache actually exists. The
34728
35116
  * getter is lazy by design — `TurboRepo` must already be attached
34729
35117
  * to the project when the bundles are first read.
35118
+ *
35119
+ * Every **config-driven convention rule** is likewise resolved here
35120
+ * and seeded into its owning bundle, so the rule enters the rule map
35121
+ * already carrying the consumer's settings. Rewriting those rules
35122
+ * after the map was assembled — the previous approach — silently
35123
+ * discarded any `ruleExtensions` append or same-name `rules`
35124
+ * override that had already been merged in.
34730
35125
  */
34731
35126
  get pathAwareBundles() {
34732
35127
  if (!this.cachedBundles) {
@@ -34736,11 +35131,52 @@ var AgentConfig = class _AgentConfig extends Component8 {
34736
35131
  resolveDefaultAgentTier(this.options),
34737
35132
  resolveBundleAgentTiers(this.options),
34738
35133
  resolvePrReviewPolicy(this.options.prReviewPolicy),
34739
- resolveBuildPolicy(this.project)
35134
+ resolveBuildPolicy(this.project),
35135
+ this.resolvedRuleConventions
34740
35136
  );
34741
35137
  }
34742
35138
  return this.cachedBundles;
34743
35139
  }
35140
+ /**
35141
+ * Resolved settings for every config-driven convention rule, derived
35142
+ * from this project's options. Consumed by `buildBuiltInBundles` so
35143
+ * the `base` and `orchestrator` bundles seed final rule content.
35144
+ *
35145
+ * `excludeBundles` feeds two of these: the orchestrator's rendered
35146
+ * tier table / scope-gate overrides / scheduled-tasks registry drop
35147
+ * rows owned by excluded bundles, and the issue-templates rule falls
35148
+ * back to its disabled stub once every downstream-issue-kind bundle
35149
+ * has been excluded.
35150
+ */
35151
+ get resolvedRuleConventions() {
35152
+ const excludeBundles = this.options.excludeBundles ?? [];
35153
+ const orchestratorAssets = resolveOrchestratorAssets(
35154
+ this.options.tiers,
35155
+ this.options.scopeGate,
35156
+ this.options.runRatio,
35157
+ this.options.scheduledTasks,
35158
+ this.options.unblockDependents,
35159
+ excludeBundles
35160
+ );
35161
+ return {
35162
+ base: {
35163
+ progressFiles: resolveProgressFiles(this.options.progressFiles),
35164
+ sharedEditing: resolveSharedEditing(this.options.sharedEditing),
35165
+ temporalFraming: resolveTemporalFraming(this.options.temporalFraming),
35166
+ skillEvals: resolveSkillEvals(this.options.skillEvals),
35167
+ issueTemplates: resolveIssueTemplates(this.options.issueTemplates),
35168
+ hasDownstreamIssueKindBundles: hasAnyDownstreamIssueKindBundle(excludeBundles)
35169
+ },
35170
+ orchestrator: {
35171
+ tiers: orchestratorAssets.tiers,
35172
+ scopeGate: orchestratorAssets.scopeGate,
35173
+ runRatio: orchestratorAssets.runRatio,
35174
+ scheduledTasks: orchestratorAssets.scheduledTasks,
35175
+ unblockDependents: orchestratorAssets.unblockDependents,
35176
+ excludeBundles
35177
+ }
35178
+ };
35179
+ }
34744
35180
  /**
34745
35181
  * Returns the bundles that are active for this project: auto-detected
34746
35182
  * bundles (when `autoDetectBundles !== false`) plus force-included
@@ -34838,11 +35274,34 @@ var AgentConfig = class _AgentConfig extends Component8 {
34838
35274
  ).split("\n"),
34839
35275
  executable: true
34840
35276
  });
35277
+ new TextFile4(
35278
+ this,
35279
+ ".claude/procedures/check-issue-template-labels.sh",
35280
+ {
35281
+ lines: renderIssueTemplateLabelsCheckerScript(
35282
+ resolvedIssueTemplates
35283
+ ).split("\n"),
35284
+ executable: true
35285
+ }
35286
+ );
34841
35287
  }
34842
35288
  if (resolvedIssueTemplates.emitStarterDoc) {
34843
35289
  new SampleFile2(this.project, resolvedIssueTemplates.templatesPath, {
34844
35290
  contents: renderIssueTemplatesStarterPage(resolvedIssueTemplates)
34845
35291
  });
35292
+ new TextFile4(
35293
+ this,
35294
+ issueTemplatesGeneratedPath(resolvedIssueTemplates.templatesPath),
35295
+ {
35296
+ lines: renderIssueTemplatesGeneratedPage(
35297
+ resolvedIssueTemplates,
35298
+ collectIssueTemplateRecipeStubs(
35299
+ this.activeBundles,
35300
+ resolveIssueDefaults(this.options.issueDefaults)
35301
+ )
35302
+ ).split("\n")
35303
+ }
35304
+ );
34846
35305
  }
34847
35306
  }
34848
35307
  const resolvedTemporalFraming = validateTemporalFramingConfig(
@@ -35032,43 +35491,10 @@ ${section}`
35032
35491
  }
35033
35492
  }
35034
35493
  const excludedBundleNames = this.options.excludeBundles ?? [];
35035
- if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.scheduledTasks || this.options.unblockDependents || excludedBundleNames.length > 0) {
35036
- const orchestratorRule = ruleMap.get("orchestrator-conventions");
35037
- if (orchestratorRule) {
35038
- const { conventionsContent } = resolveOrchestratorAssets(
35039
- this.options.tiers,
35040
- this.options.scopeGate,
35041
- this.options.runRatio,
35042
- this.options.scheduledTasks,
35043
- this.options.unblockDependents,
35044
- excludedBundleNames
35045
- );
35046
- if (conventionsContent !== orchestratorRule.content) {
35047
- ruleMap.set("orchestrator-conventions", {
35048
- ...orchestratorRule,
35049
- content: conventionsContent
35050
- });
35051
- }
35052
- }
35053
- }
35054
35494
  const injectBundleHooks = this.options.claudeMd?.injectBundleHooks ?? true;
35055
35495
  const resolvedProgressFiles = resolveProgressFiles(
35056
35496
  this.options.progressFiles
35057
35497
  );
35058
- if (this.options.progressFiles) {
35059
- const progressRule = ruleMap.get("progress-file-convention");
35060
- if (progressRule) {
35061
- const progressContent = renderProgressFilesRuleContent(
35062
- resolvedProgressFiles
35063
- );
35064
- if (progressContent !== progressRule.content) {
35065
- ruleMap.set("progress-file-convention", {
35066
- ...progressRule,
35067
- content: progressContent
35068
- });
35069
- }
35070
- }
35071
- }
35072
35498
  if (injectBundleHooks && resolvedProgressFiles.enabled) {
35073
35499
  for (const [ruleName, label] of PROGRESS_FILE_BUNDLE_HOOKS) {
35074
35500
  const existing = ruleMap.get(ruleName);
@@ -35095,20 +35521,6 @@ ${hook}`
35095
35521
  const resolvedSharedEditingForRules = resolveSharedEditing(
35096
35522
  this.options.sharedEditing
35097
35523
  );
35098
- if (this.options.sharedEditing) {
35099
- const sharedEditingRule = ruleMap.get("shared-editing-safety");
35100
- if (sharedEditingRule) {
35101
- const sharedEditingContent = renderSharedEditingRuleContent(
35102
- resolvedSharedEditingForRules
35103
- );
35104
- if (sharedEditingContent !== sharedEditingRule.content) {
35105
- ruleMap.set("shared-editing-safety", {
35106
- ...sharedEditingRule,
35107
- content: sharedEditingContent
35108
- });
35109
- }
35110
- }
35111
- }
35112
35524
  if (injectBundleHooks && resolvedSharedEditingForRules.enabled) {
35113
35525
  for (const [ruleName, label] of SHARED_EDITING_BUNDLE_HOOKS) {
35114
35526
  const existing = ruleMap.get(ruleName);
@@ -35135,20 +35547,6 @@ ${hook}`
35135
35547
  const resolvedSkillEvalsForRules = resolveSkillEvals(
35136
35548
  this.options.skillEvals
35137
35549
  );
35138
- if (this.options.skillEvals) {
35139
- const skillEvalsRule = ruleMap.get("skill-evals");
35140
- if (skillEvalsRule) {
35141
- const skillEvalsContent = renderSkillEvalsRuleContent(
35142
- resolvedSkillEvalsForRules
35143
- );
35144
- if (skillEvalsContent !== skillEvalsRule.content) {
35145
- ruleMap.set("skill-evals", {
35146
- ...skillEvalsRule,
35147
- content: skillEvalsContent
35148
- });
35149
- }
35150
- }
35151
- }
35152
35550
  if (injectBundleHooks && resolvedSkillEvalsForRules.enabled) {
35153
35551
  for (const [ruleName, label] of SKILL_EVALS_BUNDLE_HOOKS) {
35154
35552
  const existing = ruleMap.get(ruleName);
@@ -35176,21 +35574,6 @@ ${hook}`
35176
35574
  this.options.issueTemplates
35177
35575
  );
35178
35576
  const hasDownstreamBundles = hasAnyDownstreamIssueKindBundle(excludedBundleNames);
35179
- if (this.options.issueTemplates || !hasDownstreamBundles) {
35180
- const issueTemplatesRule = ruleMap.get("issue-templates-convention");
35181
- if (issueTemplatesRule) {
35182
- const issueTemplatesContent = renderIssueTemplatesRuleContent(
35183
- resolvedIssueTemplatesForRules,
35184
- hasDownstreamBundles
35185
- );
35186
- if (issueTemplatesContent !== issueTemplatesRule.content) {
35187
- ruleMap.set("issue-templates-convention", {
35188
- ...issueTemplatesRule,
35189
- content: issueTemplatesContent
35190
- });
35191
- }
35192
- }
35193
- }
35194
35577
  if (injectBundleHooks && resolvedIssueTemplatesForRules.enabled && hasDownstreamBundles) {
35195
35578
  for (const [ruleName, label] of ISSUE_TEMPLATES_BUNDLE_HOOKS) {
35196
35579
  const existing = ruleMap.get(ruleName);
@@ -35214,23 +35597,6 @@ ${hook}`
35214
35597
  });
35215
35598
  }
35216
35599
  }
35217
- const resolvedTemporalFramingForRules = resolveTemporalFraming(
35218
- this.options.temporalFraming
35219
- );
35220
- if (this.options.temporalFraming) {
35221
- const temporalFramingRule = ruleMap.get("temporal-framing-convention");
35222
- if (temporalFramingRule) {
35223
- const temporalFramingContent = renderTemporalFramingRuleContent(
35224
- resolvedTemporalFramingForRules
35225
- );
35226
- if (temporalFramingContent !== temporalFramingRule.content) {
35227
- ruleMap.set("temporal-framing-convention", {
35228
- ...temporalFramingRule,
35229
- content: temporalFramingContent
35230
- });
35231
- }
35232
- }
35233
- }
35234
35600
  const tierExamples = this.options.features?.sourceTierExamples;
35235
35601
  if (_AgentConfig.hasActiveTierExamples(tierExamples)) {
35236
35602
  const sourceRule = ruleMap.get("source-quality-verification");
@@ -41455,6 +41821,7 @@ export {
41455
41821
  DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
41456
41822
  DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
41457
41823
  DEFAULT_AUDIT_REPORT_DIR,
41824
+ DEFAULT_BASE_CONVENTIONS,
41458
41825
  DEFAULT_BUILD_POLICY,
41459
41826
  DEFAULT_BUNDLE_OVERRIDES,
41460
41827
  DEFAULT_DECOMPOSITION_TEMPLATE,
@@ -41471,6 +41838,7 @@ export {
41471
41838
  DEFAULT_ISSUE_TEMPLATES_PATH,
41472
41839
  DEFAULT_ISSUE_TEMPLATES_REQUIRE_REFERENCE,
41473
41840
  DEFAULT_OFF_PEAK_CRON_EXAMPLE,
41841
+ DEFAULT_ORCHESTRATOR_CONVENTIONS,
41474
41842
  DEFAULT_PARTIAL_UNBLOCK_COMMENT_TEMPLATE,
41475
41843
  DEFAULT_PATHS_EXEMPT_FROM_SIZE,
41476
41844
  DEFAULT_PRIORITY_LABELS,
@@ -41483,6 +41851,7 @@ export {
41483
41851
  DEFAULT_REQUIREMENT_CATEGORY_DIRS,
41484
41852
  DEFAULT_REQUIRE_PRODUCT_CONTEXT,
41485
41853
  DEFAULT_RESOLVED_ISSUE_DEFAULTS,
41854
+ DEFAULT_RULE_CONVENTIONS,
41486
41855
  DEFAULT_SAMPLE_COMPILER_OPTIONS,
41487
41856
  DEFAULT_SCHEDULED_TASKS_ROOT,
41488
41857
  DEFAULT_SCHEDULED_TASK_ENTRIES,
@@ -41509,6 +41878,7 @@ export {
41509
41878
  DOCS_SYNC_AUDIT_SCHEMA_VERSION,
41510
41879
  GITHUB_ISSUE_TYPES,
41511
41880
  GITHUB_ISSUE_TYPE_BY_TITLE_PREFIX,
41881
+ ISSUE_TEMPLATES_GENERATED_SUFFIX,
41512
41882
  JsiiFaker,
41513
41883
  LAYOUT_ENFORCEMENT,
41514
41884
  LAYOUT_ROOT_BY_PROJECT_TYPE,
@@ -41585,6 +41955,7 @@ export {
41585
41955
  buildIndustryDiscoveryBundle,
41586
41956
  buildMaintenanceAuditBundle,
41587
41957
  buildMeetingAnalysisBundle,
41958
+ buildOrchestratorBundle,
41588
41959
  buildOrchestratorConventionsContent,
41589
41960
  buildPeopleProfileBundle,
41590
41961
  buildPrReviewBundle,
@@ -41604,6 +41975,7 @@ export {
41604
41975
  checkLinksProcedure,
41605
41976
  classifyIssueScope,
41606
41977
  classifyRun,
41978
+ collectIssueTemplateRecipeStubs,
41607
41979
  companyProfileBundle,
41608
41980
  compileFencedSamples,
41609
41981
  createApiDiffCheck,
@@ -41629,6 +42001,8 @@ export {
41629
42001
  isScheduledTaskOwnedByExcluded,
41630
42002
  isSuppressedWorkflowRule,
41631
42003
  isTypeLabelOwnedByExcluded,
42004
+ issueTemplatesChildGlob,
42005
+ issueTemplatesGeneratedPath,
41632
42006
  jestBundle,
41633
42007
  labelsForPhase,
41634
42008
  maintenanceAuditBundle,
@@ -41679,8 +42053,10 @@ export {
41679
42053
  renderFocusSection,
41680
42054
  renderGithubIssueTypeSection,
41681
42055
  renderGithubIssueTypeSectionLines,
42056
+ renderIssueTemplateLabelsCheckerScript,
41682
42057
  renderIssueTemplatesBundleHook,
41683
42058
  renderIssueTemplatesCheckerScript,
42059
+ renderIssueTemplatesGeneratedPage,
41684
42060
  renderIssueTemplatesRuleContent,
41685
42061
  renderIssueTemplatesStarterPage,
41686
42062
  renderIssueTypeAssignmentBlanket,