@codedrifters/configulator 0.0.295 → 0.0.297

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
@@ -1143,8 +1143,8 @@ function resolveIssueTemplates(config) {
1143
1143
  function validateIssueTemplatesConfig(config) {
1144
1144
  return resolveIssueTemplates(config);
1145
1145
  }
1146
- function renderIssueTemplatesRuleContent(it) {
1147
- if (!it.enabled) {
1146
+ function renderIssueTemplatesRuleContent(it, hasDownstreamBundles = true) {
1147
+ if (!it.enabled || !hasDownstreamBundles) {
1148
1148
  return [
1149
1149
  "# Issue Templates",
1150
1150
  "",
@@ -11294,6 +11294,238 @@ function assertValidStateFilePath(stateFilePath) {
11294
11294
  }
11295
11295
  }
11296
11296
 
11297
+ // src/agent/bundles/bundle-ownership.ts
11298
+ var BUNDLE_OWNERSHIP = {
11299
+ agenda: {
11300
+ typeLabels: ["agenda"],
11301
+ phaseLabelPrefixes: ["agenda:"],
11302
+ scheduledTaskIds: ["worker-agenda"],
11303
+ emitsDocs: true,
11304
+ downstreamIssueKinds: true
11305
+ },
11306
+ "bcm-writer": {
11307
+ typeLabels: ["bcm-document"],
11308
+ phaseLabelPrefixes: ["bcm:"],
11309
+ scheduledTaskIds: ["worker-bcm-writer"],
11310
+ emitsDocs: true,
11311
+ downstreamIssueKinds: true
11312
+ },
11313
+ "business-models": {
11314
+ typeLabels: ["business-model"],
11315
+ phaseLabelPrefixes: ["business-models:"],
11316
+ scheduledTaskIds: ["worker-business-models"],
11317
+ emitsDocs: true,
11318
+ downstreamIssueKinds: true
11319
+ },
11320
+ "company-profile": {
11321
+ typeLabels: ["company-profile"],
11322
+ phaseLabelPrefixes: ["company:"],
11323
+ scheduledTaskIds: ["worker-company-profile"],
11324
+ emitsDocs: true,
11325
+ downstreamIssueKinds: true
11326
+ },
11327
+ "customer-profile": {
11328
+ typeLabels: ["customer-profile"],
11329
+ phaseLabelPrefixes: ["customer:"],
11330
+ scheduledTaskIds: ["worker-customer-profile"],
11331
+ emitsDocs: true,
11332
+ downstreamIssueKinds: true
11333
+ },
11334
+ "docs-sync": {
11335
+ typeLabels: ["docs-sync"],
11336
+ phaseLabelPrefixes: ["docs-sync:"],
11337
+ scheduledTaskIds: ["worker-docs-sync"],
11338
+ emitsDocs: true,
11339
+ downstreamIssueKinds: true
11340
+ },
11341
+ "industry-discovery": {
11342
+ typeLabels: ["industry-discovery"],
11343
+ phaseLabelPrefixes: ["industry:"],
11344
+ scheduledTaskIds: ["worker-industry-discovery"],
11345
+ emitsDocs: true,
11346
+ downstreamIssueKinds: true
11347
+ },
11348
+ "maintenance-audit": {
11349
+ typeLabels: ["maintenance"],
11350
+ phaseLabelPrefixes: ["maint:"],
11351
+ scheduledTaskIds: ["worker-maintenance"],
11352
+ emitsDocs: false,
11353
+ downstreamIssueKinds: true
11354
+ },
11355
+ "meeting-analysis": {
11356
+ typeLabels: ["meeting-processing"],
11357
+ phaseLabelPrefixes: ["meeting:"],
11358
+ scheduledTaskIds: ["worker-meeting-analysis"],
11359
+ emitsDocs: true,
11360
+ downstreamIssueKinds: true
11361
+ },
11362
+ orchestrator: {
11363
+ // The orchestrator pipeline manager itself ships with the
11364
+ // `worker-orchestrator` scheduled task. It owns no `type:*` label
11365
+ // (it dispatches every type) and no phase-label prefix.
11366
+ typeLabels: [],
11367
+ phaseLabelPrefixes: [],
11368
+ scheduledTaskIds: ["worker-orchestrator"],
11369
+ emitsDocs: false,
11370
+ downstreamIssueKinds: false
11371
+ },
11372
+ "people-profile": {
11373
+ typeLabels: ["people-profile"],
11374
+ phaseLabelPrefixes: ["people:"],
11375
+ scheduledTaskIds: ["worker-people-profile"],
11376
+ emitsDocs: true,
11377
+ downstreamIssueKinds: true
11378
+ },
11379
+ "pr-review": {
11380
+ typeLabels: ["pr-review"],
11381
+ phaseLabelPrefixes: [],
11382
+ scheduledTaskIds: ["worker-pr-review"],
11383
+ emitsDocs: false,
11384
+ downstreamIssueKinds: false
11385
+ },
11386
+ "regulatory-research": {
11387
+ typeLabels: ["regulatory-research"],
11388
+ phaseLabelPrefixes: ["regulatory:"],
11389
+ scheduledTaskIds: ["worker-regulatory-research"],
11390
+ emitsDocs: true,
11391
+ downstreamIssueKinds: true
11392
+ },
11393
+ "requirements-analyst": {
11394
+ typeLabels: ["requirement"],
11395
+ phaseLabelPrefixes: ["req:"],
11396
+ scheduledTaskIds: ["worker-requirements-analyst"],
11397
+ emitsDocs: true,
11398
+ downstreamIssueKinds: true
11399
+ },
11400
+ "requirements-reviewer": {
11401
+ // Co-owns `type:requirement` with the analyst and writer; owns
11402
+ // the `req:review` and `req:deprecate` phase labels exactly.
11403
+ typeLabels: ["requirement"],
11404
+ phaseLabelPrefixes: ["req:review", "req:deprecate"],
11405
+ scheduledTaskIds: ["worker-requirements-reviewer"],
11406
+ emitsDocs: true,
11407
+ downstreamIssueKinds: true
11408
+ },
11409
+ "requirements-writer": {
11410
+ // Co-owns `type:requirement` with the analyst and reviewer; owns
11411
+ // the `req:write` phase label exactly.
11412
+ typeLabels: ["requirement"],
11413
+ phaseLabelPrefixes: ["req:write"],
11414
+ scheduledTaskIds: ["worker-requirements-writer"],
11415
+ emitsDocs: true,
11416
+ downstreamIssueKinds: true
11417
+ },
11418
+ "research-pipeline": {
11419
+ typeLabels: ["research"],
11420
+ phaseLabelPrefixes: ["research:"],
11421
+ scheduledTaskIds: ["worker-research"],
11422
+ emitsDocs: true,
11423
+ downstreamIssueKinds: true
11424
+ },
11425
+ "software-profile": {
11426
+ typeLabels: ["software-profile"],
11427
+ phaseLabelPrefixes: ["software:"],
11428
+ scheduledTaskIds: ["worker-software-profile"],
11429
+ emitsDocs: true,
11430
+ downstreamIssueKinds: true
11431
+ },
11432
+ "standards-research": {
11433
+ typeLabels: ["standards-research"],
11434
+ phaseLabelPrefixes: ["standards:"],
11435
+ scheduledTaskIds: ["worker-standards-research"],
11436
+ emitsDocs: true,
11437
+ downstreamIssueKinds: true
11438
+ }
11439
+ };
11440
+ function isTypeLabelOwnedByExcluded(typeLabel, excludedBundles) {
11441
+ if (excludedBundles.length === 0) {
11442
+ return false;
11443
+ }
11444
+ for (const bundleName of excludedBundles) {
11445
+ const ownership = BUNDLE_OWNERSHIP[bundleName];
11446
+ if (!ownership) {
11447
+ continue;
11448
+ }
11449
+ if (ownership.typeLabels.includes(typeLabel)) {
11450
+ const owners = findOwnersOfTypeLabel(typeLabel);
11451
+ const allExcluded = owners.every(
11452
+ (owner) => excludedBundles.includes(owner)
11453
+ );
11454
+ if (allExcluded) {
11455
+ return true;
11456
+ }
11457
+ }
11458
+ }
11459
+ return false;
11460
+ }
11461
+ function isPhaseLabelOwnedByExcluded(phaseLabel, excludedBundles) {
11462
+ if (excludedBundles.length === 0) {
11463
+ return false;
11464
+ }
11465
+ for (const bundleName of excludedBundles) {
11466
+ const ownership = BUNDLE_OWNERSHIP[bundleName];
11467
+ if (!ownership) {
11468
+ continue;
11469
+ }
11470
+ for (const entry of ownership.phaseLabelPrefixes) {
11471
+ if (entry.endsWith(":")) {
11472
+ if (phaseLabel.startsWith(entry)) {
11473
+ return true;
11474
+ }
11475
+ } else if (phaseLabel === entry) {
11476
+ return true;
11477
+ }
11478
+ }
11479
+ }
11480
+ return false;
11481
+ }
11482
+ function isScheduledTaskOwnedByExcluded(taskId, excludedBundles) {
11483
+ if (excludedBundles.length === 0) {
11484
+ return false;
11485
+ }
11486
+ for (const bundleName of excludedBundles) {
11487
+ const ownership = BUNDLE_OWNERSHIP[bundleName];
11488
+ if (!ownership) {
11489
+ continue;
11490
+ }
11491
+ if (ownership.scheduledTaskIds.includes(taskId)) {
11492
+ return true;
11493
+ }
11494
+ }
11495
+ return false;
11496
+ }
11497
+ function hasAnyDocsEmittingBundle(excludedBundles) {
11498
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
11499
+ if (!ownership.emitsDocs) {
11500
+ continue;
11501
+ }
11502
+ if (!excludedBundles.includes(bundleName)) {
11503
+ return true;
11504
+ }
11505
+ }
11506
+ return false;
11507
+ }
11508
+ function hasAnyDownstreamIssueKindBundle(excludedBundles) {
11509
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
11510
+ if (!ownership.downstreamIssueKinds) {
11511
+ continue;
11512
+ }
11513
+ if (!excludedBundles.includes(bundleName)) {
11514
+ return true;
11515
+ }
11516
+ }
11517
+ return false;
11518
+ }
11519
+ function findOwnersOfTypeLabel(typeLabel) {
11520
+ const owners = [];
11521
+ for (const [bundleName, ownership] of Object.entries(BUNDLE_OWNERSHIP)) {
11522
+ if (ownership.typeLabels.includes(typeLabel)) {
11523
+ owners.push(bundleName);
11524
+ }
11525
+ }
11526
+ return owners;
11527
+ }
11528
+
11297
11529
  // src/agent/bundles/scheduled-tasks.ts
11298
11530
  var SCHEDULED_TASK_MODEL_VALUES = ["opus", "sonnet", "haiku"];
11299
11531
  var SCHEDULED_TASK_KIND_VALUES = ["issue-worker", "pipeline"];
@@ -11558,17 +11790,23 @@ var DEFAULT_SCHEDULED_TASK_ENTRIES = [
11558
11790
  description: "Documentation-sync drift-detection and audit pipeline (scaffolding release \u2014 behavior carried by downstream child issues of the parent docs-sync epic)."
11559
11791
  }
11560
11792
  ];
11561
- function resolveScheduledTasks(config) {
11793
+ function resolveScheduledTasks(config, excludeBundles = []) {
11562
11794
  const root = config?.root ?? DEFAULT_SCHEDULED_TASKS_ROOT;
11563
11795
  assertValidRoot(root);
11564
11796
  const merged = /* @__PURE__ */ new Map();
11565
11797
  for (const entry of DEFAULT_SCHEDULED_TASK_ENTRIES) {
11798
+ if (isScheduledTaskOwnedByExcluded(entry.taskId, excludeBundles)) {
11799
+ continue;
11800
+ }
11566
11801
  merged.set(entry.taskId, entry);
11567
11802
  }
11568
11803
  if (config?.overrides) {
11569
11804
  for (const [taskId, override] of Object.entries(config.overrides)) {
11570
11805
  const existing = merged.get(taskId);
11571
11806
  if (!existing) {
11807
+ if (isScheduledTaskOwnedByExcluded(taskId, excludeBundles)) {
11808
+ continue;
11809
+ }
11572
11810
  throw new Error(
11573
11811
  `ScheduledTasksConfig.overrides references unknown taskId ${JSON.stringify(
11574
11812
  taskId
@@ -11599,8 +11837,8 @@ function resolveScheduledTasks(config) {
11599
11837
  tasks: [...merged.values()]
11600
11838
  };
11601
11839
  }
11602
- function validateScheduledTasksConfig(config) {
11603
- return resolveScheduledTasks(config);
11840
+ function validateScheduledTasksConfig(config, excludeBundles = []) {
11841
+ return resolveScheduledTasks(config, excludeBundles);
11604
11842
  }
11605
11843
  function renderScheduledTasksSection(resolved) {
11606
11844
  const lines = [
@@ -12083,7 +12321,7 @@ function classifyIssueScope(body, gate, labels = []) {
12083
12321
  matchedLabel: effective.matchedLabel
12084
12322
  };
12085
12323
  }
12086
- function renderScopeGateSection(gate) {
12324
+ function renderScopeGateSection(gate, excludeBundles = []) {
12087
12325
  const { acceptanceCriteria: ac, sources } = gate;
12088
12326
  const lines = [
12089
12327
  "## Scope gate",
@@ -12157,7 +12395,9 @@ function renderScopeGateSection(gate) {
12157
12395
  " is in place."
12158
12396
  );
12159
12397
  }
12160
- const overrideEntries = Object.entries(gate.bundleOverrides);
12398
+ const overrideEntries = Object.entries(gate.bundleOverrides).filter(
12399
+ ([label]) => !isPhaseLabelOwnedByExcluded(label, excludeBundles)
12400
+ );
12161
12401
  if (overrideEntries.length > 0) {
12162
12402
  lines.push(
12163
12403
  "",
@@ -12501,9 +12741,12 @@ function validateAgentTierConfig(config) {
12501
12741
  }
12502
12742
  return resolveAgentTiers(config);
12503
12743
  }
12504
- function renderAgentTierSection(tiers) {
12744
+ function renderAgentTierSection(tiers, excludeBundles = []) {
12745
+ const filtered = tiers.filter(
12746
+ (entry) => !isTypeLabelOwnedByExcluded(entry.type, excludeBundles)
12747
+ );
12505
12748
  const grouped = /* @__PURE__ */ new Map();
12506
- for (const entry of tiers) {
12749
+ for (const entry of filtered) {
12507
12750
  const bucket = grouped.get(entry.tier) ?? [];
12508
12751
  bucket.push(entry);
12509
12752
  grouped.set(entry.tier, bucket);
@@ -14234,13 +14477,13 @@ var ORCHESTRATOR_CONVENTIONS_PREAMBLE = [
14234
14477
  "",
14235
14478
  'Practical implication for scheduled-task wiring: the `worker-orchestrator` scheduled task\'s `SKILL.md` instructs the **scheduled-task session itself** to read `.claude/agents/orchestrator.md` and execute its phase pipeline in-session, then use the `Agent` tool to delegate the picked work item to `issue-worker` (depth-1). The scheduled task does **not** call `Agent(subagent_type: "orchestrator")` \u2014 that would put the orchestrator at depth-1 and break the chain.'
14236
14479
  ].join("\n");
14237
- function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio(), scheduledTasks = resolveScheduledTasks(), unblockDependents = resolveUnblockDependents()) {
14480
+ function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate(), runRatio = resolveRunRatio(), scheduledTasks = resolveScheduledTasks(), unblockDependents = resolveUnblockDependents(), excludeBundles = []) {
14238
14481
  return [
14239
14482
  ORCHESTRATOR_CONVENTIONS_PREAMBLE,
14240
14483
  "",
14241
- renderAgentTierSection(tiers),
14484
+ renderAgentTierSection(tiers, excludeBundles),
14242
14485
  "",
14243
- renderScopeGateSection(scopeGate),
14486
+ renderScopeGateSection(scopeGate, excludeBundles),
14244
14487
  "",
14245
14488
  renderRunRatioSection(runRatio),
14246
14489
  "",
@@ -14249,11 +14492,14 @@ function buildOrchestratorConventionsContent(tiers, scopeGate = resolveScopeGate
14249
14492
  renderUnblockDependentsSection(unblockDependents)
14250
14493
  ].join("\n");
14251
14494
  }
14252
- function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig, scheduledTasksConfig, unblockDependentsConfig) {
14495
+ function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig, scheduledTasksConfig, unblockDependentsConfig, excludeBundles = []) {
14253
14496
  const tiers = resolveAgentTiers(tierConfig);
14254
14497
  const scopeGate = resolveScopeGate(scopeGateConfig);
14255
14498
  const runRatio = resolveRunRatio(runRatioConfig);
14256
- const scheduledTasks = resolveScheduledTasks(scheduledTasksConfig);
14499
+ const scheduledTasks = resolveScheduledTasks(
14500
+ scheduledTasksConfig,
14501
+ excludeBundles
14502
+ );
14257
14503
  const unblockDependentsResolved = resolveUnblockDependents(
14258
14504
  unblockDependentsConfig
14259
14505
  );
@@ -14268,7 +14514,8 @@ function resolveOrchestratorAssets(tierConfig, scopeGateConfig, runRatioConfig,
14268
14514
  scopeGate,
14269
14515
  runRatio,
14270
14516
  scheduledTasks,
14271
- unblockDependentsResolved
14517
+ unblockDependentsResolved,
14518
+ excludeBundles
14272
14519
  ),
14273
14520
  procedure: buildCheckBlockedProcedure(tiers, scopeGate, runRatio),
14274
14521
  unblockDependentsProcedure: buildUnblockDependentsProcedure(
@@ -15261,6 +15508,7 @@ var PnpmWorkspace = class _PnpmWorkspace extends Component {
15261
15508
  this.minimumReleaseAgeExclude = options.minimumReleaseAgeExclude ? ["@codedrifters/*", ...options.minimumReleaseAgeExclude] : ["@codedrifters/*"];
15262
15509
  this.onlyBuiltDependencies = options.onlyBuiltDependencies ? options.onlyBuiltDependencies : [];
15263
15510
  this.ignoredBuiltDependencies = options.ignoredBuiltDependencies ? options.ignoredBuiltDependencies : [];
15511
+ this.allowBuilds = options.allowBuilds ?? {};
15264
15512
  this.subprojects = options.subprojects ?? [];
15265
15513
  this.defaultCatalog = options.defaultCatalog;
15266
15514
  this.namedCatalogs = options.namedCatalogs;
@@ -15284,11 +15532,35 @@ var PnpmWorkspace = class _PnpmWorkspace extends Component {
15284
15532
  if (this.minimumReleaseAgeExclude.length > 0) {
15285
15533
  pnpmConfig.minimumReleaseAgeExclude = this.minimumReleaseAgeExclude;
15286
15534
  }
15287
- if (this.onlyBuiltDependencies.length > 0) {
15288
- pnpmConfig.onlyBuiltDependencies = this.onlyBuiltDependencies;
15535
+ const mergedAllowBuilds = {};
15536
+ for (const pkg of this.onlyBuiltDependencies) {
15537
+ mergedAllowBuilds[pkg] = true;
15289
15538
  }
15290
- if (this.ignoredBuiltDependencies.length > 0) {
15291
- pnpmConfig.ignoreBuiltDependencies = this.ignoredBuiltDependencies;
15539
+ for (const pkg of this.ignoredBuiltDependencies) {
15540
+ mergedAllowBuilds[pkg] = false;
15541
+ }
15542
+ for (const [pkg, allowed] of Object.entries(this.allowBuilds)) {
15543
+ mergedAllowBuilds[pkg] = allowed;
15544
+ }
15545
+ const allowList = [];
15546
+ const denyList = [];
15547
+ for (const [pkg, allowed] of Object.entries(mergedAllowBuilds)) {
15548
+ if (allowed) {
15549
+ allowList.push(pkg);
15550
+ } else {
15551
+ denyList.push(pkg);
15552
+ }
15553
+ }
15554
+ allowList.sort();
15555
+ denyList.sort();
15556
+ if (allowList.length > 0) {
15557
+ pnpmConfig.onlyBuiltDependencies = allowList;
15558
+ }
15559
+ if (denyList.length > 0) {
15560
+ pnpmConfig.ignoredBuiltDependencies = denyList;
15561
+ }
15562
+ if (Object.keys(mergedAllowBuilds).length > 0) {
15563
+ pnpmConfig.allowBuilds = mergedAllowBuilds;
15292
15564
  }
15293
15565
  if (this.defaultCatalog && Object.keys(this.defaultCatalog).length > 0) {
15294
15566
  pnpmConfig.catalog = this.defaultCatalog;
@@ -26569,11 +26841,11 @@ var VERSION = {
26569
26841
  * Version of `pnpm/action-setup` to use in GitHub workflows.
26570
26842
  * Tracks the version projen currently emits (see node_modules/projen/lib/javascript/node-project.js).
26571
26843
  */
26572
- PNPM_ACTION_SETUP_VERSION: "v5",
26844
+ PNPM_ACTION_SETUP_VERSION: "v6.0.5",
26573
26845
  /**
26574
26846
  * Version of PNPM to use in workflows at github actions.
26575
26847
  */
26576
- PNPM_VERSION: "10.33.3",
26848
+ PNPM_VERSION: "11.0.8",
26577
26849
  /**
26578
26850
  * Version of Projen to use.
26579
26851
  */
@@ -28043,7 +28315,8 @@ var AgentConfig = class _AgentConfig extends Component8 {
28043
28315
  this.project.gitignore.addPatterns(`/${resolvedProgressFiles.stateDir}/`);
28044
28316
  }
28045
28317
  const resolvedScheduledTasks = validateScheduledTasksConfig(
28046
- this.options.scheduledTasks
28318
+ this.options.scheduledTasks,
28319
+ this.options.excludeBundles ?? []
28047
28320
  );
28048
28321
  if (resolvedScheduledTasks.enabled) {
28049
28322
  for (const task of resolvedScheduledTasks.tasks) {
@@ -28251,7 +28524,8 @@ ${section}`
28251
28524
  }
28252
28525
  }
28253
28526
  }
28254
- if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.scheduledTasks || this.options.unblockDependents) {
28527
+ const excludedBundleNames = this.options.excludeBundles ?? [];
28528
+ if (this.options.tiers || this.options.scopeGate || this.options.runRatio || this.options.scheduledTasks || this.options.unblockDependents || excludedBundleNames.length > 0) {
28255
28529
  const orchestratorRule = ruleMap.get("orchestrator-conventions");
28256
28530
  if (orchestratorRule) {
28257
28531
  const { conventionsContent } = resolveOrchestratorAssets(
@@ -28259,7 +28533,8 @@ ${section}`
28259
28533
  this.options.scopeGate,
28260
28534
  this.options.runRatio,
28261
28535
  this.options.scheduledTasks,
28262
- this.options.unblockDependents
28536
+ this.options.unblockDependents,
28537
+ excludedBundleNames
28263
28538
  );
28264
28539
  if (conventionsContent !== orchestratorRule.content) {
28265
28540
  ruleMap.set("orchestrator-conventions", {
@@ -28392,11 +28667,13 @@ ${hook}`
28392
28667
  const resolvedIssueTemplatesForRules = resolveIssueTemplates(
28393
28668
  this.options.issueTemplates
28394
28669
  );
28395
- if (this.options.issueTemplates) {
28670
+ const hasDownstreamBundles = hasAnyDownstreamIssueKindBundle(excludedBundleNames);
28671
+ if (this.options.issueTemplates || !hasDownstreamBundles) {
28396
28672
  const issueTemplatesRule = ruleMap.get("issue-templates-convention");
28397
28673
  if (issueTemplatesRule) {
28398
28674
  const issueTemplatesContent = renderIssueTemplatesRuleContent(
28399
- resolvedIssueTemplatesForRules
28675
+ resolvedIssueTemplatesForRules,
28676
+ hasDownstreamBundles
28400
28677
  );
28401
28678
  if (issueTemplatesContent !== issueTemplatesRule.content) {
28402
28679
  ruleMap.set("issue-templates-convention", {
@@ -28406,7 +28683,10 @@ ${hook}`
28406
28683
  }
28407
28684
  }
28408
28685
  }
28409
- if (resolvedIssueTemplatesForRules.enabled) {
28686
+ if (!hasAnyDocsEmittingBundle(excludedBundleNames)) {
28687
+ ruleMap.delete("stub-index-convention");
28688
+ }
28689
+ if (resolvedIssueTemplatesForRules.enabled && hasDownstreamBundles) {
28410
28690
  for (const [ruleName, label] of ISSUE_TEMPLATES_BUNDLE_HOOKS) {
28411
28691
  const existing = ruleMap.get(ruleName);
28412
28692
  if (!existing) {
@@ -30923,9 +31203,24 @@ import {
30923
31203
  } from "projen/lib/typescript";
30924
31204
  import { merge } from "ts-deepmerge";
30925
31205
 
30926
- // src/tasks/reset-task.ts
31206
+ // src/projects/nvmrc.ts
30927
31207
  import { Component as Component14 } from "projen";
30928
- var ResetTask = class _ResetTask extends Component14 {
31208
+ import { TextFile as TextFile6 } from "projen/lib/textfile";
31209
+ var Nvmrc = class extends Component14 {
31210
+ constructor(project) {
31211
+ super(project);
31212
+ new TextFile6(project, ".nvmrc", {
31213
+ // `lines` is joined with `\n` — adding an empty trailing entry
31214
+ // produces a POSIX-friendly trailing newline (`24\n`).
31215
+ lines: [VERSION.NODE_WORKFLOWS, ""],
31216
+ readonly: true
31217
+ });
31218
+ }
31219
+ };
31220
+
31221
+ // src/tasks/reset-task.ts
31222
+ import { Component as Component15 } from "projen";
31223
+ var ResetTask = class _ResetTask extends Component15 {
30929
31224
  constructor(project, options = {}) {
30930
31225
  super(project);
30931
31226
  this.project = project;
@@ -30998,8 +31293,8 @@ var ResetTask = class _ResetTask extends Component14 {
30998
31293
  };
30999
31294
 
31000
31295
  // src/vscode/vscode.ts
31001
- import { Component as Component15, vscode } from "projen";
31002
- var VSCodeConfig = class extends Component15 {
31296
+ import { Component as Component16, vscode } from "projen";
31297
+ var VSCodeConfig = class extends Component16 {
31003
31298
  constructor(project) {
31004
31299
  super(project);
31005
31300
  const vsConfig = new vscode.VsCode(project);
@@ -31144,8 +31439,146 @@ function addBuildCompleteJob(buildWorkflow) {
31144
31439
  });
31145
31440
  }
31146
31441
 
31442
+ // src/workflows/pin-pnpm-action-setup.ts
31443
+ import { GitHub as GitHub2 } from "projen/lib/github";
31444
+ var PNPM_ACTION_SETUP_PREFIX = "pnpm/action-setup@";
31445
+ var STEP_ARRAY_FIELDS = ["preBuildSteps", "postBuildSteps"];
31446
+ function pinPnpmActionSetup(project) {
31447
+ const pinned = `${PNPM_ACTION_SETUP_PREFIX}${VERSION.PNPM_ACTION_SETUP_VERSION}`;
31448
+ patchComponentStepArrays(project, pinned);
31449
+ for (const sub of project.subprojects) {
31450
+ patchComponentStepArrays(sub, pinned);
31451
+ }
31452
+ const github = GitHub2.of(project);
31453
+ if (!github) {
31454
+ return;
31455
+ }
31456
+ for (const workflow of github.workflows) {
31457
+ for (const job of Object.values(workflow.jobs)) {
31458
+ patchStepArray(getEagerSteps(job), pinned);
31459
+ }
31460
+ }
31461
+ }
31462
+ function patchComponentStepArrays(project, pinned) {
31463
+ for (const component of project.components) {
31464
+ for (const field of STEP_ARRAY_FIELDS) {
31465
+ const steps = readStepArray(component, field);
31466
+ if (steps !== void 0) {
31467
+ patchStepArray(steps, pinned);
31468
+ }
31469
+ }
31470
+ }
31471
+ }
31472
+ function readStepArray(component, field) {
31473
+ const value = component[field];
31474
+ return Array.isArray(value) ? value : void 0;
31475
+ }
31476
+ function getEagerSteps(job) {
31477
+ if (typeof job !== "object" || job === null) {
31478
+ return void 0;
31479
+ }
31480
+ const steps = job.steps;
31481
+ return Array.isArray(steps) ? steps : void 0;
31482
+ }
31483
+ function patchStepArray(steps, pinned) {
31484
+ if (!steps) {
31485
+ return;
31486
+ }
31487
+ for (const step of steps) {
31488
+ if (typeof step.uses === "string" && step.uses.startsWith(PNPM_ACTION_SETUP_PREFIX)) {
31489
+ step.uses = pinned;
31490
+ }
31491
+ }
31492
+ }
31493
+
31494
+ // src/workflows/pin-setup-node-version.ts
31495
+ import { GitHub as GitHub3 } from "projen/lib/github";
31496
+ var SETUP_NODE_PREFIX = "actions/setup-node@";
31497
+ var STEP_ARRAY_FIELDS2 = ["preBuildSteps", "postBuildSteps"];
31498
+ var NODE_VERSION_FIELDS = ["workflowNodeVersion"];
31499
+ function pinSetupNodeVersion(project) {
31500
+ const pinned = VERSION.NODE_WORKFLOWS;
31501
+ patchComponentNodeVersionFields(project, pinned);
31502
+ patchComponentStepArrays2(project, pinned);
31503
+ for (const sub of project.subprojects) {
31504
+ patchComponentNodeVersionFields(sub, pinned);
31505
+ patchComponentStepArrays2(sub, pinned);
31506
+ }
31507
+ const github = GitHub3.of(project);
31508
+ if (!github) {
31509
+ return;
31510
+ }
31511
+ for (const workflow of github.workflows) {
31512
+ for (const job of Object.values(workflow.jobs)) {
31513
+ patchJobToolsNode(job, pinned);
31514
+ patchStepArray2(getEagerSteps2(job), pinned);
31515
+ }
31516
+ }
31517
+ }
31518
+ function patchComponentNodeVersionFields(project, pinned) {
31519
+ for (const component of project.components) {
31520
+ for (const field of NODE_VERSION_FIELDS) {
31521
+ const record = component;
31522
+ const value = record[field];
31523
+ if (typeof value === "string") {
31524
+ record[field] = pinned;
31525
+ }
31526
+ }
31527
+ }
31528
+ }
31529
+ function patchComponentStepArrays2(project, pinned) {
31530
+ for (const component of project.components) {
31531
+ for (const field of STEP_ARRAY_FIELDS2) {
31532
+ const steps = readStepArray2(component, field);
31533
+ if (steps !== void 0) {
31534
+ patchStepArray2(steps, pinned);
31535
+ }
31536
+ }
31537
+ }
31538
+ }
31539
+ function readStepArray2(component, field) {
31540
+ const value = component[field];
31541
+ return Array.isArray(value) ? value : void 0;
31542
+ }
31543
+ function getEagerSteps2(job) {
31544
+ if (typeof job !== "object" || job === null) {
31545
+ return void 0;
31546
+ }
31547
+ const steps = job.steps;
31548
+ return Array.isArray(steps) ? steps : void 0;
31549
+ }
31550
+ function patchJobToolsNode(job, pinned) {
31551
+ if (typeof job !== "object" || job === null) {
31552
+ return;
31553
+ }
31554
+ const tools = job.tools;
31555
+ if (typeof tools !== "object" || tools === null) {
31556
+ return;
31557
+ }
31558
+ const node = tools.node;
31559
+ if (typeof node !== "object" || node === null) {
31560
+ return;
31561
+ }
31562
+ if (typeof node.version === "string") {
31563
+ node.version = pinned;
31564
+ }
31565
+ }
31566
+ function patchStepArray2(steps, pinned) {
31567
+ if (!steps) {
31568
+ return;
31569
+ }
31570
+ for (const step of steps) {
31571
+ if (typeof step.uses === "string" && step.uses.startsWith(SETUP_NODE_PREFIX)) {
31572
+ if (!step.with || typeof step.with !== "object") {
31573
+ step.with = {};
31574
+ }
31575
+ step.with["node-version"] = pinned;
31576
+ }
31577
+ }
31578
+ }
31579
+
31147
31580
  // src/workflows/sync-labels.ts
31148
- import { Component as Component16, YamlFile as YamlFile2 } from "projen";
31581
+ import { Component as Component17, YamlFile as YamlFile2 } from "projen";
31149
31582
  import { JobPermission as JobPermission4 } from "projen/lib/github/workflows-model";
31150
31583
  var DEFAULT_STATUS_LABELS = [
31151
31584
  {
@@ -31353,7 +31786,7 @@ ${offenders}`
31353
31786
  }
31354
31787
  });
31355
31788
  }
31356
- var LabelsFile = class extends Component16 {
31789
+ var LabelsFile = class extends Component17 {
31357
31790
  constructor(project, labels) {
31358
31791
  super(project);
31359
31792
  new YamlFile2(project, LABELS_CONFIG_PATH, {
@@ -31574,6 +32007,7 @@ var MonorepoProject = class extends TypeScriptAppProject {
31574
32007
  this.configulatorRegistryConsumer = options.configulatorRegistryConsumer ?? true;
31575
32008
  this.layoutEnforcement = options.layoutEnforcement ?? LAYOUT_ENFORCEMENT.WARN;
31576
32009
  new VSCodeConfig(this);
32010
+ new Nvmrc(this);
31577
32011
  new PnpmWorkspace(this, options.pnpmOptions?.pnpmWorkspaceOptions);
31578
32012
  if (options.turbo) {
31579
32013
  new TurboRepo(this, options.turboOptions);
@@ -31692,6 +32126,8 @@ var MonorepoProject = class extends TypeScriptAppProject {
31692
32126
  */
31693
32127
  preSynthesize() {
31694
32128
  super.preSynthesize();
32129
+ pinPnpmActionSetup(this);
32130
+ pinSetupNodeVersion(this);
31695
32131
  if (this.layoutEnforcement === LAYOUT_ENFORCEMENT.OFF) {
31696
32132
  return;
31697
32133
  }
@@ -32095,12 +32531,12 @@ import { merge as merge4 } from "ts-deepmerge";
32095
32531
 
32096
32532
  // src/workflows/aws-deploy-workflow.ts
32097
32533
  var import_utils11 = __toESM(require_lib());
32098
- import { Component as Component17 } from "projen";
32534
+ import { Component as Component18 } from "projen";
32099
32535
  import { BuildWorkflow } from "projen/lib/build";
32100
- import { GitHub as GitHub2, WorkflowSteps as WorkflowSteps2 } from "projen/lib/github";
32536
+ import { GitHub as GitHub4, WorkflowSteps as WorkflowSteps2 } from "projen/lib/github";
32101
32537
  import { JobPermission as JobPermission5 } from "projen/lib/github/workflows-model";
32102
32538
  var PROD_DEPLOY_NAME = "prod-deploy";
32103
- var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component17 {
32539
+ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component18 {
32104
32540
  constructor(project, options = {}) {
32105
32541
  super(project);
32106
32542
  this.project = project;
@@ -32213,7 +32649,7 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component17 {
32213
32649
  );
32214
32650
  }
32215
32651
  this.rootProject = project.root;
32216
- const github = GitHub2.of(this.rootProject);
32652
+ const github = GitHub4.of(this.rootProject);
32217
32653
  if (!github) {
32218
32654
  throw new Error(
32219
32655
  "AwsDeployWorkflow requires a GitHub component in the root project"
@@ -32364,8 +32800,8 @@ var AwsDeployWorkflow = class _AwsDeployWorkflow extends Component17 {
32364
32800
  };
32365
32801
 
32366
32802
  // src/workflows/aws-teardown-workflow.ts
32367
- import { Component as Component18 } from "projen";
32368
- import { GitHub as GitHub3, GithubWorkflow } from "projen/lib/github";
32803
+ import { Component as Component19 } from "projen";
32804
+ import { GitHub as GitHub5, GithubWorkflow } from "projen/lib/github";
32369
32805
  import { JobPermission as JobPermission6 } from "projen/lib/github/workflows-model";
32370
32806
  var DEFAULT_TEARDOWN_BRANCH_PATTERNS = [
32371
32807
  "feat/*",
@@ -32386,7 +32822,7 @@ var resolveBranchPatterns = (explicit, targets) => {
32386
32822
  }
32387
32823
  return [...DEFAULT_TEARDOWN_BRANCH_PATTERNS];
32388
32824
  };
32389
- var AwsTeardownWorkflow = class extends Component18 {
32825
+ var AwsTeardownWorkflow = class extends Component19 {
32390
32826
  constructor(rootProject, options) {
32391
32827
  super(rootProject);
32392
32828
  this.rootProject = rootProject;
@@ -32415,7 +32851,7 @@ var AwsTeardownWorkflow = class extends Component18 {
32415
32851
  "AwsTeardownWorkflow requires the root project to be a MonorepoProject"
32416
32852
  );
32417
32853
  }
32418
- const github = GitHub3.of(this.rootProject);
32854
+ const github = GitHub5.of(this.rootProject);
32419
32855
  if (!github) {
32420
32856
  throw new Error(
32421
32857
  "AwsTeardownWorkflow requires a GitHub component in the root project"
@@ -32860,9 +33296,9 @@ export const collections = {
32860
33296
 
32861
33297
  // src/typescript/typescript-config.ts
32862
33298
  import { relative as relative7 } from "path";
32863
- import { Component as Component19 } from "projen";
33299
+ import { Component as Component20 } from "projen";
32864
33300
  import { ensureRelativePathStartsWithDot } from "projen/lib/util/path";
32865
- var TypeScriptConfig = class extends Component19 {
33301
+ var TypeScriptConfig = class extends Component20 {
32866
33302
  constructor(project) {
32867
33303
  super(project);
32868
33304
  let tsPaths = {};
@@ -32908,6 +33344,7 @@ export {
32908
33344
  AwsDeploymentTarget,
32909
33345
  AwsTeardownWorkflow,
32910
33346
  BUILT_IN_BUNDLES,
33347
+ BUNDLE_OWNERSHIP,
32911
33348
  CLAUDE_RULE_TARGET,
32912
33349
  COMPLETE_JOB_ID,
32913
33350
  DEFAULT_AC_THRESHOLDS,
@@ -32972,6 +33409,7 @@ export {
32972
33409
  MINIMUM_RELEASE_AGE,
32973
33410
  MONOREPO_LAYOUT,
32974
33411
  MonorepoProject,
33412
+ Nvmrc,
32975
33413
  PROD_DEPLOY_NAME,
32976
33414
  PROGRESS_FILES_FORMAT_VALUES,
32977
33415
  PnpmWorkspace,
@@ -33051,7 +33489,12 @@ export {
33051
33489
  formatStarlightSingletonViolation,
33052
33490
  getLatestEligibleVersion,
33053
33491
  githubWorkflowBundle,
33492
+ hasAnyDocsEmittingBundle,
33493
+ hasAnyDownstreamIssueKindBundle,
33054
33494
  industryDiscoveryBundle,
33495
+ isPhaseLabelOwnedByExcluded,
33496
+ isScheduledTaskOwnedByExcluded,
33497
+ isTypeLabelOwnedByExcluded,
33055
33498
  jestBundle,
33056
33499
  labelsForPhase,
33057
33500
  maintenanceAuditBundle,
@@ -33060,6 +33503,8 @@ export {
33060
33503
  parseApiRollup,
33061
33504
  peopleProfileBundle,
33062
33505
  persistAuditReport,
33506
+ pinPnpmActionSetup,
33507
+ pinSetupNodeVersion,
33063
33508
  pnpmBundle,
33064
33509
  prReviewBundle,
33065
33510
  projenBundle,