@codedrifters/configulator 0.0.289 → 0.0.291

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
@@ -585,13 +585,16 @@ var agendaAnalystSubAgent = {
585
585
  " `duration_min` \xB1 5 minutes. If it doesn't, adjust section times",
586
586
  " or cut content \u2014 do not ship a math-broken agenda.",
587
587
  "",
588
- "7. **Create the folder `index.md` if missing.** Populate it with:",
588
+ "7. **Create the folder `index.md` if missing.** Populate it",
589
+ " following the `stub-index-convention` rule:",
589
590
  " - Frontmatter `title` (short sidebar label) and `description`",
590
591
  " (one line).",
591
592
  " - A 2\u20134 sentence summary of the meeting's objective and desired",
592
593
  " outcomes pulled from the agenda.",
593
594
  " - A `## Documents` section linking to `./agenda.md` (and",
594
595
  " `./notes.md` once the meeting-analyst produces it).",
596
+ " - No body `# Heading` \u2014 the frontmatter `title:` already",
597
+ " renders as the page H1.",
595
598
  "",
596
599
  " Do NOT set `sidebar.hidden: true` on `index.md` \u2014 it is the page",
597
600
  " that should appear in the sidebar.",
@@ -1355,8 +1358,6 @@ function renderIssueTemplatesStarterPage(_it) {
1355
1358
  "description: Canonical gh issue create recipes \u2014 one per downstream phase label.",
1356
1359
  "---",
1357
1360
  "",
1358
- "# Issue Templates",
1359
- "",
1360
1361
  "This page lists the canonical `gh issue create` recipe for every",
1361
1362
  "downstream issue kind dispatched by an agent in this monorepo.",
1362
1363
  "Bundles and agent prompts cite the matching section below instead",
@@ -2791,6 +2792,71 @@ function assertValidProductContextPath(value) {
2791
2792
  }
2792
2793
  }
2793
2794
 
2795
+ // src/agent/bundles/stub-index-convention.ts
2796
+ function renderStubIndexConventionRuleContent() {
2797
+ return [
2798
+ "# Section Index Pages",
2799
+ "",
2800
+ "When any agent creates or updates an `index.md` (or `README.md`)",
2801
+ "in a docs subdirectory under a Starlight content root, the file's",
2802
+ "body must include:",
2803
+ "",
2804
+ "1. **A 1\u20132 paragraph summary** of the section's purpose and how",
2805
+ " it fits into the larger research, requirements, or capability",
2806
+ " area. Readers landing on the page should understand what's in",
2807
+ " the section without falling back to the sidebar.",
2808
+ "",
2809
+ "2. **A grouped, linked listing of the directory's children**",
2810
+ " (table or bullet list). When a natural taxonomy exists (e.g.",
2811
+ " organizations grouped by sub-segment, regulations grouped by",
2812
+ " jurisdiction, capabilities grouped by tier), use it. Otherwise",
2813
+ " sort alphabetically by title. Every listing entry must link",
2814
+ " to the child page.",
2815
+ "",
2816
+ "3. **No body `# Heading`.** Starlight renders the frontmatter",
2817
+ " `title:` as the page H1 automatically \u2014 a body H1 produces a",
2818
+ " duplicate. The same no-body-H1 contract that applies to skill",
2819
+ " templates and inline agent templates also applies to every",
2820
+ " index page emitted by an agent.",
2821
+ "",
2822
+ "## When this applies",
2823
+ "",
2824
+ "The convention applies to every `index.md` / `README.md` file",
2825
+ "covered by the shared-index path globs documented in the",
2826
+ "`shared-editing-safety` rule:",
2827
+ "",
2828
+ "- `docs/src/content/docs/**/index.md`",
2829
+ "- `docs/src/content/docs/**/README.md`",
2830
+ "",
2831
+ "Any agent that scaffolds a new directory under a Starlight",
2832
+ "content root MUST populate the directory's index page following",
2833
+ "this contract \u2014 a one-sentence stub is not acceptable.",
2834
+ "",
2835
+ "## Reference shapes",
2836
+ "",
2837
+ "Existing rich indexes are the canonical reference shape. Examples:",
2838
+ "",
2839
+ "- A `<MEETINGS_ROOT>/<YYYY-MM-DD>-<slug>/index.md` populated by",
2840
+ " the `agenda-analyst` bundle: frontmatter `title` /",
2841
+ " `description`, a 2\u20134 sentence summary, and a `## Documents`",
2842
+ " section listing every page in the folder.",
2843
+ "- A regulations scope index that opens with two paragraphs of",
2844
+ " context and groups every linked regulation under a",
2845
+ " `## Regulations` table by jurisdiction.",
2846
+ "- A standards-organizations index that groups every linked",
2847
+ " organization under a `## Organizations` heading by role",
2848
+ " (governance, working group, implementer).",
2849
+ "",
2850
+ "## Out of scope",
2851
+ "",
2852
+ "- Auto-generated child listings via Astro/Starlight components.",
2853
+ " Agents emit hand-curated tables or bullet lists; the rule",
2854
+ " defines the shape, not the rendering technology.",
2855
+ "- Backfilling pre-existing stub indexes is a downstream-consumer",
2856
+ " cleanup task, not a configulator change."
2857
+ ].join("\n");
2858
+ }
2859
+
2794
2860
  // src/agent/bundles/base.ts
2795
2861
  var createPackageSkill = {
2796
2862
  name: "create-package",
@@ -3876,6 +3942,16 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
3876
3942
  },
3877
3943
  tags: ["workflow"]
3878
3944
  },
