@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.js CHANGED
@@ -205,6 +205,7 @@ __export(index_exports, {
205
205
  DEFAULT_API_EXTRACTOR_REPORT_FILENAME: () => DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
206
206
  DEFAULT_API_EXTRACTOR_REPORT_FOLDER: () => DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
207
207
  DEFAULT_AUDIT_REPORT_DIR: () => DEFAULT_AUDIT_REPORT_DIR,
208
+ DEFAULT_BUNDLE_OVERRIDES: () => DEFAULT_BUNDLE_OVERRIDES,
208
209
  DEFAULT_DECOMPOSITION_TEMPLATE: () => DEFAULT_DECOMPOSITION_TEMPLATE,
209
210
  DEFAULT_DISPATCH_MODEL: () => DEFAULT_DISPATCH_MODEL,
210
211
  DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO: () => DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
@@ -376,6 +377,7 @@ __export(index_exports, {
376
377
  renderSkillEvalsRuleContent: () => renderSkillEvalsRuleContent,
377
378
  renderSkillEvalsRunnerScript: () => renderSkillEvalsRunnerScript,
378
379
  renderSourceTierExamples: () => renderSourceTierExamples,
380
+ renderStubIndexConventionRuleContent: () => renderStubIndexConventionRuleContent,
379
381
  renderUnblockDependentsScript: () => renderUnblockDependentsScript,
380
382
  renderUnblockDependentsSection: () => renderUnblockDependentsSection,
381
383
  requirementsAnalystBundle: () => requirementsAnalystBundle,
@@ -390,6 +392,7 @@ __export(index_exports, {
390
392
  resolveModelAlias: () => resolveModelAlias,
391
393
  resolveOrchestratorAssets: () => resolveOrchestratorAssets,
392
394
  resolveOutdirFromPackageName: () => resolveOutdirFromPackageName,
395
+ resolveOverrideForLabels: () => resolveOverrideForLabels,
393
396
  resolveProgressFiles: () => resolveProgressFiles,
394
397
  resolveRunRatio: () => resolveRunRatio,
395
398
  resolveScheduledTasks: () => resolveScheduledTasks,
@@ -835,13 +838,16 @@ var agendaAnalystSubAgent = {
835
838
  " `duration_min` \xB1 5 minutes. If it doesn't, adjust section times",
836
839
  " or cut content \u2014 do not ship a math-broken agenda.",
837
840
  "",
838
- "7. **Create the folder `index.md` if missing.** Populate it with:",
841
+ "7. **Create the folder `index.md` if missing.** Populate it",
842
+ " following the `stub-index-convention` rule:",
839
843
  " - Frontmatter `title` (short sidebar label) and `description`",
840
844
  " (one line).",
841
845
  " - A 2\u20134 sentence summary of the meeting's objective and desired",
842
846
  " outcomes pulled from the agenda.",
843
847
  " - A `## Documents` section linking to `./agenda.md` (and",
844
848
  " `./notes.md` once the meeting-analyst produces it).",
849
+ " - No body `# Heading` \u2014 the frontmatter `title:` already",
850
+ " renders as the page H1.",
845
851
  "",
846
852
  " Do NOT set `sidebar.hidden: true` on `index.md` \u2014 it is the page",
847
853
  " that should appear in the sidebar.",
@@ -1605,8 +1611,6 @@ function renderIssueTemplatesStarterPage(_it) {
1605
1611
  "description: Canonical gh issue create recipes \u2014 one per downstream phase label.",
1606
1612
  "---",
1607
1613
  "",
1608
- "# Issue Templates",
1609
- "",
1610
1614
  "This page lists the canonical `gh issue create` recipe for every",
1611
1615
  "downstream issue kind dispatched by an agent in this monorepo.",
1612
1616
  "Bundles and agent prompts cite the matching section below instead",
@@ -3041,6 +3045,71 @@ function assertValidProductContextPath(value) {
3041
3045
  }
3042
3046
  }
3043
3047
 
3048
+ // src/agent/bundles/stub-index-convention.ts
3049
+ function renderStubIndexConventionRuleContent() {
3050
+ return [
3051
+ "# Section Index Pages",
3052
+ "",
3053
+ "When any agent creates or updates an `index.md` (or `README.md`)",
3054
+ "in a docs subdirectory under a Starlight content root, the file's",
3055
+ "body must include:",
3056
+ "",
3057
+ "1. **A 1\u20132 paragraph summary** of the section's purpose and how",
3058
+ " it fits into the larger research, requirements, or capability",
3059
+ " area. Readers landing on the page should understand what's in",
3060
+ " the section without falling back to the sidebar.",
3061
+ "",
3062
+ "2. **A grouped, linked listing of the directory's children**",
3063
+ " (table or bullet list). When a natural taxonomy exists (e.g.",
3064
+ " organizations grouped by sub-segment, regulations grouped by",
3065
+ " jurisdiction, capabilities grouped by tier), use it. Otherwise",
3066
+ " sort alphabetically by title. Every listing entry must link",
3067
+ " to the child page.",
3068
+ "",
3069
+ "3. **No body `# Heading`.** Starlight renders the frontmatter",
3070
+ " `title:` as the page H1 automatically \u2014 a body H1 produces a",
3071
+ " duplicate. The same no-body-H1 contract that applies to skill",
3072
+ " templates and inline agent templates also applies to every",
3073
+ " index page emitted by an agent.",
3074
+ "",
3075
+ "## When this applies",
3076
+ "",
3077
+ "The convention applies to every `index.md` / `README.md` file",
3078
+ "covered by the shared-index path globs documented in the",
3079
+ "`shared-editing-safety` rule:",
3080
+ "",
3081
+ "- `docs/src/content/docs/**/index.md`",
3082
+ "- `docs/src/content/docs/**/README.md`",
3083
+ "",
3084
+ "Any agent that scaffolds a new directory under a Starlight",
3085
+ "content root MUST populate the directory's index page following",
3086
+ "this contract \u2014 a one-sentence stub is not acceptable.",
3087
+ "",
3088
+ "## Reference shapes",
3089
+ "",
3090
+ "Existing rich indexes are the canonical reference shape. Examples:",
3091
+ "",
3092
+ "- A `<MEETINGS_ROOT>/<YYYY-MM-DD>-<slug>/index.md` populated by",
3093
+ " the `agenda-analyst` bundle: frontmatter `title` /",
3094
+ " `description`, a 2\u20134 sentence summary, and a `## Documents`",
3095
+ " section listing every page in the folder.",
3096
+ "- A regulations scope index that opens with two paragraphs of",
3097
+ " context and groups every linked regulation under a",
3098
+ " `## Regulations` table by jurisdiction.",
3099
+ "- A standards-organizations index that groups every linked",
3100
+ " organization under a `## Organizations` heading by role",
3101
+ " (governance, working group, implementer).",
3102
+ "",
3103
+ "## Out of scope",
3104
+ "",
3105
+ "- Auto-generated child listings via Astro/Starlight components.",
3106
+ " Agents emit hand-curated tables or bullet lists; the rule",
3107
+ " defines the shape, not the rendering technology.",
3108
+ "- Backfilling pre-existing stub indexes is a downstream-consumer",
3109
+ " cleanup task, not a configulator change."
3110
+ ].join("\n");
3111
+ }
3112
+
3044
3113
  // src/agent/bundles/base.ts
3045
3114
  var createPackageSkill = {
3046
3115
  name: "create-package",
@@ -4126,6 +4195,16 @@ function buildBaseBundle(paths = DEFAULT_AGENT_PATHS) {
4126
4195
  },
4127
4196
  tags: ["workflow"]
4128
4197
  },
4198
+ {
4199
+ name: "stub-index-convention",
4200
+ 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).",
4201
+ scope: AGENT_RULE_SCOPE.ALWAYS,
4202
+ content: renderStubIndexConventionRuleContent(),
4203
+ platforms: {
4204
+ cursor: { exclude: true }
4205
+ },
4206
+ tags: ["workflow"]
4207
+ },
4129
4208
  {
4130
4209
  name: "skill-evals",
4131
4210
  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.",
@@ -4401,8 +4480,6 @@ function buildBcmWriterSubAgent(paths) {
4401
4480
  " tier: L1 | L2 | L3",
4402
4481
  " ---",
4403
4482
  "",
4404
- " # BCM Outline: <Capability Name>",
4405
- "",
4406
4483
  " ## Capability Name",
4407
4484
  " <noun or noun phrase \u2014 never a verb>",
4408
4485
  "",
@@ -4482,8 +4559,6 @@ function buildBcmWriterSubAgent(paths) {
4482
4559
  " date: YYYY-MM-DD",
4483
4560
  " ---",
4484
4561
  "",
4485
- " # <Capability Name>",
4486
- "",
4487
4562
  " ## Capability Definitions",
4488
4563
  " <1\u20132 sentences from the outline's Proposed Definition, refined>",
4489
4564
  "",
@@ -4893,8 +4968,6 @@ title: "Business Model: <Segment Name>"
4893
4968
  description: "Business Model Canvas analysis for the <segment name> segment of the <industry> industry."
4894
4969
  ---
4895
4970
 
4896
- # Business Model: <Segment Name>
4897
-
4898
4971
  | Field | Value |
4899
4972
  |-------|-------|
4900
4973
  | **Industry** | <industry name> |
@@ -5278,8 +5351,6 @@ function buildBusinessModelsAnalystSubAgent(paths) {
5278
5351
  " status: complete",
5279
5352
  " ---",
5280
5353
  "",
5281
- " # Business Model Scan: <Industry Name>",
5282
- "",
5283
5354
  " ## Source Inputs",
5284
5355
  " - Industry plan: <relative path>",
5285
5356
  " - Prior research: <relative path> (optional)",
@@ -5367,8 +5438,12 @@ function buildBusinessModelsAnalystSubAgent(paths) {
5367
5438
  "",
5368
5439
  "7. **Create the segment index** at",
5369
5440
  " `<BUSINESS_MODELS_ROOT>/<industry>/segments/<SEGMENT_SLUG>/index.md`",
5370
- " if it does not already exist. The index should carry a short",
5371
- " description and link to `./business-model.md`.",
5441
+ " if it does not already exist. Follow the",
5442
+ " `stub-index-convention` rule: a 1\u20132 paragraph summary of the",
5443
+ " segment plus a linked listing of every page in the folder",
5444
+ " (`./business-model.md` and any value-stream / capability pages",
5445
+ " that land later). No body `# Heading` \u2014 the frontmatter",
5446
+ " `title:` already renders as the page H1.",
5372
5447
  "",
5373
5448
  "8. **Create one `business-models:complete` issue** with",
5374
5449
  " `Depends on: #<canvas-issue>`. Its body references the path to",
@@ -6159,8 +6234,6 @@ function buildCompanyProfileAnalystSubAgent(paths) {
6159
6234
  " parent_issue: <N>",
6160
6235
  " ---",
6161
6236
  "",
6162
- " # Research Notes: <company name>",
6163
- "",
6164
6237
  " ## Framing",
6165
6238
  " <why this company was requested and what to learn>",
6166
6239
  "",
@@ -6222,8 +6295,6 @@ function buildCompanyProfileAnalystSubAgent(paths) {
6222
6295
  " referencedIn: []",
6223
6296
  " ---",
6224
6297
  "",
6225
- " # <company name>",
6226
- "",
6227
6298
  " ## Summary",
6228
6299
  " <2\u20134 sentence elevator description: what they do, who they sell to>",
6229
6300
  "",
@@ -6634,8 +6705,6 @@ function buildCompanyProfileAnalystSubAgent(paths) {
6634
6705
  " - <relative path to profile 2>",
6635
6706
  " ---",
6636
6707
  "",
6637
- " # <segment name> Competitive Analysis",
6638
- "",
6639
6708
  " ## Market Overview",
6640
6709
  " <landscape summary \u2014 total players, market structure, trends",
6641
6710
  " visible from the profiles>",
@@ -7198,8 +7267,6 @@ archetype: <ARCHETYPE_SLUG>
7198
7267
  segment: <SEGMENT_SLUG>
7199
7268
  ---
7200
7269
 
7201
- # <Customer Archetype Name>
7202
-
7203
7270
  | Field | Value |
7204
7271
  |-------|-------|
7205
7272
  | **Archetype** | <short noun phrase identifying the archetype> |
@@ -7565,8 +7632,6 @@ function buildCustomerProfileAnalystSubAgent(paths) {
7565
7632
  " scope: <SCOPE_SLUG>",
7566
7633
  " ---",
7567
7634
  "",
7568
- " # Customer Discovery: <Scope>",
7569
- "",
7570
7635
  " ## Scope Context",
7571
7636
  " - **Scope:** <SCOPE_SLUG>",
7572
7637
  " - **Sources scanned:** <counts by source type>",
@@ -7712,8 +7777,6 @@ function buildCustomerProfileAnalystSubAgent(paths) {
7712
7777
  " archetype: <ARCHETYPE_SLUG>",
7713
7778
  " ---",
7714
7779
  "",
7715
- " # Competitors: <Archetype Name>",
7716
- "",
7717
7780
  " ## Market Overview",
7718
7781
  " <Brief overview of the competitive landscape this archetype",
7719
7782
  " evaluates.>",
@@ -9620,8 +9683,6 @@ function buildIndustryDiscoveryAnalystSubAgent(paths) {
9620
9683
  " candidate_count: <N>",
9621
9684
  " ---",
9622
9685
  "",
9623
- " # Industry Candidates: <scope>",
9624
- "",
9625
9686
  " ## Scope",
9626
9687
  " <verbatim scope statement from the issue>",
9627
9688
  "",
@@ -9682,8 +9743,6 @@ function buildIndustryDiscoveryAnalystSubAgent(paths) {
9682
9743
  " threshold: <0.0\u20131.0>",
9683
9744
  " ---",
9684
9745
  "",
9685
- " # Industry Evaluation: <scope>",
9686
- "",
9687
9746
  " ## Fit Rubric",
9688
9747
  " <verbatim rubric table, including weights and threshold>",
9689
9748
  "",
@@ -9740,8 +9799,6 @@ function buildIndustryDiscoveryAnalystSubAgent(paths) {
9740
9799
  " evaluation_source: <EVALUATIONS_DIR>/<DISCOVERY_SLUG>.evaluation.md",
9741
9800
  " ---",
9742
9801
  "",
9743
- " # Industry Plan: <scope>",
9744
- "",
9745
9802
  " ## Cleared Verticals",
9746
9803
  " | Vertical | Score | Downstream Issue |",
9747
9804
  " |----------|-------|------------------|",
@@ -10194,8 +10251,6 @@ function buildMaintenanceAuditSubAgent(paths) {
10194
10251
  " status: complete",
10195
10252
  " ---",
10196
10253
  "",
10197
- " # Maintenance Audit: <AUDIT_SLUG>",
10198
- "",
10199
10254
  " ## Scope",
10200
10255
  " - **Docs root:** `<DOCS_ROOT>`",
10201
10256
  " - **Checks run:** <list of check categories>",
@@ -10317,8 +10372,6 @@ function buildMaintenanceAuditSubAgent(paths) {
10317
10372
  " status: complete",
10318
10373
  " ---",
10319
10374
  "",
10320
- " # Maintenance Fix: <AUDIT_SLUG>",
10321
- "",
10322
10375
  " ## Source Audit Report",
10323
10376
  " <link to the Phase 1 audit report>",
10324
10377
  "",
@@ -10419,8 +10472,6 @@ function buildMaintenanceAuditSubAgent(paths) {
10419
10472
  " status: complete",
10420
10473
  " ---",
10421
10474
  "",
10422
- " # Maintenance Verify: <AUDIT_SLUG>",
10423
- "",
10424
10475
  " ## Source Reports",
10425
10476
  " - Scan report: <link>",
10426
10477
  " - Fix report: <link>",
@@ -12011,6 +12062,14 @@ var DEFAULT_SOURCES_THRESHOLDS = {
12011
12062
  smallMax: 2,
12012
12063
  mediumMax: 5
12013
12064
  };
12065
+ var DEFAULT_BUNDLE_OVERRIDES = {
12066
+ "req:write": {
12067
+ acceptanceCriteria: { smallMax: 3, mediumMax: 20 }
12068
+ },
12069
+ "bcm:scaffold": {
12070
+ acceptanceCriteria: { smallMax: 3, mediumMax: 12 }
12071
+ }
12072
+ };
12014
12073
  var DEFAULT_DECOMPOSITION_TEMPLATE = [
12015
12074
  "## Scope gate: issue is too large to dispatch",
12016
12075
  "",
@@ -12050,22 +12109,58 @@ function resolveScopeGate(config) {
12050
12109
  DEFAULT_SOURCES_THRESHOLDS,
12051
12110
  "sources"
12052
12111
  );
12112
+ const bundleOverrides = resolveBundleOverrides(
12113
+ config?.bundleOverrides,
12114
+ DEFAULT_BUNDLE_OVERRIDES
12115
+ );
12053
12116
  return {
12054
12117
  enabled: config?.enabled ?? true,
12055
12118
  acceptanceCriteria: ac,
12056
12119
  sources,
12057
12120
  autoFile: config?.autoFile ?? false,
12058
- decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE
12121
+ decompositionTemplate: config?.decompositionTemplate ?? DEFAULT_DECOMPOSITION_TEMPLATE,
12122
+ bundleOverrides
12123
+ };
12124
+ }
12125
+ function resolveOverrideForLabels(gate, labels) {
12126
+ const overrideKeys = Object.keys(gate.bundleOverrides);
12127
+ if (overrideKeys.length === 0 || labels.length === 0) {
12128
+ return {
12129
+ acceptanceCriteria: gate.acceptanceCriteria,
12130
+ sources: gate.sources
12131
+ };
12132
+ }
12133
+ const sortedLabels = [...labels].sort((a, b) => a.localeCompare(b));
12134
+ const matchedLabel = sortedLabels.find(
12135
+ (label) => Object.prototype.hasOwnProperty.call(gate.bundleOverrides, label)
12136
+ );
12137
+ if (matchedLabel === void 0) {
12138
+ return {
12139
+ acceptanceCriteria: gate.acceptanceCriteria,
12140
+ sources: gate.sources
12141
+ };
12142
+ }
12143
+ const override = gate.bundleOverrides[matchedLabel];
12144
+ return {
12145
+ acceptanceCriteria: override.acceptanceCriteria ?? gate.acceptanceCriteria,
12146
+ sources: override.sources ?? gate.sources,
12147
+ matchedLabel
12059
12148
  };
12060
12149
  }
12061
12150
  function validateScopeGateConfig(config) {
12062
12151
  return resolveScopeGate(config);
12063
12152
  }
12064
- function classifyIssueScope(body, gate) {
12153
+ function classifyIssueScope(body, gate, labels = []) {
12065
12154
  const acCount = countAcceptanceCriteria(body);
12066
12155
  const sourcesCount = countSources(body);
12067
- const scope = resolveScopeClass(acCount, sourcesCount, gate);
12068
- return { scope, acCount, sourcesCount };
12156
+ const effective = resolveOverrideForLabels(gate, labels);
12157
+ const scope = resolveScopeClass(acCount, sourcesCount, effective);
12158
+ return {
12159
+ scope,
12160
+ acCount,
12161
+ sourcesCount,
12162
+ matchedLabel: effective.matchedLabel
12163
+ };
12069
12164
  }
12070
12165
  function renderScopeGateSection(gate) {
12071
12166
  const { acceptanceCriteria: ac, sources } = gate;
@@ -12141,6 +12236,38 @@ function renderScopeGateSection(gate) {
12141
12236
  " is in place."
12142
12237
  );
12143
12238
  }
12239
+ const overrideEntries = Object.entries(gate.bundleOverrides);
12240
+ if (overrideEntries.length > 0) {
12241
+ lines.push(
12242
+ "",
12243
+ "### Per-phase-label thresholds",
12244
+ "",
12245
+ "Issues whose `type:*` or phase label matches an entry below are",
12246
+ "classified against the override's thresholds instead of the",
12247
+ "global `acceptance-criteria` / `sources` defaults. This",
12248
+ "accommodates **content-spec workflows** \u2014 issues whose AC list",
12249
+ "is the per-section content checklist for one cohesive document,",
12250
+ "not a phase-completion checklist that can be decomposed.",
12251
+ "",
12252
+ "| Phase label | AC `mediumMax` | Sources `mediumMax` |",
12253
+ "|-------------|----------------|---------------------|"
12254
+ );
12255
+ const sortedKeys = overrideEntries.map(([k]) => k).sort();
12256
+ for (const label of sortedKeys) {
12257
+ const override = gate.bundleOverrides[label];
12258
+ const acMax = override.acceptanceCriteria?.mediumMax;
12259
+ const sourcesMax = override.sources?.mediumMax;
12260
+ const acCell = acMax === void 0 ? "_(global)_" : `${acMax}`;
12261
+ const sourcesCell = sourcesMax === void 0 ? "_(global)_" : `${sourcesMax}`;
12262
+ lines.push(`| \`${label}\` | ${acCell} | ${sourcesCell} |`);
12263
+ }
12264
+ lines.push(
12265
+ "",
12266
+ "When an issue carries multiple labels that each match an entry",
12267
+ "in this table, the orchestrator picks the **first match in",
12268
+ "alphabetical order on the label name** \u2014 first-wins, deterministic."
12269
+ );
12270
+ }
12144
12271
  lines.push(
12145
12272
  "",
12146
12273
  "### Decomposition-proposal template",
@@ -12157,12 +12284,21 @@ function renderScopeGateSection(gate) {
12157
12284
  }
12158
12285
  function renderScopeGateShellHelpers(gate) {
12159
12286
  const { acceptanceCriteria: ac, sources } = gate;
12160
- return [
12287
+ const lines = [
12161
12288
  "# Classify an issue body against the scope-gate thresholds.",
12162
12289
  "# Reads the issue body from stdin. Echoes one of 'small',",
12163
12290
  "# 'medium', or 'large' on stdout. Writes the observed counts to",
12164
12291
  "# stderr as `ac=<n> sources=<n>` so the caller can embed them in",
12165
12292
  "# the decomposition-proposal comment.",
12293
+ "#",
12294
+ "# Effective per-issue thresholds are read from the four",
12295
+ "# `_EFFECTIVE_*` shell variables. The caller is expected to set",
12296
+ "# them via `bundle_override_for()` before invoking scope_of();",
12297
+ "# defaults preserve the global thresholds.",
12298
+ `_EFFECTIVE_AC_SMALL_MAX="\${_EFFECTIVE_AC_SMALL_MAX:-${ac.smallMax}}"`,
12299
+ `_EFFECTIVE_AC_MEDIUM_MAX="\${_EFFECTIVE_AC_MEDIUM_MAX:-${ac.mediumMax}}"`,
12300
+ `_EFFECTIVE_SOURCES_SMALL_MAX="\${_EFFECTIVE_SOURCES_SMALL_MAX:-${sources.smallMax}}"`,
12301
+ `_EFFECTIVE_SOURCES_MEDIUM_MAX="\${_EFFECTIVE_SOURCES_MEDIUM_MAX:-${sources.mediumMax}}"`,
12166
12302
  "scope_of() {",
12167
12303
  " local body",
12168
12304
  " body=$(cat)",
@@ -12191,15 +12327,79 @@ function renderScopeGateShellHelpers(gate) {
12191
12327
  " END { print count }",
12192
12328
  " ')",
12193
12329
  ` printf 'ac=%s sources=%s\\n' "$ac_count" "$sources_count" >&2`,
12194
- ` if [ "$ac_count" -le ${ac.smallMax} ] && [ "$sources_count" -le ${sources.smallMax} ]; then`,
12330
+ ' if [ "$ac_count" -le "$_EFFECTIVE_AC_SMALL_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_SMALL_MAX" ]; then',
12195
12331
  " echo small",
12196
- ` elif [ "$ac_count" -le ${ac.mediumMax} ] && [ "$sources_count" -le ${sources.mediumMax} ]; then`,
12332
+ ' elif [ "$ac_count" -le "$_EFFECTIVE_AC_MEDIUM_MAX" ] && [ "$sources_count" -le "$_EFFECTIVE_SOURCES_MEDIUM_MAX" ]; then',
12197
12333
  " echo medium",
12198
12334
  " else",
12199
12335
  " echo large",
12200
12336
  " fi",
12337
+ "}",
12338
+ "",
12339
+ "# Resolve per-phase-label threshold overrides. Reads a newline-",
12340
+ "# separated list of label names on stdin and echoes effective",
12341
+ "# threshold variable assignments (one per line) on stdout. The",
12342
+ "# caller `eval`s the output to set the four `_EFFECTIVE_*`",
12343
+ "# variables before calling `scope_of()`. Also echoes",
12344
+ "# `MATCHED_LABEL=<label>` (or `MATCHED_LABEL=`) so the caller can",
12345
+ "# report which override fired.",
12346
+ "#",
12347
+ "# Tie-breaking: when more than one label matches an override key,",
12348
+ "# the alphabetical first-wins rule applies \u2014 the same rule the",
12349
+ "# TypeScript classifier uses.",
12350
+ "bundle_override_for() {",
12351
+ ` local default_ac_small=${ac.smallMax}`,
12352
+ ` local default_ac_medium=${ac.mediumMax}`,
12353
+ ` local default_sources_small=${sources.smallMax}`,
12354
+ ` local default_sources_medium=${sources.mediumMax}`,
12355
+ " # Sort labels alphabetically so the first match is deterministic.",
12356
+ " local sorted_labels",
12357
+ " sorted_labels=$(LC_ALL=C sort)",
12358
+ " local matched_label=''",
12359
+ " local ac_small=$default_ac_small",
12360
+ " local ac_medium=$default_ac_medium",
12361
+ " local sources_small=$default_sources_small",
12362
+ " local sources_medium=$default_sources_medium",
12363
+ " while IFS= read -r label; do",
12364
+ ' [ -z "$label" ] && continue',
12365
+ ` case "$label" in`
12366
+ ];
12367
+ const sortedKeys = Object.keys(gate.bundleOverrides).sort();
12368
+ if (sortedKeys.length === 0) {
12369
+ lines.push(" *) ;;");
12370
+ } else {
12371
+ for (const label of sortedKeys) {
12372
+ const override = gate.bundleOverrides[label];
12373
+ const acThresholds = override.acceptanceCriteria;
12374
+ const sourcesThresholds = override.sources;
12375
+ const branchLines = [` '${label}')`];
12376
+ if (acThresholds !== void 0) {
12377
+ branchLines.push(` ac_small=${acThresholds.smallMax}`);
12378
+ branchLines.push(` ac_medium=${acThresholds.mediumMax}`);
12379
+ }
12380
+ if (sourcesThresholds !== void 0) {
12381
+ branchLines.push(` sources_small=${sourcesThresholds.smallMax}`);
12382
+ branchLines.push(
12383
+ ` sources_medium=${sourcesThresholds.mediumMax}`
12384
+ );
12385
+ }
12386
+ branchLines.push(` matched_label='${label}'`);
12387
+ branchLines.push(" break");
12388
+ branchLines.push(" ;;");
12389
+ lines.push(...branchLines);
12390
+ }
12391
+ }
12392
+ lines.push(
12393
+ " esac",
12394
+ ` done <<<"$sorted_labels"`,
12395
+ ` printf 'MATCHED_LABEL=%s\\n' "$matched_label"`,
12396
+ ` printf '_EFFECTIVE_AC_SMALL_MAX=%s\\n' "$ac_small"`,
12397
+ ` printf '_EFFECTIVE_AC_MEDIUM_MAX=%s\\n' "$ac_medium"`,
12398
+ ` printf '_EFFECTIVE_SOURCES_SMALL_MAX=%s\\n' "$sources_small"`,
12399
+ ` printf '_EFFECTIVE_SOURCES_MEDIUM_MAX=%s\\n' "$sources_medium"`,
12201
12400
  "}"
12202
- ].join("\n");
12401
+ );
12402
+ return lines.join("\n");
12203
12403
  }
12204
12404
  function resolveThresholds(supplied, defaults, field) {
12205
12405
  const merged = {
@@ -12209,6 +12409,42 @@ function resolveThresholds(supplied, defaults, field) {
12209
12409
  assertValidThresholds(merged, field);
12210
12410
  return merged;
12211
12411
  }
12412
+ function resolveBundleOverrides(supplied, defaults) {
12413
+ const out = {};
12414
+ for (const [label, override] of Object.entries(defaults)) {
12415
+ out[label] = override;
12416
+ }
12417
+ if (supplied !== void 0) {
12418
+ for (const label of Object.keys(supplied)) {
12419
+ const value = supplied[label];
12420
+ if (value === void 0) {
12421
+ delete out[label];
12422
+ continue;
12423
+ }
12424
+ const resolved = {};
12425
+ if (value.acceptanceCriteria !== void 0) {
12426
+ resolved.acceptanceCriteria = resolveThresholds(
12427
+ value.acceptanceCriteria,
12428
+ DEFAULT_AC_THRESHOLDS,
12429
+ "acceptanceCriteria"
12430
+ );
12431
+ }
12432
+ if (value.sources !== void 0) {
12433
+ resolved.sources = resolveThresholds(
12434
+ value.sources,
12435
+ DEFAULT_SOURCES_THRESHOLDS,
12436
+ "sources"
12437
+ );
12438
+ }
12439
+ if (resolved.acceptanceCriteria === void 0 && resolved.sources === void 0) {
12440
+ delete out[label];
12441
+ continue;
12442
+ }
12443
+ out[label] = resolved;
12444
+ }
12445
+ }
12446
+ return out;
12447
+ }
12212
12448
  function assertValidThresholds(thresholds, field) {
12213
12449
  const { smallMax, mediumMax } = thresholds;
12214
12450
  for (const [key, value] of [
@@ -12232,8 +12468,8 @@ function assertValidThresholds(thresholds, field) {
12232
12468
  );
12233
12469
  }
12234
12470
  }
12235
- function resolveScopeClass(acCount, sourcesCount, gate) {
12236
- const { acceptanceCriteria: ac, sources } = gate;
12471
+ function resolveScopeClass(acCount, sourcesCount, thresholds) {
12472
+ const { acceptanceCriteria: ac, sources } = thresholds;
12237
12473
  if (acCount <= ac.smallMax && sourcesCount <= sources.smallMax) {
12238
12474
  return "small";
12239
12475
  }
@@ -12813,7 +13049,9 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
12813
13049
  "STALE_BLOCKED_HOURS=168",
12814
13050
  `SCOPE_GATE_ENABLED=${scopeGate.enabled ? "1" : "0"}`,
12815
13051
  `SCOPE_GATE_AUTO_FILE=${scopeGate.autoFile ? "1" : "0"}`,
13052
+ `SCOPE_AC_SMALL_MAX=${scopeGate.acceptanceCriteria.smallMax}`,
12816
13053
  `SCOPE_AC_MEDIUM_MAX=${scopeGate.acceptanceCriteria.mediumMax}`,
13054
+ `SCOPE_SOURCES_SMALL_MAX=${scopeGate.sources.smallMax}`,
12817
13055
  `SCOPE_SOURCES_MEDIUM_MAX=${scopeGate.sources.mediumMax}`,
12818
13056
  `RUN_RATIO_ENABLED=${runRatio.enabled ? "1" : "0"}`,
12819
13057
  `RUN_RATIO_DISPATCH_PER_HOUSEKEEPING=${runRatio.ratio}`,
@@ -13167,12 +13405,45 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
13167
13405
  ' echo "SCOPE #${issue_num} disabled"',
13168
13406
  " return 0",
13169
13407
  " fi",
13408
+ " # Fetch the body and labels in a single API call so we resolve",
13409
+ " # any per-phase-label override against the issue's actual labels.",
13410
+ " local issue_json",
13411
+ ' issue_json=$(gh issue view "$issue_num" --json body,labels 2>/dev/null || echo "")',
13412
+ ' if [[ -z "$issue_json" ]]; then',
13413
+ ' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
13414
+ " return 1",
13415
+ " fi",
13170
13416
  " local body",
13171
- ` body=$(gh issue view "$issue_num" --json body --jq '.body' 2>/dev/null || echo "")`,
13417
+ ` body=$(printf '%s' "$issue_json" | jq -r '.body // ""')`,
13172
13418
  ' if [[ -z "$body" ]]; then',
13173
13419
  ' echo "SCOPE #${issue_num} unknown \u2014 could not read issue body"',
13174
13420
  " return 1",
13175
13421
  " fi",
13422
+ " local labels",
13423
+ ` labels=$(printf '%s' "$issue_json" | jq -r '.labels[]?.name // empty')`,
13424
+ " # Resolve the effective thresholds. `bundle_override_for` reads",
13425
+ " # labels on stdin and emits `KEY=VALUE` assignments (one per line)",
13426
+ " # for the four `_EFFECTIVE_*` variables plus `MATCHED_LABEL`.",
13427
+ " local override_output",
13428
+ ` override_output=$(printf '%s\\n' "$labels" | bundle_override_for)`,
13429
+ " local matched_label=''",
13430
+ " local _EFFECTIVE_AC_SMALL_MAX=$SCOPE_AC_SMALL_MAX",
13431
+ " local _EFFECTIVE_AC_MEDIUM_MAX=$SCOPE_AC_MEDIUM_MAX",
13432
+ " local _EFFECTIVE_SOURCES_SMALL_MAX=$SCOPE_SOURCES_SMALL_MAX",
13433
+ " local _EFFECTIVE_SOURCES_MEDIUM_MAX=$SCOPE_SOURCES_MEDIUM_MAX",
13434
+ " while IFS= read -r assignment; do",
13435
+ ' [ -z "$assignment" ] && continue',
13436
+ ' case "$assignment" in',
13437
+ " MATCHED_LABEL=*) matched_label=${assignment#MATCHED_LABEL=} ;;",
13438
+ " _EFFECTIVE_AC_SMALL_MAX=*) _EFFECTIVE_AC_SMALL_MAX=${assignment#_EFFECTIVE_AC_SMALL_MAX=} ;;",
13439
+ " _EFFECTIVE_AC_MEDIUM_MAX=*) _EFFECTIVE_AC_MEDIUM_MAX=${assignment#_EFFECTIVE_AC_MEDIUM_MAX=} ;;",
13440
+ " _EFFECTIVE_SOURCES_SMALL_MAX=*) _EFFECTIVE_SOURCES_SMALL_MAX=${assignment#_EFFECTIVE_SOURCES_SMALL_MAX=} ;;",
13441
+ " _EFFECTIVE_SOURCES_MEDIUM_MAX=*) _EFFECTIVE_SOURCES_MEDIUM_MAX=${assignment#_EFFECTIVE_SOURCES_MEDIUM_MAX=} ;;",
13442
+ " esac",
13443
+ ' done <<<"$override_output"',
13444
+ " # Export the effective thresholds so scope_of() picks them up.",
13445
+ " export _EFFECTIVE_AC_SMALL_MAX _EFFECTIVE_AC_MEDIUM_MAX",
13446
+ " export _EFFECTIVE_SOURCES_SMALL_MAX _EFFECTIVE_SOURCES_MEDIUM_MAX",
13176
13447
  " local scope counts",
13177
13448
  " # scope_of writes counts to stderr (ac=N sources=N); capture both.",
13178
13449
  ` scope=$(printf '%s' "$body" | scope_of 2> >(read -r counts; echo "$counts" >&2))`,
@@ -13186,9 +13457,17 @@ function buildCheckBlockedScript(tiers, scopeGate, runRatio) {
13186
13457
  ` sources_count=$(echo "$stderr_capture" | grep -oE 'sources=[0-9]+' | head -1 | cut -d= -f2)`,
13187
13458
  " ac_count=${ac_count:-0}",
13188
13459
  " sources_count=${sources_count:-0}",
13189
- " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s\\n' \\",
13190
- ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13191
- ' "$SCOPE_AC_MEDIUM_MAX" "$SCOPE_SOURCES_MEDIUM_MAX" "$SCOPE_GATE_AUTO_FILE"',
13460
+ ' if [ -n "$matched_label" ]; then',
13461
+ " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s matched_label=%s\\n' \\",
13462
+ ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13463
+ ' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
13464
+ ' "$SCOPE_GATE_AUTO_FILE" "$matched_label"',
13465
+ " else",
13466
+ " printf 'SCOPE #%s %s ac=%s sources=%s ac_limit=%s sources_limit=%s auto_file=%s\\n' \\",
13467
+ ' "$issue_num" "$scope" "$ac_count" "$sources_count" \\',
13468
+ ' "$_EFFECTIVE_AC_MEDIUM_MAX" "$_EFFECTIVE_SOURCES_MEDIUM_MAX" \\',
13469
+ ' "$SCOPE_GATE_AUTO_FILE"',
13470
+ " fi",
13192
13471
  "}",
13193
13472
  "",
13194
13473
  "cmd_tick() {",
@@ -13393,9 +13672,16 @@ var orchestratorSubAgent = {
13393
13672
  "The output is one line of the form:",
13394
13673
  "",
13395
13674
  "```",
13396
- "SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1>",
13675
+ "SCOPE #<n> <small|medium|large> ac=<n> sources=<n> ac_limit=<n> sources_limit=<n> auto_file=<0|1> [matched_label=<label>]",
13397
13676
  "```",
13398
13677
  "",
13678
+ "When `matched_label=<label>` is present, the scope gate applied a",
13679
+ "per-phase-label override (e.g. `req:write` raises the AC ceiling to",
13680
+ "20 for content-spec workflows whose AC list is the per-section",
13681
+ "checklist for one document). The `ac_limit` / `sources_limit`",
13682
+ "values reflect the override; the global thresholds are documented",
13683
+ "in the **Scope gate** section in `CLAUDE.md`.",
13684
+ "",
13399
13685
  "If the scope is `small` or `medium`, continue \u2014 the issue is",
13400
13686
  "dispatchable. Record the first `PICK` line as the next work item",
13401
13687
  "and proceed to Phase G:",
@@ -14352,8 +14638,6 @@ function buildPeopleProfileAnalystSubAgent(paths) {
14352
14638
  " parent_issue: <N>",
14353
14639
  " ---",
14354
14640
  "",
14355
- " # Research Notes: <person name>",
14356
- "",
14357
14641
  " ## Framing",
14358
14642
  " <why this person was requested and what to learn>",
14359
14643
  "",
@@ -14415,8 +14699,6 @@ function buildPeopleProfileAnalystSubAgent(paths) {
14415
14699
  " notes: <NOTES_DIR>/<PERSON_SLUG>.notes.md",
14416
14700
  " ---",
14417
14701
  "",
14418
- " # <person name>",
14419
- "",
14420
14702
  " ## Summary",
14421
14703
  " <2\u20134 sentence elevator description: who they are, what they do,",
14422
14704
  " why they matter to this project>",
@@ -17000,8 +17282,6 @@ jurisdiction: <federal | state | provincial | international | local>
17000
17282
  industry: <industry-slug or 'platform'>
17001
17283
  ---
17002
17284
 
17003
- # <Regulation Name>
17004
-
17005
17285
  | Field | Value |
17006
17286
  |-------|-------|
17007
17287
  | **Official name** | <full legal name> |
@@ -17372,8 +17652,6 @@ function buildRegulatoryResearchAnalystSubAgent(paths) {
17372
17652
  " scope: <SCOPE_SLUG>",
17373
17653
  " ---",
17374
17654
  "",
17375
- " # Regulatory Scan: <Scope>",
17376
- "",
17377
17655
  " ## Scope Context",
17378
17656
  " - **Scope type:** <industry / jurisdiction / platform>",
17379
17657
  " - **Scope identifier:** <SCOPE_SLUG>",
@@ -17424,7 +17702,12 @@ function buildRegulatoryResearchAnalystSubAgent(paths) {
17424
17702
  "",
17425
17703
  "7. **Create or update the scope index page** at",
17426
17704
  " `<SCOPE_INDEX_PAGE>` so downstream regulations link back to a",
17427
- " coherent landing page for the scope.",
17705
+ " coherent landing page for the scope. Follow the",
17706
+ " `stub-index-convention` rule: the body must carry a 1\u20132",
17707
+ " paragraph contextual summary plus a grouped, linked listing of",
17708
+ " every regulation in the scope (e.g. by jurisdiction). No body",
17709
+ " `# Heading` \u2014 the frontmatter `title:` already renders as the",
17710
+ " page H1.",
17428
17711
  "",
17429
17712
  "8. **Commit and push** the scan report and the scope index page.",
17430
17713
  " Close the scan issue.",
@@ -18282,8 +18565,6 @@ function buildRequirementsAnalystSubAgent(paths) {
18282
18565
  " status: complete",
18283
18566
  " ---",
18284
18567
  "",
18285
- " # Requirements Scan: <scope>",
18286
- "",
18287
18568
  " ## Source Documents Reviewed",
18288
18569
  " - <path> \u2014 <brief description>",
18289
18570
  "",
@@ -18419,42 +18700,32 @@ function buildRequirementsAnalystSubAgent(paths) {
18419
18700
  " directory under `<REQUIREMENTS_ROOT>/<category>/` to find the next",
18420
18701
  " available `NNN` for each proposed requirement.",
18421
18702
  "",
18422
- "5. **Create requirement issues.** For each proposal:",
18703
+ "5. **Create requirement issues.** For each proposal, file a",
18704
+ " `req:write` issue using the canonical recipe documented in",
18705
+ " `## Template: req:write` of",
18706
+ " `docs/src/content/docs/agents/issue-templates.md`.",
18423
18707
  "",
18424
18708
  " All `type:requirement` issues default to `priority:medium` (override",
18425
18709
  " only if the proposal's priority was explicitly High or Low). Each",
18426
18710
  " issue must also carry the `req:write` phase label plus the matching",
18427
18711
  " `tier:*` label so the downstream `requirements-writer` bundle picks",
18428
- " it up with the correct tier.",
18429
- "",
18430
- " ```bash",
18431
- " gh issue create \\",
18432
- ' --title "docs(<category>): <PREFIX>-<NNN> \u2014 <title>" \\',
18433
- ' --label "type:requirement" --label "req:write" --label "tier:<tier-slug>" --label "status:ready" --label "priority:medium" \\',
18434
- ' --body "## Objective',
18435
- " Write <PREFIX>-<NNN> \u2014 <title>.",
18436
- "",
18437
- " **Category:** <BR/FR/NFR/TR/ADR/SEC/DR/INT/OPS/UX/MT>",
18438
- " **Tier:** <platform/industry/customer-workflow/consumer-app>",
18439
- " **Output Path:** <REQUIREMENTS_ROOT>/<category-dir>/<PREFIX>-<NNN>-<slug>.md",
18440
- "",
18441
- " ## Context",
18442
- " - **Gap identified by:** requirements scan of <source>",
18443
- " - **Proposals file:** <RESEARCH_REQUIREMENTS_ROOT>/req-proposals-<scope>-<date>.md",
18712
+ " it up with the correct tier. Concretely, the label list includes",
18713
+ ' `--label "type:requirement"`, `--label "req:write"`,',
18714
+ ' `--label "tier:<tier-slug>"`, `--label "status:ready"`, and',
18715
+ ' `--label "priority:medium"`.',
18444
18716
  "",
18445
- " ## Inputs / Read",
18446
- " - **Depends on:** (none)",
18447
- " - **Read:** <RESEARCH_REQUIREMENTS_ROOT>/req-proposals-<scope>-<date>.md, <source docs>",
18717
+ " The body must carry the three writer-required fields in an",
18718
+ " `## Objective` block, written as bold-prefixed lines so the",
18719
+ " writer's intake parser can pull them out:",
18448
18720
  "",
18449
- " ## Acceptance Criteria",
18450
- " - [ ] Requirement document follows <category> template",
18451
- " - [ ] Traceability links to source BCM/competitive/product doc",
18452
- " - [ ] Registry index updated",
18453
- " - [ ] Decision authority rules followed (direct write vs. proposed)",
18721
+ " - `**Category:** <BR/FR/NFR/TR/ADR/SEC/DR/INT/OPS/UX/MT>`",
18722
+ " - `**Tier:** <platform/industry/customer-workflow/consumer-app>`",
18723
+ " - `**Output Path:** <REQUIREMENTS_ROOT>/<category-dir>/<PREFIX>-<NNN>-<slug>.md`",
18454
18724
  "",
18455
- " ## Scope Size",
18456
- ' small"',
18457
- " ```",
18725
+ " Then under `## Inputs / Read`, list the proposals file",
18726
+ " (`<RESEARCH_REQUIREMENTS_ROOT>/req-proposals-<scope>-<date>.md`)",
18727
+ " and any source documents the writer should consult (BCM model",
18728
+ " docs, competitive analyses, product docs).",
18458
18729
  "",
18459
18730
  " When one of Category / Tier / Output Path could not be derived",
18460
18731
  " from the proposal (for example, the proposal omitted the Tier",
@@ -18630,8 +18901,6 @@ var TEMPLATE_BR = `---
18630
18901
  title: "BR-NNN: [Business Requirement Title]"
18631
18902
  ---
18632
18903
 
18633
- # BR-NNN: [Business Requirement Title]
18634
-
18635
18904
  ## Metadata
18636
18905
 
18637
18906
  | Field | Value |
@@ -18739,8 +19008,6 @@ var TEMPLATE_FR = `---
18739
19008
  title: "FR-NNN: [Functional Requirement Title]"
18740
19009
  ---
18741
19010
 
18742
- # FR-NNN: [Functional Requirement Title]
18743
-
18744
19011
  ## Metadata
18745
19012
 
18746
19013
  | Field | Value |
@@ -18880,8 +19147,6 @@ var TEMPLATE_NFR = `---
18880
19147
  title: "NFR-NNN: [Non-Functional Requirement Title]"
18881
19148
  ---
18882
19149
 
18883
- # NFR-NNN: [Non-Functional Requirement Title]
18884
-
18885
19150
  ## Metadata
18886
19151
 
18887
19152
  | Field | Value |
@@ -18982,8 +19247,6 @@ var TEMPLATE_TR = `---
18982
19247
  title: "TR-NNN: [Technical Requirement Title]"
18983
19248
  ---
18984
19249
 
18985
- # TR-NNN: [Technical Requirement Title]
18986
-
18987
19250
  ## Metadata
18988
19251
 
18989
19252
  | Field | Value |
@@ -19135,8 +19398,6 @@ var TEMPLATE_ADR = `---
19135
19398
  title: "ADR-NNN: [Decision Title]"
19136
19399
  ---
19137
19400
 
19138
- # ADR-NNN: [Decision Title]
19139
-
19140
19401
  ## Metadata
19141
19402
 
19142
19403
  | Field | Value |
@@ -19265,8 +19526,6 @@ var TEMPLATE_SEC = `---
19265
19526
  title: "SEC-NNN: [Security Requirement Title]"
19266
19527
  ---
19267
19528
 
19268
- # SEC-NNN: [Security Requirement Title]
19269
-
19270
19529
  ## Metadata
19271
19530
 
19272
19531
  | Field | Value |
@@ -19378,8 +19637,6 @@ var TEMPLATE_DR = `---
19378
19637
  title: "DR-NNN: [Data Requirement Title]"
19379
19638
  ---
19380
19639
 
19381
- # DR-NNN: [Data Requirement Title]
19382
-
19383
19640
  ## Metadata
19384
19641
 
19385
19642
  | Field | Value |
@@ -19494,8 +19751,6 @@ var TEMPLATE_INT = `---
19494
19751
  title: "INT-NNN: [Integration Requirement Title]"
19495
19752
  ---
19496
19753
 
19497
- # INT-NNN: [Integration Requirement Title]
19498
-
19499
19754
  ## Metadata
19500
19755
 
19501
19756
  | Field | Value |
@@ -19627,8 +19882,6 @@ var TEMPLATE_OPS = `---
19627
19882
  title: "OPS-NNN: [Operational Requirement Title]"
19628
19883
  ---
19629
19884
 
19630
- # OPS-NNN: [Operational Requirement Title]
19631
-
19632
19885
  ## Metadata
19633
19886
 
19634
19887
  | Field | Value |
@@ -19743,8 +19996,6 @@ var TEMPLATE_UX = `---
19743
19996
  title: "UX-NNN: [UX Requirement Title]"
19744
19997
  ---
19745
19998
 
19746
- # UX-NNN: [UX Requirement Title]
19747
-
19748
19999
  ## Metadata
19749
20000
 
19750
20001
  | Field | Value |
@@ -19861,8 +20112,6 @@ var TEMPLATE_MT = `---
19861
20112
  title: "MT-NNN: [Multi-Tenancy Requirement Title]"
19862
20113
  ---
19863
20114
 
19864
- # MT-NNN: [Multi-Tenancy Requirement Title]
19865
-
19866
20115
  ## Metadata
19867
20116
 
19868
20117
  | Field | Value |
@@ -20015,8 +20264,6 @@ var TEMPLATE_REQUIREMENTS_README = `---
20015
20264
  title: "[Project Name] \u2014 Requirements Documentation"
20016
20265
  ---
20017
20266
 
20018
- # [Project Name] \u2014 Requirements Documentation
20019
-
20020
20267
  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.
20021
20268
 
20022
20269
  ## Taxonomy Overview
@@ -20773,12 +21020,9 @@ function buildRequirementsWriterSubAgent(paths) {
20773
21020
  "tier: platform",
20774
21021
  "---",
20775
21022
  "",
20776
- "# FR-001: User Registration",
20777
- "",
20778
21023
  "...",
20779
21024
  "```",
20780
21025
  "",
20781
- "The `title` value must match the document's `# Heading` line.",
20782
21026
  "Titles containing colons must be wrapped in double quotes. The",
20783
21027
  "`tier` field must be one of `platform`, `industry`,",
20784
21028
  "`customer-workflow`, or `consumer-app` (use whatever tier slugs the",
@@ -22842,8 +23086,6 @@ function buildResearchAnalystSubAgent(paths) {
22842
23086
  " slice_count: <N>",
22843
23087
  " ---",
22844
23088
  "",
22845
- " # Research Scope: <question>",
22846
- "",
22847
23089
  " ## Question",
22848
23090
  " <verbatim question from the issue>",
22849
23091
  "",
@@ -22913,8 +23155,6 @@ function buildResearchAnalystSubAgent(paths) {
22913
23155
  " parent_issue: <N>",
22914
23156
  " ---",
22915
23157
  "",
22916
- " # Slice NN: <title>",
22917
- "",
22918
23158
  " ## Question",
22919
23159
  " <the slice-level question>",
22920
23160
  "",
@@ -22979,8 +23219,6 @@ function buildResearchAnalystSubAgent(paths) {
22979
23219
  " slices_read: <count>",
22980
23220
  " ---",
22981
23221
  "",
22982
- " # Verification Report: <question>",
22983
- "",
22984
23222
  " ## Acceptance Criteria Coverage",
22985
23223
  " - [x] <criterion> \u2014 covered by slice(s) <NN, NN>",
22986
23224
  " - [ ] <criterion> \u2014 **not covered**; gap noted in deliverable",
@@ -23588,8 +23826,6 @@ function buildSoftwareProfileAnalystSubAgent(paths) {
23588
23826
  " parent_issue: <N>",
23589
23827
  " ---",
23590
23828
  "",
23591
- " # Research Notes: <product name>",
23592
- "",
23593
23829
  " ## Framing",
23594
23830
  " <why this product was requested and what to learn>",
23595
23831
  "",
@@ -23650,8 +23886,6 @@ function buildSoftwareProfileAnalystSubAgent(paths) {
23650
23886
  " notes: <NOTES_DIR>/<PRODUCT_SLUG>.notes.md",
23651
23887
  " ---",
23652
23888
  "",
23653
- " # <product name>",
23654
- "",
23655
23889
  " ## Summary",
23656
23890
  " <2\u20134 sentence elevator description: what it does, who it's for>",
23657
23891
  "",
@@ -23736,8 +23970,6 @@ function buildSoftwareProfileAnalystSubAgent(paths) {
23736
23970
  "updated: YYYY-MM-DD",
23737
23971
  "---",
23738
23972
  "",
23739
- "# Software Feature Matrix",
23740
- "",
23741
23973
  "| product | feature | <segment-1> | <segment-2> | ... | score | source |",
23742
23974
  "|---------|---------|-------------|-------------|-----|-------|--------|",
23743
23975
  "| <slug> | <feature-name> | 0\u20131 | 0\u20131 | ... | <weighted-sum> | <profile-path> |",
@@ -24365,8 +24597,6 @@ title: "<Standard Name>"
24365
24597
  description: "Overview and cross-version comparison of <Standard Name>."
24366
24598
  ---
24367
24599
 
24368
- # <Standard Name>
24369
-
24370
24600
  | Field | Value |
24371
24601
  |-------|-------|
24372
24602
  | **Standard** | <standard name> |
@@ -24684,8 +24914,6 @@ function buildStandardsResearchAnalystSubAgent(paths) {
24684
24914
  " standard: <STANDARD_SLUG>",
24685
24915
  " ---",
24686
24916
  "",
24687
- " # Query Plan: <Standard Name>",
24688
- "",
24689
24917
  " ## Context",
24690
24918
  " <1\u20132 paragraphs on why this standard matters to the consuming",
24691
24919
  " project and what gaps this research campaign fills.>",
@@ -24887,7 +25115,14 @@ function buildStandardsResearchAnalystSubAgent(paths) {
24887
25115
  " the overview page, then populate each section from the version",
24888
25116
  " pages.",
24889
25117
  "",
24890
- "4. **Write the overview page** at `<OVERVIEW_PAGE>`. Fill:",
25118
+ "4. **Write the overview page** at `<OVERVIEW_PAGE>`. Follow",
25119
+ " the `stub-index-convention` rule: the body opens with a",
25120
+ " 1\u20132 paragraph contextual summary of the standard and its",
25121
+ " versions, and the version pages, modules, and extensions",
25122
+ " sections below double as the grouped, linked listing of",
25123
+ " every child page in the standard's folder. No body",
25124
+ " `# Heading` \u2014 the frontmatter `title:` already renders as",
25125
+ " the page H1. Required sections to fill:",
24891
25126
  " - **Version Timeline** \u2014 publication dates, status, milestones",
24892
25127
  " - **Module / Resource Maturity Progression** \u2014 matrix with",
24893
25128
  " modules as rows and versions as columns, using the standard's",
@@ -32676,6 +32911,7 @@ var TypeScriptConfig = class extends import_projen23.Component {
32676
32911
  DEFAULT_API_EXTRACTOR_REPORT_FILENAME,
32677
32912
  DEFAULT_API_EXTRACTOR_REPORT_FOLDER,
32678
32913
  DEFAULT_AUDIT_REPORT_DIR,
32914
+ DEFAULT_BUNDLE_OVERRIDES,
32679
32915
  DEFAULT_DECOMPOSITION_TEMPLATE,
32680
32916
  DEFAULT_DISPATCH_MODEL,
32681
32917
  DEFAULT_DISPATCH_TO_HOUSEKEEPING_RATIO,
@@ -32847,6 +33083,7 @@ var TypeScriptConfig = class extends import_projen23.Component {
32847
33083
  renderSkillEvalsRuleContent,
32848
33084
  renderSkillEvalsRunnerScript,
32849
33085
  renderSourceTierExamples,
33086
+ renderStubIndexConventionRuleContent,
32850
33087
  renderUnblockDependentsScript,
32851
33088
  renderUnblockDependentsSection,
32852
33089
  requirementsAnalystBundle,
@@ -32861,6 +33098,7 @@ var TypeScriptConfig = class extends import_projen23.Component {
32861
33098
  resolveModelAlias,
32862
33099
  resolveOrchestratorAssets,
32863
33100
  resolveOutdirFromPackageName,
33101
+ resolveOverrideForLabels,
32864
33102
  resolveProgressFiles,
32865
33103
  resolveRunRatio,
32866
33104
  resolveScheduledTasks,