/** * Flat-monorepo discovery strategy — Phase 12 of opensip's substrate * consolidation (opensip DEC-498). * * Phase 0 Q4 audit determined that a flat monorepo of >2500 .ts files * cannot run on the engine in single-process mode: * - `heap-preflight.ts` caps elevation at 12 GB (`HEAP_TARGETS` * fileThreshold=2500 → heapMb=12288). * - `--packages` fan-out (see `packages-runner.ts`) depends on * workspace boundaries — `discoverWorkspacePackages` walks * `/packages/**` looking for `tsconfig.json`. A flat directory * has no such boundaries; the strategy is structurally inapplicable. * * This module ships the partition strategy. It is intentionally * subprocess-free: callers supply file lists (real or synthetic for * tests), and the partition primitives are pure. Wiring to * `runPackagesInParallel` lives in `graph.ts` — at the CLI dispatch * layer, where the subprocess shape (cliScript, displayPath rendering) * already exists. * * Cross-partition fidelity: cross-package call sites are NO LONGER * dropped. Plan #2 (sharded build) recovers them in the cross-shard * boundary pass — each partition becomes a `Shard`, and the inter-shard * edges are resolved against the merged catalog and tagged * `CallEdge.crossShard: true` (the `partition_boundary` follow-up this * comment once anticipated). Intra-partition edges keep full fidelity. */ import type { SyntheticPartition } from './partition-chunk.js'; import type { PartitionStrategy } from '../../types.js'; export type { PartitionStrategy } from '../../types.js'; export type { SyntheticPartition } from './partition-chunk.js'; /** * Layout classification — three buckets that drive the strategy choice * in `selectStrategyForLayout`. * * - **workspaces:** at least one nested `package.json` OR root * `package.json` declares `workspaces`. Existing `--packages` fan-out * handles it. * - **flat-small:** no workspace structure, file count ≤ threshold. * Single-process mode + heap-preflight elevation handles it. * - **flat-large:** no workspace structure, file count > threshold. * Must synthetically partition; no single-process mode can hold the * `ts.Program` in 12 GB. */ export type MonorepoLayout = { readonly kind: 'workspaces'; readonly packageDirs: readonly string[]; } | { readonly kind: 'flat-small'; readonly files: readonly string[]; } | { readonly kind: 'flat-large'; readonly files: readonly string[]; }; export interface DetectMonorepoLayoutInput { readonly repoRoot: string; /** * Source-file count threshold above which a no-workspace layout is * classified `flat-large`. Default 2500 (matches `heap-preflight.ts` * top elevation tier). */ readonly heapElevationThreshold?: number; /** * Optional injection for tests — pre-computed file list. When set, the * walker is skipped entirely. Production callers leave undefined. */ readonly files?: readonly string[]; /** * Optional injection for tests — pre-computed nested package.json * directory list. When set, filesystem walking for workspace * detection is skipped. */ readonly nestedPackageDirs?: readonly string[]; /** * Optional injection for tests — root package.json contents. When set, * the on-disk root package.json read is skipped. */ readonly rootPackageJson?: { readonly workspaces?: unknown; } | null; } /** * Classify a repository's layout. Walks `` for nested * `package.json` files (one and two levels deep — `/*` and * `/packages/*` — matching the conventions * `discoverWorkspacePackages` already supports) and the root * `package.json` for `workspaces`. * * - Multiple nested package.json OR root `workspaces` → `'workspaces'`. * - No workspace structure + .ts(x)/.js(x) count ≤ threshold → * `'flat-small'`. * - No workspace structure + count > threshold → `'flat-large'`. * * Test injection: callers may supply `files`, `nestedPackageDirs`, or * `rootPackageJson` to skip the filesystem walk. Production callers * leave these undefined. */ export declare function detectMonorepoLayout(input: DetectMonorepoLayoutInput): MonorepoLayout; export interface PartitionFlatRepoInput { readonly files: readonly string[]; readonly repoRoot: string; readonly strategy: PartitionStrategy; /** Directory depth for `'directory-depth'`. Default 2. */ readonly depth?: number; /** Chunk size for `'file-count-chunks'` and `'hybrid'`. Default 2000. */ readonly chunkSize?: number; } /** * Partition a flat file list into synthetic packages. * * Strategies: * * - **`directory-depth`** (default depth=2): bucket files by their * first N path segments under `repoRoot`. Partition IDs join the * segments with `.` (e.g., `src/api/foo.ts` at depth=2 → partition * `src.api`). Edge cases: * - Files shallower than `depth` (e.g., `src/foo.ts` at depth=2) * bucket under their actual depth — partition `src` here. * - Files directly at `repoRoot` (e.g., `foo.ts`) bucket under * `_root`. (Underscore prefix avoids collision with a real * top-level directory named `root`.) * - Files outside `repoRoot` (`..` segments) bucket under `_external` * — should be rare; usually a sign of a malformed input. * * - **`file-count-chunks`**: sort alphabetically (stable), split into * chunks of `chunkSize`. Partition IDs `chunk-0`, `chunk-1`, … * Worst semantic quality (no directory coherence) but works on any * layout — used as the fallback inside `hybrid` and as a flag-driven * override. * * - **`hybrid`** (recommended default for `flat-large`): apply * `directory-depth` first; if any single partition exceeds * `chunkSize`, sub-partition that partition using `file-count-chunks`. * Sub-partition IDs concatenate: `.chunk-N`. Preserves * directory coherence where the structure helps, falls back to * chunking where one directory dominates. * * Sorted output: partition IDs are returned in stable lexicographic * order; files within each partition are sorted lexicographically. * Determinism matters for cache keys and reproducible runs. * * @throws {Error} When `input.chunkSize` is non-positive. */ export declare function partitionFlatRepo(input: PartitionFlatRepoInput): readonly SyntheticPartition[]; export interface StrategySelection { readonly mode: 'single-process' | 'packages-fanout' | 'synthetic-partition'; readonly partitionStrategy?: PartitionStrategy; } /** * Choose the orchestration mode for a detected layout. * * - `workspaces` → `packages-fanout` (existing path; `graph.ts` * delegates to `runPackagesInParallel`). * - `flat-small` → `single-process` (current default; heap-preflight * handles elevation if needed). * - `flat-large` → `synthetic-partition` with `hybrid` strategy * (recommended default). * * Note (2026-06): A "community" (Louvain) partitionStrategy was prototyped * and measured under ADR-0045's numeric gate, and discarded by measurement: * real-corpus boundary-call reduction was −4.7% (gate required ≥25%) and * warm wall-time regressed 1.94× on the synthetic fixture (the import scan * + Louvain ran on every warm build). Equivalence floors stayed at 0 as * scoped — accuracy was NOT the failure. See ADR-0045's Outcome for the * full matrix; the prototype is recoverable at tag * `prototype/louvain-partitioning`. The `partitionStrategy` config knob and * `StrategySelection` type are retained for future experiments, but only * `hybrid` is currently supported in production paths. */ export declare function selectStrategyForLayout(layout: MonorepoLayout): StrategySelection; //# sourceMappingURL=flat-monorepo-strategy.d.ts.map