3945
+ {
3946
+ name: "stub-index-convention",
3947
+ description: "Section-index convention: every agent-emitted `index.md` / `README.md` under a Starlight docs root carries a contextual summary plus a grouped, linked listing of the directory's children \u2014 and no body `# Heading` (Starlight renders the frontmatter `title:` as the page H1).",
3948
+ scope: AGENT_RULE_SCOPE.ALWAYS,
3949
+ content: renderStubIndexConventionRuleContent(),
3950
+ platforms: {
3951
+ cursor: { exclude: true }
3952
+ },
3953
+ tags: ["workflow"]
3954
+ },
3879
3955
  {
3880
3956
  name: "skill-evals",
3881
3957
  description: "Skill eval harness contract: declarative prompt/expected-output regression suites per skill at `<skillsRoot>/<skill-name>/evals/evals.json`, parameterised by a shared product-context fixture so the same eval shape works across every configulator-consuming project without forking fixtures.",
@@ -4151,8 +4227,6 @@ function buildBcmWriterSubAgent(paths) {
4151
4227
  " tier: L1 | L2 | L3",
4152
4228
  " ---",
4153
4229
  "",
4154
- " # BCM Outline: <Capability Name>",
4155
- "",
4156
4230
  " ## Capability Name",
4157
4231
  " <noun or noun phrase \u2014 never a verb>",
4158
4232
  "",
@@ -4232,8 +4306,6 @@ function buildBcmWriterSubAgent(paths) {
4232
4306
  " date: YYYY-MM-DD",
4233
4307
  " ---",
4234
4308
  "",
4235
- " # <Capability Name>",
4236
- "",
4237
4309
  " ## Capability Definitions",
4238
4310
  " <1\u20132 sentences from the outline's Proposed Definition, refined>",
4239
4311
  "",
@@ -4643,8 +4715,6 @@ title: "Business Model: <Segment Name>"
4643
4715
  description: "Business Model Canvas analysis for the <segment name> segment of the <industry> industry."
4644
4716
  ---
4645
4717
 
4646
- # Business Model: <Segment Name>
4647
-
4648
4718
  | Field | Value |
4649
4719
  |-------|-------|
4650
4720
  | **Industry** | <industry name> |
@@ -5028,8 +5098,6 @@ function buildBusinessModelsAnalystSubAgent(paths) {
5028
5098
  " status: complete",
5029
5099
  " ---",
5030
5100
  "",
5031
- " # Business Model Scan: <Industry Name>",
5032
- "",
5033
5101
  " ## Source Inputs",
5034
5102
  " - Industry plan: <relative path>",
5035
5103
  " - Prior research: <relative path> (optional)",
@@ -5117,8 +5185,12 @@ function buildBusinessModelsAnalystSubAgent(paths) {
5117
5185
  "",
5118
5186
  "7. **Create the segment index** at",
5119
5187
  " `<BUSINESS_MODELS_ROOT>/<industry>/segments/<SEGMENT_SLUG>/index.md`",
5120
- " if it does not already exist. The index should carry a short",
5121
- " description and link to `./business-model.md`.",
5188
+ " if it does not already exist. Follow the",
5189
+ " `stub-index-convention` rule: a 1\u20132 paragraph summary of the",
5190
+ " segment plus a linked listing of every page in the folder",
5191
+ " (`./business-model.md` and any value-stream / capability pages",
5192
+ " that land later). No body `# Heading` \u2014 the frontmatter",
5193
+ " `title:` already renders as the page H1.",
5122
5194
  "",
5123
5195
  "8. **Create one `business-models:complete` issue** with",
5124
5196
  " `Depends on: #<canvas-issue>`. Its body references the path to",
@@ -5909,8 +5981,6 @@ function buildCompanyProfileAnalystSubAgent(paths) {
5909
5981
  " parent_issue: <N>",
5910
5982
  " ---",
5911
5983
  "",
5912
- " # Research Notes: <company name>",
5913
- "",
5914
5984
  " ## Framing",
5915
5985
  " <why this company was requested and what to learn>",
5916
5986
  "",
@@ -5972,8 +6042,6 @@ function buildCompanyProfileAnalystSubAgent(paths) {
5972
6042
  " referencedIn: []",
5973
6043
  " ---",
5974
6044
  "",
5975
- " # <company name>",
5976
- "",
5977
6045
  " ## Summary",
5978
6046
  " <2\u20134 sentence elevator description: what they do, who they sell to>",
5979
6047
  "",
@@ -6384,8 +6452,6 @@ function buildCompanyProfileAnalystSubAgent(paths) {
6384
6452
  " - <relative path to profile 2>",
6385
6453
  " ---",
6386
6454
  "",
6387
- " # <segment name> Competitive Analysis",
6388
- "",
6389
6455
  " ## Market Overview",
6390
6456
  " <landscape summary \u2014 total players, market structure, trends",
6391
6457
  " visible from the profiles>",
@@ -6948,8 +7014,6 @@ archetype: <ARCHETYPE_SLUG>
6948
7014
  segment: <SEGMENT_SLUG>
6949
7015
  ---
6950
7016
 
6951
- # <Customer Archetype Name>
6952
-
6953
7017
  | Field | Value |
6954
7018
  |-------|-------|
6955
7019
  | **Archetype** | <short noun phrase identifying the archetype> |
@@ -7315,8 +7379,6 @@ function buildCustomerProfileAnalystSubAgent(paths) {
7315
7379
  " scope: <SCOPE_SLUG>",
7316
7380
  " ---",
7317
7381
  "",
7318
- " # Customer Discovery: <Scope>",
7319
- "",
7320
7382
  " ## Scope Context",
7321
7383
  " - **Scope:** <SCOPE_SLUG>",
7322
7384
  " - **Sources scanned:** <counts by source type>",
@@ -7462,8 +7524,6 @@ function buildCustomerProfileAnalystSubAgent(paths) {
7462
7524
  " archetype: <ARCHETYPE_SLUG>",
7463
7525
  " ---",
7464
7526
  "",
7465
- " # Competitors: <Archetype Name>",
7466
- "",
7467
7527
  " ## Market Overview",
7468
7528
  " <Brief overview of the competitive landscape this archetype",
7469
7529
  " evaluates.>",
@@ -9370,8 +9430,6 @@ function buildIndustryDiscoveryAnalystSubAgent(paths) {
9370
9430
  " candidate_count: <N>",
9371
9431
  " ---",
9372
9432
  "",
9373
- " # Industry Candidates: <scope>",
9374
- "",
9375
9433
  " ## Scope",
9376
9434
  " <verbatim scope statement from the issue>",
9377
9435
  "",
@@ -9432,8 +9490,6 @@ function buildIndustryDiscoveryAnalystSubAgent(paths) {
9432
9490
  " threshold: <0.0\u20131.0>",
9433
9491
  " ---",
9434
9492
  "",
9435
- " # Industry Evaluation: <scope>",
9436
- "",
9437
9493
  " ## Fit Rubric",
9438
9494
  " <verbatim rubric table, including weights and threshold>",
9439
9495
  "",
@@ -9490,8 +9546,6 @@ function buildIndustryDiscoveryAnalystSubAgent(paths) {
9490
9546
  " evaluation_source: <EVALUATIONS_DIR>/<DISCOVERY_SLUG>.evaluation.md",
9491
9547
  " ---",
9492
9548
  "",
9493
- " # Industry Plan: <scope>",
9494
- "",
9495
9549
  " ## Cleared Verticals",
9496
9550
  " | Vertical | Score | Downstream Issue |",
9497
9551
  " |----------|-------|------------------|",
@@ -9944,8 +9998,6 @@ function buildMaintenanceAuditSubAgent(paths) {
9944
9998
  " status: complete",
9945
9999
  " ---",
9946
10000
  "",
9947
- " # Maintenance Audit: <AUDIT_SLUG>",
9948
- "",
9949
10001
  " ## Scope",
9950
10002
  " - **Docs root:** `<DOCS_ROOT>`",
9951
10003
  " - **Checks run:** <list of check categories>",
@@ -10067,8 +10119,6 @@ function buildMaintenanceAuditSubAgent(paths) {
10067
10119
  " status: complete",
10068
10120
  " ---",
10069
10121
  "",
10070
- " # Maintenance Fix: <AUDIT_SLUG>",
10071
- "",
10072
10122
  " ## Source Audit Report",
10073
10123
  " <link to the Phase 1 audit report>",
10074
10124
  "",
@@ -10169,8 +10219,6 @@ function buildMaintenanceAuditSubAgent(paths) {
10169
10219
  " status: complete",
10170
10220
  " ---",
10171
10221
  "",
10172
- " # Maintenance Verify: <AUDIT_SLUG>",
10173
- "",
10174
10222
  " ## Source Reports",
10175
10223
  " - Scan report: <link>",
10176
10224
  " - Fix report: <link>",
@@ -11761,6 +11809,14 @@ var DEFAULT_SOURCES_THRESHOLDS = {
11761
11809
  smallMax: 2,
11762
11810
  mediumMax: 5
11763
11811
  };
11812
+ var DEFAULT_BUNDLE_OVERRIDES = {
11813
+ "req:write": {
11814
+ acceptanceCriteria: { smallMax: 3, mediumMax: 20 }
11815
+ },
11816
+ "bcm:scaffold": {
11817
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
11818
+ }
11819
+ };
11764
11820
  var DEFAULT_DECOMPOSITION_TEMPLATE = [
11765
11821
  "## Scope gate: issue is too large to dispatch",
11766
11822
  "",
@@ -11800,22 +11856,58 @@ function resolveScopeGate(config) {
11800
11856
  DEFAULT_SOURCES_THRESHOLDS,
11801
11857
  "sources"
11802
11858
  );
11859
+ const bundleOverrides = resolveBundleOverrides(
11860
+ config?.bundleOverrides,
11861
+ DEFAULT_BUNDLE_OVERRIDES
11862
+ );
11803
11863
  return {
11804
11864
  enabled: config?.enabled ?? true,
11805
11865
  acceptanceCriteria: ac,
11806
11866
  sources,
11807
11867
  autoFile: config?.autoFile ?? false,
11808
- decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE
11868
+ decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE,
11869
+ bundleOverrides
11870
+ };
11871
+ }
11872
+ function resolveOverrideForLabels(gate, labels) {
11873
+ const overrideKeys = Object.keys(gate.bundleOverrides);
11874
+ if (overrideKeys.length === 0 || labels.length === 0) {
11875
+ return {
11876
+ acceptanceCriteria: gate.acceptanceCriteria,
11877
+ sources: gate.sources
11878
+ };
11879
+ }
11880
+ const sortedLabels = [...labels].sort((a, b) => a.localeCompare(b));
11881
+ const matchedLabel = sortedLabels.find(
11882
+ (label) => Object.prototype.hasOwnProperty.call(gate.bundleOverrides, label)
11883
+ );
11884
+ if (matchedLabel === void 0) {
11885
+ return {
11886
+ acceptanceCriteria: gate.acceptanceCriteria,
11887
+ sources: gate.sources
11888
+ };
11889
+ }
11890
+ const override = gate.bundleOverrides[matchedLabel];
11891
+ return {
11892
+ acceptanceCriteria: override.acceptanceCriteria ?? gate.acceptanceCriteria,
11893
+ sources: override.sources ?? gate.sources,
11894
+ matchedLabel
11809
11895
  };
11810
11896
  }
11811
11897
  function validateScopeGateConfig(config) {
11812
11898
  return resolveScopeGate(config);
11813
11899
  }
11814
- function classifyIssueScope(body, gate) {
11900
+ function classifyIssueScope(body, gate, labels = []) {
11815
11901
  const acCount = countAcceptanceCriteria(body);
11816
11902
  const sourcesCount = countSources(body);
11817
- const scope = resolveScopeClass(acCount, sourcesCount, gate);
11818
- return { scope, acCount, sourcesCount };
11903
+ const effective = resolveOverrideForLabels(gate, labels);
11904
+ const scope = resolveScopeClass(acCount, sourcesCount, effective);
11905
+ return {
11906
+ scope,
11907
+ acCount,
11908
+ sourcesCount,
11909
+ matchedLabel: effective.matchedLabel
11910
+ };
11819
11911
  }
11820
11912
  function renderScopeGateSection(gate) {
11821
11913
  const { acceptanceCriteria: ac, sources } = gate;
@@ -11891,6 +11983,38 @@ function renderScopeGateSection(gate) {
11891
11983
  " is in place."
11892
11984
  );
11893
11985
  }
11986
+ const overrideEntries = Object.entries(gate.bundleOverrides);
11987
+ if (overrideEntries.length > 0) {
11988
+ lines.push(
11989
+ "",
11990
+ "### Per-phase-label thresholds",
11991
+ "",
11992
+ "Issues whose `type:*` or phase label matches an entry below are",
11993
+ "classified against the override's thresholds instead of the",
11994
+ "global `acceptance-criteria` / `sources` defaults. This",
11995
+ "accommodates **content-spec workflows** \u2014 issues whose AC list",
11996
+ "is the per-section content checklist for one cohesive document,",
11997
+ "not a phase-completion checklist that can be decomposed.",
11998
+ "",
11999
+ "| Phase label | AC `mediumMax` | Sources `mediumMax` |",
12000
+ "|-------------|----------------|---------------------|"
12001
+ );
12002
+ const sortedKeys = overrideEntries.map(([k]) => k).sort();
12003
+ for (const label of sortedKeys) {
12004
+ const override = gate.bundleOverrides[label];
12005
+ const acMax = override.acceptanceCriteria?.mediumMax;
12006
+ const sourcesMax = override.sources?.mediumMax;
12007
+ const acCell = acMax === void 0 ? "_(global)_" : `${acMax}`;
12008
+ const sourcesCell = sourcesMax === void 0 ? "_(global)_" : `${sourcesMax}`;
12009
+ lines.push(`| \`${label}\` | ${acCell} | ${sourcesCell} |`);
12010
+ }
12011
+ lines.push(
12012
+ "",
12013
+ "When an issue carries multiple labels that each match an entry",
12014
+ "in this table, the orchestrator picks the **first match in",
12015
+ "alphabetical order on the label name** \u2014 first-wins, deterministic."
12016
+ );
12017
+ }
11894
12018
  lines.push(
11895
12019
  "",
11896
12020
  "### Decomposition-proposal template",
@@ -11907,12 +12031,21 @@ function renderScopeGateSection(gate) {
11907
12031
  }
11908
12032
  function renderScopeGateShellHelpers(gate) {
11909
12033
  const { acceptanceCriteria: ac, sources } = gate;
11910
- return [
12034
+ const lines = [
11911
12035
  "# Classify an issue body against the scope-gate thresholds.",
11912
12036
  "# Reads the issue body from stdin. Echoes one of 'small',",
11913
12037
  "# 'medium', or 'large' on stdout. Writes the observed counts to",
11914
12038
  "# stderr as `ac=<n> sources=<n>` so the caller can embed them in",
11915
12039
  "# the decomposition-proposal comment.",
12040
+ "#",
12041
+ "# Effective per-issue thresholds are read from the four",
12042
+ "# `_EFFECTIVE_*` shell variables. The caller is expected to set",
12043
+ "# them via `bundle_override_for()` before invoking scope_of();",
12044
+ "# defaults preserve the global thresholds.",
12045
+ `_EFFECTIVE_AC_SMALL_MAX="\${_EFFECTIVE_AC_SMALL_MAX:-${ac.smallMax}}"`,
12046
+ `_EFFECTIVE_AC_MEDIUM_MAX="\${_EFFECTIVE_AC_MEDIUM_MAX:-${ac.mediumMax}}"`,
12047
+ `_EFFECTIVE_SOURCES_SMALL_MAX="\${_EFFECTIVE_SOURCES_SMALL_MAX:-${sources.smallMax}}"`,
12048
+ `_EFFECTIVE_SOURCES_MEDIUM_MAX="\${_EFFECTIVE_SOURCES_MEDIUM_MAX:-${sources.mediumMax}}"`,
11916
12049
  "scope_of() {",
11917
12050
  " local body",
11918
12051
  " body=$(cat)",
@@ -11941,15 +12074,79 @@ function renderScopeGateShellHelpers(gate) {
11941
12074
  " END { print count }",
11942
12075
  " ')",
11943
12076
  ` printf 'ac=%s sources=%s\\n' "$ac_count" "$sources_count" >&2`,
11944
- ` if [ "$ac_count" -le ${ac.smallMax} ] && [ "$sources_count" -le ${sources.smallMax} ]; then`,
12077
+ ' if [ "$ac_count" -le "$_EFFECTIVE_AC_SMALL_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_SMALL_MAX" ]; then',
11945
12078
  " echo small",
11946
- ` elif [ "$ac_count" -le ${ac.mediumMax} ] && [ "$sources_count" -le ${sources.mediumMax} ]; then`,
12079
+ ' elif [ "$ac_count" -le "$_EFFECTIVE_AC_MEDIUM_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_MEDIUM_MAX" ]; then',
11947
12080
  " echo medium",
11948
12081
  " else",
11949
12082
  " echo large",
11950
12083
  " fi",
12084
+ "}",
12085
+ "",
12086
+ "# Resolve per-phase-label threshold overrides. Reads a newline-",
12087
+ "# separated list of label names on stdin and echoes effective",
12088
+ "# threshold variable assignments (one per line) on stdout. The",
12089
+ "# caller `eval`s the output to set the four `_EFFECTIVE_*`",
12090
+ "# variables before calling `scope_of()`. Also echoes",
12091
+ "# `MATCHED_LABEL=<label>` (or `MATCHED_LABEL=`) so the caller can",
12092
+ "# report which override fired.",
12093
+ "#",
12094
+ "# Tie-breaking: when more than one label matches an override key,",
12095
+ "# the alphabetical first-wins rule applies \u2014 the same rule the",
12096
+ "# TypeScript classifier uses.",
12097
+ "bundle_override_for() {",
12098
+ ` local default_ac_small=${ac.smallMax}`,
12099
+ ` local default_ac_medium=${ac.mediumMax}`,
12100
+ ` local default_sources_small=${sources.smallMax}`,
12101
+ ` local default_sources_medium=${sources.mediumMax}`,
12102
+ " # Sort labels alphabetically so the first match is deterministic.",
12103
+ " local sorted_labels",
12104
+ " sorted_labels=$(LC_ALL=C sort)",
12105
+ " local matched_label=''",
12106
+ " local ac_small=$default_ac_small",
12107
+ " local ac_medium=$default_ac_medium",
12108
+ " local sources_small=$default_sources_small",
12109
+ " local sources_medium=$default_sources_medium",
12110
+ " while IFS= read -r label; do",
12111
+ ' [ -z "$label" ] && continue',
12112
+ ` case "$label" in`
12113
+ ];
12114
+ const sortedKeys = Object.keys(gate.bundleOverrides).sort();
12115
+ if (sortedKeys.length === 0) {
12116
+ lines.push(" *) ;;");
12117
+ } else {
12118
+ for (const label of sortedKeys) {
12119
+ const override = gate.bundleOverrides[label];
12120
+ const acThresholds = override.acceptanceCriteria;
12121
+ const sourcesThresholds = override.sources;
12122
+ const branchLines = [` '${label}')`];
12123
+ if (acThresholds !== void 0) {
12124
+ branchLines.push(` ac_small=${acThresholds.smallMax}`);
12125
+ branchLines.push(` ac_medium=${acThresholds.mediumMax}`);
12126
+ }
12127
+ if (sourcesThresholds !== void 0) {
12128
+ branchLines.push(` sources_small=${sourcesThresholds.smallMax}`);
12129
+ branchLines.push(
12130
+ ` sources_medium=${sourcesThresholds.mediumMax}`
12131
+ );
12132
+ }
12133
+ branchLines.push(` matched_label='${label}'`);
12134
+ branchLines.push(" break");
12135
+ branchLines.push(" ;;");
12136
+ lines.push(...branchLines);
12137
+ }
12138
+ }
12139
+ lines.push(
12140
+ " esac",
12141
+ ` done <<<"$sorted_labels"`,
12142
+ ` printf 'MATCHED_LABEL=%s\\n' "$matched_label"`,
12143
+ ` printf '_EFFECTIVE_AC_SMALL_MAX=%s\\n' "$ac_small"`,
12144
+ ` printf '_EFFECTIVE_AC_MEDIUM_MAX=%s\\n' "$ac_medium"`,
12145
+ ` printf '_EFFECTIVE_SOURCES_SMALL_MAX=%s\\n' "$sources_small"`,
12146
+ ` printf '_EFFECTIVE_SOURCES_MEDIUM_MAX=%s\\n' "$sources_medium"`,
11951
12147
  "}"
11952
- ].join("\n");
12148
+ );
12149
+ return lines.join("\n");
11953
12150
  }
11954
12151
  function resolveThresholds(supplied, defaults, field) {
11955
12152
  const merged = {
@@ -11959,6 +12156,42 @@ function resolveThresholds(supplied, defaults, field) {
11959
12156
  assertValidThresholds(merged, field);
11960
12157
  return merged;
11961
12158
  }
12159
+ function resolveBundleOverrides(supplied, defaults) {
12160
+ const out = {};
12161
+ for (const [label, override] of Object.entries(defaults)) {
12162
+ out[label] = override;
12163
+ }
12164
+ if (supplied !== void 0) {
12165
+ for (const label of Object.keys(supplied)) {
12166
+ const value = supplied[label];
12167
+ if (value === void 0) {
12168
+ delete out[label];
12169
+ continue;
12170
+ }
12171
+ const resolved = {};
12172
+ if (value.acceptanceCriteria !== void 0) {
12173
+ resolved.acceptanceCriteria = resolveThresholds(
12174
+ value.acceptanceCriteria,
12175
+ DEFAULT_AC_THRESHOLDS,
12176
+ "acceptanceCriteria"
12177
+ );
12178
+ }
12179
+ if (value.sources !== void 0) {
12180
+ resolved.sources = resolveThresholds(
12181
+ value.sources,
12182
+ DEFAULT_SOURCES_THRESHOLDS,
12183
+ "sources"
12184
+ );
12185
+ }
12186
+ if (resolved.acceptanceCriteria === void 0 && resolved.sources === void 0) {
12187
+ delete out[label];
12188
+ continue;
12189
+ }
12190
+ out[label] = resolved;
12191
+ }
12192
+ }
12193
+ return out;
12194
+ }
11962
12195
  function assertValidThresholds(thresholds, field) {
11963
12196
  const { smallMax, mediumMax } = thresholds;
11964
12197
  for (const [key, value] of [
@@ -11982,8 +12215,8 @@ function assertValidThresholds(thresholds, field) {
11982
12215
  );
11983
12216
  }
11984
12217
  }
11985
- function resolveScopeClass(acCount, sourcesCount, gate) {
11986
- const { acceptanceCriteria: ac, sources } = gate;
12218
+ function resolveScopeClass(acCount, sourcesCount, thresholds) {
12219
+ const { acceptanceCriteria: ac, sources } = thresholds;
11987
12220
  if (acCount <= ac.smallMax && sourcesCount <= sources.smallMax) {
11988
12221
  return "small";
11989
12222
  }
@@ -12563,7 +12796,9 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
12563
12796
  "STALE_BLOCKED_HOURS=168",
12564
12797
  `SCOPE_GATE_ENABLED=${scopeGate.enabled ? "1" : "0"}`,
12565
12798
  `SCOPE_GATE_AUTO_FILE=${scopeGate.autoFile ? "1" : "0"}`,
12799
+ `SCOPE_AC_SMALL_MAX=${scopeGate.acceptanceCriteria.smallMax}`,
12566
12800
  `SCOPE_AC_MEDIUM_MAX=${scopeGate.acceptanceCriteria.mediumMax}`,
12801
+ `SCOPE_SOURCES_SMALL_MAX=${scopeGate.sources.smallMax}`,
12567
12802
  `SCOPE_SOURCES_MEDIUM_MAX=${scopeGate.sources.mediumMax}`,
12568
12803
  `RUN_RATIO_ENABLED=${runRatio.enabled ? "1" : "0"}`,
12569
12804
  `RUN_RATIO_DISPATCH_PER_HOUSEKEEPING=${runRatio.ratio}`,
@@ -12917,12 +13152,45 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
12917
13152
  ' echo "SCOPE #${issue_num} disabled"',
12918
13153
  " return 0",
12919
13154
  " fi",
13155
+ " # Fetch the body and labels in a single API call so we resolve",
13156
+ " # any per-phase-label override against the issue's actual labels.",
13157
+ " local issue_json",
13158
+ ' issue_json=$(gh issue view "$issue_num" --json body,labels 2>/dev/null || echo "")',
13159
+ ' if [[ -z "$issue_json" ]]; then',
13160
+ ' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
13161
+ " return 1",
13162
+ " fi",
12920
13163
  " local body",
12921
- ` body=$(gh issue view "$issue_num" --json body --jq '.body' 2>/dev/null || echo "")`,
13164
+ ` body=$(printf '%s' "$issue_json" | jq -r '.body // ""')`,
12922
13165
  ' if [[ -z "$body" ]]; then',
12923
13166
  ' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
12924
13167
  " return 1",
12925
13168
  " fi",
13169
+ " local labels",
13170
+ ` labels=$(printf '%s' "$issue_json" | jq -r '.labels[]?.name // empty')`,
13171
+ " # Resolve the effective thresholds. `bundle_override_for` reads",
13172
+ " # labels on stdin and emits `KEY=VALUE` assignments (one per line)",
13173
+ " # for the four `_EFFECTIVE_*` variables plus `MATCHED_LABEL`.",
13174
+ " local override_output",
13175
+ ` override_output=$(printf '%s\\n' "$labels" | bundle_override_for)`,
13176
+ " local matched_label=''",
13177
+ " local _EFFECTIVE_AC_SMALL_MAX=$SCOPE_AC_SMALL_MAX",
13178
+ " local _EFFECTIVE_AC_MEDIUM_MAX=$SCOPE_AC_MEDIUM_MAX",
13179
+ " local _EFFECTIVE_SOURCES_SMALL_MAX=$SCOPE_SOURCES_SMALL_MAX",
13180
+ " local _EFFECTIVE_SOURCES_MEDIUM_MAX=$SCOPE_SOURCES_MEDIUM_MAX",
13181
+ " while IFS= read -r assignment; do",
13182
+ ' [ -z "$assignment" ] && continue',
13183
+ ' case "$assignment" in',
13184
+ " MATCHED_LABEL=*) matched_label=${assignment#MATCHED_LABEL=} ;;",
13185
+ " _EFFECTIVE_AC_SMALL_MAX=*) _EFFECTIVE_AC_SMALL_MAX=${assignment#_EFFECTIVE_AC_SMALL_MAX=} ;;",
13186
+ " _EFFECTIVE_AC_MEDIUM_MAX=*) _EFFECTIVE_AC_MEDIUM_MAX=${assignment#_EFFECTIVE_AC_MEDIUM_MAX=} ;;",
13187
+ " _EFFECTIVE_SOURCES_SMALL_MAX=*) _EFFECTIVE_SOURCES_SMALL_MAX=${assignment#_EFFECTIVE_SOURCES_SMALL_MAX=} ;;",
13188
+ " _EFFECTIVE_SOURCES_MEDIUM_MAX=*) _EFFECTIVE_SOURCES_MEDIUM_MAX=${assignment#_EFFECTIVE_SOURCES_MEDIUM_MAX=} ;;",
13189
+ " esac",
13190
+ ' done <<<"$override_output"',
13191
+ " # Export the effective thresholds so scope_of() picks them up.",
13192
+ " export _EFFECTIVE_AC_SMALL_MAX _EFFECTIVE_AC_MEDIUM_MAX",
13193
+ " export _EFFECTIVE_SOURCES_SMALL_MAX _EFFECTIVE_SOURCES_MEDIUM_MAX",
12926
13194
  " local scope counts",
12927
13195
  " # scope_of writes counts to stderr (ac=N sources=N); capture both.",
12928
13196
  ` scope=$(printf '%s' "$body" | scope_of 2> >(read -r counts; echo "$counts" >&2))`,
@@ -12936,9 +13204,17 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
12936
13204
  ` sources_count=$(echo "$stderr_capture" | grep -oE 'sources=[0-9]+' | head -1 | cut -d= -f2)`,
12937
13205
  " ac_count=${ac_count:-0}",
12938
13206
  " sources_count=${sources_count:-0}",
12939
- " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s\\n' \\",
12940
- ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
12941
- ' "$SCOPE_AC_MEDIUM_MAX" "$SCOPE_SOURCES_MEDIUM_MAX" "$SCOPE_GATE_AUTO_FILE"',
13207
+ ' if [ -n "$matched_label" ]; then',
13208
+ " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s matched_label=%s\\n' \\",
13209
+ ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13210
+ ' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
13211
+ ' "$SCOPE_GATE_AUTO_FILE" "$matched_label"',
13212
+ " else",
13213
+ " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s\\n' \\",
13214
+ ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13215
+ ' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
13216
+ ' "$SCOPE_GATE_AUTO_FILE"',
13217
+ " fi",
12942
13218
  "}",
12943
13219
  "",
12944
13220
  "cmd_tick() {",
@@ -13143,9 +13419,16 @@ var orchestratorSubAgent = {
13143
13419
  "The output is one line of the form:",
13144
13420
  "",
13145
13421
  "```",
13146
- "SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1>",
13422
+ "SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1> [matched_label=<label>]",
13147
13423
  "```",
13148
13424
  "",
13425
+ "When `matched_label=<label>` is present, the scope gate applied a",
13426
+ "per-phase-label override (e.g. `req:write` raises the AC ceiling to",
13427
+ "20 for content-spec workflows whose AC list is the per-section",
13428
+ "checklist for one document). The `ac_limit` / `sources_limit`",
13429
+ "values reflect the override; the global thresholds are documented",
13430
+ "in the **Scope gate** section in `CLAUDE.md`.",
13431
+ "",
13149
13432
  "If the scope is `small` or `medium`, continue \u2014 the issue is",
13150
13433
  "dispatchable. Record the first `PICK` line as the next work item",
13151
13434
  "and proceed to Phase G:",
@@ -14102,8 +14385,6 @@ function buildPeopleProfileAnalystSubAgent(paths) {
14102
14385
  " parent_issue: <N>",
14103
14386
  " ---",
14104
14387
  "",
14105
- " # Research Notes: <person name>",
14106
- "",
14107
14388
  " ## Framing",
14108
14389
  " <why this person was requested and what to learn>",
14109
14390
  "",
@@ -14165,8 +14446,6 @@ function buildPeopleProfileAnalystSubAgent(paths) {
14165
14446
  " notes: <NOTES_DIR>/<PERSON_SLUG>.notes.md",
14166
14447
  " ---",
14167
14448
  "",
14168
- " # <person name>",
14169
- "",
14170
14449
  " ## Summary",
14171
14450
  " <2\u20134 sentence elevator description: who they are, what they do,",
14172
14451
  " why they matter to this project>",
@@ -16750,8 +17029,6 @@ jurisdiction: <federal | state | provincial | international | local>
16750
17029
  industry: <industry-slug or 'platform'>
16751
17030
  ---
16752
17031
 
16753
- # <Regulation Name>
16754
-
16755
17032
  | Field | Value |
16756
17033
  |-------|-------|
16757
17034
  | **Official name** | <full legal name> |
@@ -17122,8 +17399,6 @@ function buildRegulatoryResearchAnalystSubAgent(paths) {
17122
17399
  " scope: <SCOPE_SLUG>",
17123
17400
  " ---",
17124
17401
  "",
17125
- " # Regulatory Scan: <Scope>",
17126
- "",
17127
17402
  " ## Scope Context",
17128
17403
  " - **Scope type:** <industry / jurisdiction / platform>",
17129
17404
  " - **Scope identifier:** <SCOPE_SLUG>",
@@ -17174,7 +17449,12 @@ function buildRegulatoryResearchAnalystSubAgent(paths) {
17174
17449
  "",
17175
17450
  "7. **Create or update the scope index page** at",
17176
17451
  " `<SCOPE_INDEX_PAGE>` so downstream regulations link back to a",
17177
- " coherent landing page for the scope.",
17452
+ " coherent landing page for the scope. Follow the",
17453
+ " `stub-index-convention` rule: the body must carry a 1\u20132",
17454
+ " paragraph contextual summary plus a grouped, linked listing of",
17455
+ " every regulation in the scope (e.g. by jurisdiction). No body",
17456
+ " `# Heading` \u2014 the frontmatter `title:` already renders as the",
17457
+ " page H1.",
17178
17458
  "",
17179
17459
  "8. **Commit and push** the scan report and the scope index page.",
17180
17460
  " Close the scan issue.",
@@ -18032,8 +18312,6 @@ function buildRequirementsAnalystSubAgent(paths) {
18032
18312
  " status: complete",
18033
18313
  " ---",
18034
18314
  "",
18035
- " # Requirements Scan: <scope>",
18036
- "",
18037
18315
  " ## Source Documents Reviewed",
18038
18316
  " - <path> \u2014 <brief description>",
18039
18317
  "",
@@ -18169,42 +18447,32 @@ function buildRequirementsAnalystSubAgent(paths) {
18169
18447
  " directory under `<REQUIREMENTS_ROOT>/<category>/` to find the next",
18170
18448
  " available `NNN` for each proposed requirement.",
18171
18449
  "",
18172
- "5. **Create requirement issues.** For each proposal:",
18450
+ "5. **Create requirement issues.** For each proposal, file a",
18451
+ " `req:write` issue using the canonical recipe documented in",
18452
+ " `## Template: req:write` of",
18453
+ " `docs/src/content/docs/agents/issue-templates.md`.",
18173
18454
  "",
18174
18455
  " All `type:requirement` issues default to `priority:medium` (override",
18175
18456
  " only if the proposal's priority was explicitly High or Low). Each",
18176
18457
  " issue must also carry the `req:write` phase label plus the matching",
18177
18458
  " `tier:*` label so the downstream `requirements-writer` bundle picks",
18178
- " it up with the correct tier.",
18179
- "",
18180
- " ```bash",
18181
- " gh issue create \\",
18182
- ' --title "docs(<category>): <PREFIX>-<NNN> \u2014 <title>" \\',
18183
- ' --label "type:requirement" --label "req:write" --label "tier:<tier-slug>" --label "status:ready" --label "priority:medium" \\',
18184
- ' --body "## Objective',
18185
- " Write <PREFIX>-<NNN> \u2014 <title>.",
18186
- "",
18187
- " **Category:** <BR/FR/NFR/TR/ADR/SEC/DR/INT/OPS/UX/MT>",
18188
- " **Tier:** <platform/industry/customer-workflow/consumer-app>",
18189
- " **Output Path:** <REQUIREMENTS_ROOT>/<category-dir>/<PREFIX>-<NNN>-<slug>.md",
18190
- "",
18191
- " ## Context",
18192
- " - **Gap identified by:** requirements scan of <source>",
18193
- " - **Proposals file:** <RESEARCH_REQUIREMENTS_ROOT>/req-proposals-<scope>-<date>.md",
18459
+ " it up with the correct tier. Concretely, the label list includes",
18460
+ ' `--label "type:requirement"`, `--label "req:write"`,',
18461
+ ' `--label "tier:<tier-slug>"`, `--label "status:ready"`, and',
18462
+ ' `--label "priority:medium"`.',
18194
18463
  "",
18195
- " ## Inputs / Read",
18196
- " - **Depends on:** (none)",
18197
- " - **Read:** <RESEARCH_REQUIREMENTS_ROOT>/req-proposals-<scope>-<date>.md, <source docs>",
18464
+ " The body must carry the three writer-required fields in an",
18465
+ " `## Objective` block, written as bold-prefixed lines so the",
18466
+ " writer's intake parser can pull them out:",
18198
18467
  "",
18199
- " ## Acceptance Criteria",
18200
- " - [ ] Requirement document follows <category> template",
18201
- " - [ ] Traceability links to source BCM/competitive/product doc",
18202
- " - [ ] Registry index updated",
18203
- " - [ ] Decision authority rules followed (direct write vs. proposed)",
18468
+ " - `**Category:** <BR/FR/NFR/TR/ADR/SEC/DR/INT/OPS/UX/MT>`",
18469
+ " - `**Tier:** <platform/industry/customer-workflow/consumer-app>`",
18470
+ " - `**Output Path:** <REQUIREMENTS_ROOT>/<category-dir>/<PREFIX>-<NNN>-<slug>.md`",
18204
18471
  "",
18205
- " ## Scope Size",
18206
- ' small"',
18207
- " ```",
18472
+ " Then under `## Inputs / Read`, list the proposals file",
18473
+ " (`<RESEARCH_REQUIREMENTS_ROOT>/req-proposals-<scope>-<date>.md`)",
18474
+ " and any source documents the writer should consult (BCM model",
18475
+ " docs, competitive analyses, product docs).",
18208
18476
  "",
18209
18477
  " When one of Category / Tier / Output Path could not be derived",
18210
18478
  " from the proposal (for example, the proposal omitted the Tier",
@@ -18380,8 +18648,6 @@ var TEMPLATE_BR = `---
18380
18648
  title: "BR-NNN: [Business Requirement Title]"
18381
18649
  ---
18382
18650
 
18383
- # BR-NNN: [Business Requirement Title]
18384
-
18385
18651
  ## Metadata
18386
18652
 
18387
18653
  | Field | Value |
@@ -18489,8 +18755,6 @@ var TEMPLATE_FR = `---
18489
18755
  title: "FR-NNN: [Functional Requirement Title]"
18490
18756
  ---
18491
18757
 
18492
- # FR-NNN: [Functional Requirement Title]
18493
-
18494
18758
  ## Metadata
18495
18759
 
18496
18760
  | Field | Value |
@@ -18630,8 +18894,6 @@ var TEMPLATE_NFR = `---
18630
18894
  title: "NFR-NNN: [Non-Functional Requirement Title]"
18631
18895
  ---
18632
18896
 
18633
- # NFR-NNN: [Non-Functional Requirement Title]
18634
-
18635
18897
  ## Metadata
18636
18898
 
18637
18899
  | Field | Value |
@@ -18732,8 +18994,6 @@ var TEMPLATE_TR = `---
18732
18994
  title: "TR-NNN: [Technical Requirement Title]"
18733
18995
  ---
18734
18996
 
18735
- # TR-NNN: [Technical Requirement Title]
18736
-
18737
18997
  ## Metadata
18738
18998
 
18739
18999
  | Field | Value |
@@ -18885,8 +19145,6 @@ var TEMPLATE_ADR = `---
18885
19145
  title: "ADR-NNN: [Decision Title]"
18886
19146
  ---
18887
19147
 
18888
- # ADR-NNN: [Decision Title]
18889
-
18890
19148
  ## Metadata
18891
19149
 
18892
19150
  | Field | Value |
@@ -19015,8 +19273,6 @@ var TEMPLATE_SEC = `---
19015
19273
  title: "SEC-NNN: [Security Requirement Title]"
19016
19274
  ---
19017
19275
 
19018
- # SEC-NNN: [Security Requirement Title]
19019
-
19020
19276
  ## Metadata
19021
19277
 
19022
19278
  | Field | Value |
@@ -19128,8 +19384,6 @@ var TEMPLATE_DR = `---
19128
19384
  title: "DR-NNN: [Data Requirement Title]"
19129
19385
  ---
19130
19386
 
19131
- # DR-NNN: [Data Requirement Title]
19132
-
19133
19387
  ## Metadata
19134
19388
 
19135
19389
  | Field | Value |
@@ -19244,8 +19498,6 @@ var TEMPLATE_INT = `---
19244
19498
  title: "INT-NNN: [Integration Requirement Title]"
19245
19499
  ---
19246
19500
 
19247
- # INT-NNN: [Integration Requirement Title]
19248
-
19249
19501
  ## Metadata
19250
19502
 
19251
19503
  | Field | Value |
@@ -19377,8 +19629,6 @@ var TEMPLATE_OPS = `---
19377
19629
  title: "OPS-NNN: [Operational Requirement Title]"
19378
19630
  ---
19379
19631
 
19380
- # OPS-NNN: [Operational Requirement Title]
19381
-
19382
19632
  ## Metadata
19383
19633
 
19384
19634
  | Field | Value |
@@ -19493,8 +19743,6 @@ var TEMPLATE_UX = `---
19493
19743
  title: "UX-NNN: [UX Requirement Title]"
19494
19744
  ---
19495
19745
 
19496
- # UX-NNN: [UX Requirement Title]
19497
-
19498
19746
  ## Metadata
19499
19747
 
19500
19748
  | Field | Value |
@@ -19611,8 +19859,6 @@ var TEMPLATE_MT = `---
19611
19859
  title: "MT-NNN: [Multi-Tenancy Requirement Title]"
19612
19860
  ---
19613
19861
 
19614
- # MT-NNN: [Multi-Tenancy Requirement Title]
19615
-
19616
19862
  ## Metadata
19617
19863
 
19618
19864
  | Field | Value |
@@ -19765,8 +20011,6 @@ var TEMPLATE_REQUIREMENTS_README = `---
19765
20011
  title: "[Project Name] \u2014 Requirements Documentation"
19766
20012
  ---
19767
20013
 
19768
- # [Project Name] \u2014 Requirements Documentation
19769
-
19770
20014
  This directory contains the structured requirements taxonomy for [Project Name]. It covers the full lifecycle from business intent through operational concerns, providing traceability between strategic goals and implementation work.
19771
20015
 
19772
20016
  ## Taxonomy Overview
@@ -20523,12 +20767,9 @@ function buildRequirementsWriterSubAgent(paths) {
20523
20767
  "tier: platform",
20524
20768
  "---",
20525
20769
  "",
20526
- "# FR-001: User Registration",
20527
- "",
20528
20770
  "...",
20529
20771
  "```",
20530
20772
  "",
20531
- "The `title` value must match the document's `# Heading` line.",
20532
20773
  "Titles containing colons must be wrapped in double quotes. The",
20533
20774
  "`tier` field must be one of `platform`, `industry`,",
20534
20775
  "`customer-workflow`, or `consumer-app` (use whatever tier slugs the",
@@ -22592,8 +22833,6 @@ function buildResearchAnalystSubAgent(paths) {
22592
22833
  " slice_count: <N>",
22593
22834
  " ---",
22594
22835
  "",
22595
- " # Research Scope: <question>",
22596
- "",
22597
22836
  " ## Question",
22598
22837
  " <verbatim question from the issue>",
22599
22838
  "",
@@ -22663,8 +22902,6 @@ function buildResearchAnalystSubAgent(paths) {
22663
22902
  " parent_issue: <N>",
22664
22903
  " ---",
22665
22904
  "",
22666
- " # Slice NN: <title>",
22667
- "",
22668
22905
  " ## Question",
22669
22906
  " <the slice-level question>",
22670
22907
  "",
@@ -22729,8 +22966,6 @@ function buildResearchAnalystSubAgent(paths) {
22729
22966
  " slices_read: <count>",
22730
22967
  " ---",
22731
22968
  "",
22732
- " # Verification Report: <question>",
22733
- "",
22734
22969
  " ## Acceptance Criteria Coverage",
22735
22970
  " - [x] <criterion> \u2014 covered by slice(s) <NN, NN>",
22736
22971
  " - [ ] <criterion> \u2014 **not covered**; gap noted in deliverable",
@@ -23338,8 +23573,6 @@ function buildSoftwareProfileAnalystSubAgent(paths) {
23338
23573
  " parent_issue: <N>",
23339
23574
  " ---",
23340
23575
  "",
23341
- " # Research Notes: <product name>",
23342
- "",
23343
23576
  " ## Framing",
23344
23577
  " <why this product was requested and what to learn>",
23345
23578
  "",
@@ -23400,8 +23633,6 @@ function buildSoftwareProfileAnalystSubAgent(paths) {
23400
23633
  " notes: <NOTES_DIR>/<PRODUCT_SLUG>.notes.md",
23401
23634
  " ---",
23402
23635
  "",
23403
- " # <product name>",
23404
- "",
23405
23636
  " ## Summary",
23406
23637
  " <2\u20134 sentence elevator description: what it does, who it's for>",
23407
23638
  "",
@@ -23486,8 +23717,6 @@ function buildSoftwareProfileAnalystSubAgent(paths) {
23486
23717
  "updated: YYYY-MM-DD",
23487
23718
  "---",
23488
23719
  "",
23489
- "# Software Feature Matrix",
23490
- "",
23491
23720
  "| product | feature | <segment-1> | <segment-2> | ... | score | source |",
23492
23721
  "|---------|---------|-------------|-------------|-----|-------|--------|",
23493
23722
  "| <slug> | <feature-name> | 0\u20131 | 0\u20131 | ... | <weighted-sum> | <profile-path> |",
@@ -24115,8 +24344,6 @@ title: "<Standard Name>"
24115
24344
  description: "Overview and cross-version comparison of <Standard Name>."
24116
24345
  ---
24117
24346
 
24118
- # <Standard Name>
24119
-
24120
24347
  | Field | Value |
24121
24348
  |-------|-------|
24122
24349
  | **Standard** | <standard name> |
@@ -24434,8 +24661,6 @@ function buildStandardsResearchAnalystSubAgent(paths) {
24434
24661
  " standard: <STANDARD_SLUG>",
24435
24662
  " ---",
24436
24663
  "",
24437
- " # Query Plan: <Standard Name>",
24438
- "",
24439
24664
  " ## Context",
24440
24665
  " <1\u20132 paragraphs on why this standard matters to the consuming",
24441
24666
  " project and what gaps this research campaign fills.>",
@@ -24637,7 +24862,14 @@ function buildStandardsResearchAnalystSubAgent(paths) {
24637
24862
  " the overview page, then populate each section from the version",
24638
24863
  " pages.",
24639
24864
  "",
24640
- "4. **Write the overview page** at `<OVERVIEW_PAGE>`. Fill:",
24865
+ "4. **Write the overview page** at `<OVERVIEW_PAGE>`. Follow",
24866
+ " the `stub-index-convention` rule: the body opens with a",
24867
+ " 1\u20132 paragraph contextual summary of the standard and its",
24868
+ " versions, and the version pages, modules, and extensions",
24869
+ " sections below double as the grouped, linked listing of",
24870
+ " every child page in the standard's folder. No body",
24871
+ " `# Heading` \u2014 the frontmatter `title:` already renders as",
24872
+ " the page H1. Required sections to fill:",
24641
24873
  " - **Version Timeline** \u2014 publication dates, status, milestones",
24642
24874
  " - **Module / Resource Maturity Progression** \u2014 matrix with",
24643
24875
  " modules as rows and versions as columns, using the standard's",
@@ -32438,6 +32670,7 @@ export {
32438
32670
  DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
32439
32671
  DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
32440
32672
  DEFAULT_AUDIT_REPORT_DIR,
32673
+ DEFAULT_BUNDLE_OVERRIDES,
32441
32674
  DEFAULT_DECOMPOSITION_TEMPLATE,
32442
32675
  DEFAULT_DISPATCH_MODEL,
32443
32676
  DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
@@ -32609,6 +32842,7 @@ export {
32609
32842
  renderSkillEvalsRuleContent,
32610
32843
  renderSkillEvalsRunnerScript,
32611
32844
  renderSourceTierExamples,
32845
+ renderStubIndexConventionRuleContent,
32612
32846
  renderUnblockDependentsScript,
32613
32847
  renderUnblockDependentsSection,
32614
32848
  requirementsAnalystBundle,
@@ -32623,6 +32857,7 @@ export {
32623
32857
  resolveModelAlias,
32624
32858
  resolveOrchestratorAssets,
32625
32859
  resolveOutdirFromPackageName,
32860
+ resolveOverrideForLabels,
32626
32861
  resolveProgressFiles,
32627
32862
  resolveRunRatio,
32628
32863
  resolveScheduledTasks,