@harness-engineering/core 0.10.0 → 0.11.0

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/dist/index.js CHANGED
@@ -34,33 +34,58 @@ __export(index_exports, {
34
34
  AGENT_DESCRIPTORS: () => AGENT_DESCRIPTORS,
35
35
  ARCHITECTURE_DESCRIPTOR: () => ARCHITECTURE_DESCRIPTOR,
36
36
  AgentActionEmitter: () => AgentActionEmitter,
37
+ ArchBaselineManager: () => ArchBaselineManager,
38
+ ArchBaselineSchema: () => ArchBaselineSchema,
39
+ ArchConfigSchema: () => ArchConfigSchema,
40
+ ArchDiffResultSchema: () => ArchDiffResultSchema,
41
+ ArchMetricCategorySchema: () => ArchMetricCategorySchema,
37
42
  BUG_DETECTION_DESCRIPTOR: () => BUG_DETECTION_DESCRIPTOR,
38
43
  BaselineManager: () => BaselineManager,
39
44
  BenchmarkRunner: () => BenchmarkRunner,
45
+ BlueprintGenerator: () => BlueprintGenerator,
46
+ BundleConstraintsSchema: () => BundleConstraintsSchema,
47
+ BundleSchema: () => BundleSchema,
40
48
  COMPLIANCE_DESCRIPTOR: () => COMPLIANCE_DESCRIPTOR,
49
+ CategoryBaselineSchema: () => CategoryBaselineSchema,
50
+ CategoryRegressionSchema: () => CategoryRegressionSchema,
41
51
  ChecklistBuilder: () => ChecklistBuilder,
52
+ CircularDepsCollector: () => CircularDepsCollector,
53
+ ComplexityCollector: () => ComplexityCollector,
42
54
  ConfirmationSchema: () => ConfirmationSchema,
43
55
  ConsoleSink: () => ConsoleSink,
56
+ ConstraintRuleSchema: () => ConstraintRuleSchema,
57
+ ContentPipeline: () => ContentPipeline,
58
+ ContributionsSchema: () => ContributionsSchema,
59
+ CouplingCollector: () => CouplingCollector,
44
60
  CriticalPathResolver: () => CriticalPathResolver,
45
61
  DEFAULT_PROVIDER_TIERS: () => DEFAULT_PROVIDER_TIERS,
46
62
  DEFAULT_SECURITY_CONFIG: () => DEFAULT_SECURITY_CONFIG,
47
63
  DEFAULT_STATE: () => DEFAULT_STATE,
48
64
  DEFAULT_STREAM_INDEX: () => DEFAULT_STREAM_INDEX,
65
+ DepDepthCollector: () => DepDepthCollector,
49
66
  EmitInteractionInputSchema: () => EmitInteractionInputSchema,
50
67
  EntropyAnalyzer: () => EntropyAnalyzer,
51
68
  EntropyConfigSchema: () => EntropyConfigSchema,
52
69
  ExclusionSet: () => ExclusionSet,
53
70
  FailureEntrySchema: () => FailureEntrySchema,
54
71
  FileSink: () => FileSink,
72
+ ForbiddenImportCollector: () => ForbiddenImportCollector,
55
73
  GateConfigSchema: () => GateConfigSchema,
56
74
  GateResultSchema: () => GateResultSchema,
57
75
  HandoffSchema: () => HandoffSchema,
58
76
  HarnessStateSchema: () => HarnessStateSchema,
59
77
  InteractionTypeSchema: () => InteractionTypeSchema,
78
+ LayerViolationCollector: () => LayerViolationCollector,
79
+ LockfilePackageSchema: () => LockfilePackageSchema,
80
+ LockfileSchema: () => LockfileSchema,
81
+ ManifestSchema: () => ManifestSchema,
82
+ MetricResultSchema: () => MetricResultSchema,
83
+ ModuleSizeCollector: () => ModuleSizeCollector,
60
84
  NoOpExecutor: () => NoOpExecutor,
61
85
  NoOpSink: () => NoOpSink,
62
86
  NoOpTelemetryAdapter: () => NoOpTelemetryAdapter,
63
87
  PatternConfigSchema: () => PatternConfigSchema,
88
+ ProjectScanner: () => ProjectScanner,
64
89
  QuestionSchema: () => QuestionSchema,
65
90
  REQUIRED_SECTIONS: () => REQUIRED_SECTIONS,
66
91
  RegressionDetector: () => RegressionDetector,
@@ -68,16 +93,26 @@ __export(index_exports, {
68
93
  SECURITY_DESCRIPTOR: () => SECURITY_DESCRIPTOR,
69
94
  SecurityConfigSchema: () => SecurityConfigSchema,
70
95
  SecurityScanner: () => SecurityScanner,
96
+ SharableBoundaryConfigSchema: () => SharableBoundaryConfigSchema,
97
+ SharableForbiddenImportSchema: () => SharableForbiddenImportSchema,
98
+ SharableLayerSchema: () => SharableLayerSchema,
99
+ SharableSecurityRulesSchema: () => SharableSecurityRulesSchema,
71
100
  StreamIndexSchema: () => StreamIndexSchema,
72
101
  StreamInfoSchema: () => StreamInfoSchema,
102
+ ThresholdConfigSchema: () => ThresholdConfigSchema,
73
103
  TransitionSchema: () => TransitionSchema,
74
104
  TypeScriptParser: () => TypeScriptParser,
75
105
  VERSION: () => VERSION,
106
+ ViolationSchema: () => ViolationSchema,
107
+ addProvenance: () => addProvenance,
76
108
  analyzeDiff: () => analyzeDiff,
77
109
  appendFailure: () => appendFailure,
78
110
  appendLearning: () => appendLearning,
79
111
  applyFixes: () => applyFixes,
80
112
  applyHotspotDowngrade: () => applyHotspotDowngrade,
113
+ archMatchers: () => archMatchers,
114
+ archModule: () => archModule,
115
+ architecture: () => architecture,
81
116
  archiveFailures: () => archiveFailures,
82
117
  archiveStream: () => archiveStream,
83
118
  buildDependencyGraph: () => buildDependencyGraph,
@@ -87,6 +122,7 @@ __export(index_exports, {
87
122
  checkEligibility: () => checkEligibility,
88
123
  classifyFinding: () => classifyFinding,
89
124
  configureFeedback: () => configureFeedback,
125
+ constraintRuleId: () => constraintRuleId,
90
126
  contextBudget: () => contextBudget,
91
127
  contextFilter: () => contextFilter,
92
128
  createBoundaryValidator: () => createBoundaryValidator,
@@ -101,6 +137,8 @@ __export(index_exports, {
101
137
  cryptoRules: () => cryptoRules,
102
138
  deduplicateCleanupFindings: () => deduplicateCleanupFindings,
103
139
  deduplicateFindings: () => deduplicateFindings,
140
+ deepMergeConstraints: () => deepMergeConstraints,
141
+ defaultCollectors: () => defaultCollectors,
104
142
  defineLayer: () => defineLayer,
105
143
  deserializationRules: () => deserializationRules,
106
144
  detectChangeType: () => detectChangeType,
@@ -113,9 +151,12 @@ __export(index_exports, {
113
151
  detectPatternViolations: () => detectPatternViolations,
114
152
  detectSizeBudgetViolations: () => detectSizeBudgetViolations,
115
153
  detectStack: () => detectStack,
154
+ detectStaleConstraints: () => detectStaleConstraints,
116
155
  determineAssessment: () => determineAssessment,
156
+ diff: () => diff,
117
157
  executeWorkflow: () => executeWorkflow,
118
158
  expressRules: () => expressRules,
159
+ extractBundle: () => extractBundle,
119
160
  extractMarkdownLinks: () => extractMarkdownLinks,
120
161
  extractSections: () => extractSections,
121
162
  fanOutReview: () => fanOutReview,
@@ -146,6 +187,7 @@ __export(index_exports, {
146
187
  networkRules: () => networkRules,
147
188
  nodeRules: () => nodeRules,
148
189
  parseDiff: () => parseDiff,
190
+ parseManifest: () => parseManifest,
149
191
  parseRoadmap: () => parseRoadmap,
150
192
  parseSecurityConfig: () => parseSecurityConfig,
151
193
  parseSize: () => parseSize,
@@ -153,6 +195,8 @@ __export(index_exports, {
153
195
  previewFix: () => previewFix,
154
196
  reactRules: () => reactRules,
155
197
  readCheckState: () => readCheckState,
198
+ readLockfile: () => readLockfile,
199
+ removeProvenance: () => removeProvenance,
156
200
  requestMultiplePeerReviews: () => requestMultiplePeerReviews,
157
201
  requestPeerReview: () => requestPeerReview,
158
202
  resetFeedbackConfig: () => resetFeedbackConfig,
@@ -160,6 +204,8 @@ __export(index_exports, {
160
204
  resolveModelTier: () => resolveModelTier,
161
205
  resolveRuleSeverity: () => resolveRuleSeverity,
162
206
  resolveStreamPath: () => resolveStreamPath,
207
+ resolveThresholds: () => resolveThresholds,
208
+ runAll: () => runAll,
163
209
  runArchitectureAgent: () => runArchitectureAgent,
164
210
  runBugDetectionAgent: () => runBugDetectionAgent,
165
211
  runCIChecks: () => runCIChecks,
@@ -179,6 +225,7 @@ __export(index_exports, {
179
225
  setActiveStream: () => setActiveStream,
180
226
  shouldRunCheck: () => shouldRunCheck,
181
227
  spawnBackgroundCheck: () => spawnBackgroundCheck,
228
+ syncConstraintNodes: () => syncConstraintNodes,
182
229
  syncRoadmap: () => syncRoadmap,
183
230
  touchStream: () => touchStream,
184
231
  trackAction: () => trackAction,
@@ -191,6 +238,9 @@ __export(index_exports, {
191
238
  validateFindings: () => validateFindings,
192
239
  validateKnowledgeMap: () => validateKnowledgeMap,
193
240
  validatePatternConfig: () => validatePatternConfig,
241
+ violationId: () => violationId,
242
+ writeConfig: () => writeConfig,
243
+ writeLockfile: () => writeLockfile,
194
244
  xssRules: () => xssRules
195
245
  });
196
246
  module.exports = __toCommonJS(index_exports);
@@ -213,17 +263,17 @@ var import_util = require("util");
213
263
  var import_glob = require("glob");
214
264
  var accessAsync = (0, import_util.promisify)(import_fs.access);
215
265
  var readFileAsync = (0, import_util.promisify)(import_fs.readFile);
216
- async function fileExists(path11) {
266
+ async function fileExists(path13) {
217
267
  try {
218
- await accessAsync(path11, import_fs.constants.F_OK);
268
+ await accessAsync(path13, import_fs.constants.F_OK);
219
269
  return true;
220
270
  } catch {
221
271
  return false;
222
272
  }
223
273
  }
224
- async function readFileContent(path11) {
274
+ async function readFileContent(path13) {
225
275
  try {
226
- const content = await readFileAsync(path11, "utf-8");
276
+ const content = await readFileAsync(path13, "utf-8");
227
277
  return (0, import_types.Ok)(content);
228
278
  } catch (error) {
229
279
  return (0, import_types.Err)(error);
@@ -271,15 +321,15 @@ function validateConfig(data, schema) {
271
321
  let message = "Configuration validation failed";
272
322
  const suggestions = [];
273
323
  if (firstError) {
274
- const path11 = firstError.path.join(".");
275
- const pathDisplay = path11 ? ` at "${path11}"` : "";
324
+ const path13 = firstError.path.join(".");
325
+ const pathDisplay = path13 ? ` at "${path13}"` : "";
276
326
  if (firstError.code === "invalid_type") {
277
327
  const received = firstError.received;
278
328
  const expected = firstError.expected;
279
329
  if (received === "undefined") {
280
330
  code = "MISSING_FIELD";
281
331
  message = `Missing required field${pathDisplay}: ${firstError.message}`;
282
- suggestions.push(`Field "${path11}" is required and must be of type "${expected}"`);
332
+ suggestions.push(`Field "${path13}" is required and must be of type "${expected}"`);
283
333
  } else {
284
334
  code = "INVALID_TYPE";
285
335
  message = `Invalid type${pathDisplay}: ${firstError.message}`;
@@ -492,30 +542,30 @@ function extractSections(content) {
492
542
  return result;
493
543
  });
494
544
  }
495
- function isExternalLink(path11) {
496
- return path11.startsWith("http://") || path11.startsWith("https://") || path11.startsWith("#") || path11.startsWith("mailto:");
545
+ function isExternalLink(path13) {
546
+ return path13.startsWith("http://") || path13.startsWith("https://") || path13.startsWith("#") || path13.startsWith("mailto:");
497
547
  }
498
548
  function resolveLinkPath(linkPath, baseDir) {
499
549
  return linkPath.startsWith(".") ? (0, import_path.join)(baseDir, linkPath) : linkPath;
500
550
  }
501
- async function validateAgentsMap(path11 = "./AGENTS.md") {
551
+ async function validateAgentsMap(path13 = "./AGENTS.md") {
502
552
  console.warn(
503
553
  "[harness] validateAgentsMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
504
554
  );
505
- const contentResult = await readFileContent(path11);
555
+ const contentResult = await readFileContent(path13);
506
556
  if (!contentResult.ok) {
507
557
  return (0, import_types.Err)(
508
558
  createError(
509
559
  "PARSE_ERROR",
510
560
  `Failed to read AGENTS.md: ${contentResult.error.message}`,
511
- { path: path11 },
561
+ { path: path13 },
512
562
  ["Ensure the file exists", "Check file permissions"]
513
563
  )
514
564
  );
515
565
  }
516
566
  const content = contentResult.value;
517
567
  const sections = extractSections(content);
518
- const baseDir = (0, import_path.dirname)(path11);
568
+ const baseDir = (0, import_path.dirname)(path13);
519
569
  const sectionTitles = sections.map((s) => s.title);
520
570
  const missingSections = REQUIRED_SECTIONS.filter(
521
571
  (required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
@@ -555,6 +605,7 @@ async function validateAgentsMap(path11 = "./AGENTS.md") {
555
605
  }
556
606
 
557
607
  // src/context/doc-coverage.ts
608
+ var import_minimatch = require("minimatch");
558
609
  var import_path2 = require("path");
559
610
  function determineImportance(filePath) {
560
611
  const name = (0, import_path2.basename)(filePath).toLowerCase();
@@ -577,7 +628,7 @@ function suggestSection(filePath, domain) {
577
628
  return `${domain} Reference`;
578
629
  }
579
630
  async function checkDocCoverage(domain, options = {}) {
580
- const { docsDir = "./docs", sourceDir = "./src", excludePatterns = [], graphCoverage } = options;
631
+ const { docsDir = "./docs", sourceDir = ".", excludePatterns = [], graphCoverage } = options;
581
632
  if (graphCoverage) {
582
633
  const gaps = graphCoverage.undocumented.map((file) => ({
583
634
  file,
@@ -597,9 +648,7 @@ async function checkDocCoverage(domain, options = {}) {
597
648
  const filteredSourceFiles = sourceFiles.filter((file) => {
598
649
  const relativePath = (0, import_path2.relative)(sourceDir, file);
599
650
  return !excludePatterns.some((pattern) => {
600
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
601
- const regex = new RegExp("^" + escaped + "$");
602
- return regex.test(relativePath) || regex.test(file);
651
+ return (0, import_minimatch.minimatch)(relativePath, pattern, { dot: true }) || (0, import_minimatch.minimatch)(file, pattern, { dot: true });
603
652
  });
604
653
  });
605
654
  const docFiles = await findFiles("**/*.md", docsDir);
@@ -657,8 +706,8 @@ async function checkDocCoverage(domain, options = {}) {
657
706
 
658
707
  // src/context/knowledge-map.ts
659
708
  var import_path3 = require("path");
660
- function suggestFix(path11, existingFiles) {
661
- const targetName = (0, import_path3.basename)(path11).toLowerCase();
709
+ function suggestFix(path13, existingFiles) {
710
+ const targetName = (0, import_path3.basename)(path13).toLowerCase();
662
711
  const similar = existingFiles.find((file) => {
663
712
  const fileName = (0, import_path3.basename)(file).toLowerCase();
664
713
  return fileName.includes(targetName) || targetName.includes(fileName);
@@ -666,7 +715,7 @@ function suggestFix(path11, existingFiles) {
666
715
  if (similar) {
667
716
  return `Did you mean "${similar}"?`;
668
717
  }
669
- return `Create the file "${path11}" or remove the link`;
718
+ return `Create the file "${path13}" or remove the link`;
670
719
  }
671
720
  async function validateKnowledgeMap(rootDir = process.cwd()) {
672
721
  console.warn(
@@ -1011,7 +1060,7 @@ function getPhaseCategories(phase) {
1011
1060
  }
1012
1061
 
1013
1062
  // src/constraints/layers.ts
1014
- var import_minimatch = require("minimatch");
1063
+ var import_minimatch2 = require("minimatch");
1015
1064
  function defineLayer(name, patterns, allowedDependencies) {
1016
1065
  return {
1017
1066
  name,
@@ -1022,7 +1071,7 @@ function defineLayer(name, patterns, allowedDependencies) {
1022
1071
  function resolveFileToLayer(file, layers) {
1023
1072
  for (const layer of layers) {
1024
1073
  for (const pattern of layer.patterns) {
1025
- if ((0, import_minimatch.minimatch)(file, pattern)) {
1074
+ if ((0, import_minimatch2.minimatch)(file, pattern)) {
1026
1075
  return layer;
1027
1076
  }
1028
1077
  }
@@ -1269,8 +1318,8 @@ function createBoundaryValidator(schema, name) {
1269
1318
  return (0, import_types.Ok)(result.data);
1270
1319
  }
1271
1320
  const suggestions = result.error.issues.map((issue) => {
1272
- const path11 = issue.path.join(".");
1273
- return path11 ? `${path11}: ${issue.message}` : issue.message;
1321
+ const path13 = issue.path.join(".");
1322
+ return path13 ? `${path13}: ${issue.message}` : issue.message;
1274
1323
  });
1275
1324
  return (0, import_types.Err)(
1276
1325
  createError(
@@ -1314,6 +1363,422 @@ function validateBoundaries(boundaries, data) {
1314
1363
  });
1315
1364
  }
1316
1365
 
1366
+ // src/constraints/sharing/types.ts
1367
+ var import_zod = require("zod");
1368
+ var ManifestSchema = import_zod.z.object({
1369
+ name: import_zod.z.string(),
1370
+ version: import_zod.z.string(),
1371
+ description: import_zod.z.string().optional(),
1372
+ include: import_zod.z.array(import_zod.z.string()).min(1),
1373
+ minHarnessVersion: import_zod.z.string().optional(),
1374
+ keywords: import_zod.z.array(import_zod.z.string()).optional().default([]),
1375
+ layers: import_zod.z.record(import_zod.z.array(import_zod.z.string())).optional(),
1376
+ boundaries: import_zod.z.array(
1377
+ import_zod.z.object({
1378
+ name: import_zod.z.string(),
1379
+ layer: import_zod.z.string(),
1380
+ direction: import_zod.z.enum(["input", "output"]),
1381
+ schema: import_zod.z.string()
1382
+ })
1383
+ ).optional()
1384
+ });
1385
+ var BundleConstraintsSchema = import_zod.z.object({
1386
+ layers: import_zod.z.array(
1387
+ import_zod.z.object({
1388
+ name: import_zod.z.string(),
1389
+ pattern: import_zod.z.string(),
1390
+ allowedDependencies: import_zod.z.array(import_zod.z.string())
1391
+ })
1392
+ ).optional(),
1393
+ forbiddenImports: import_zod.z.array(
1394
+ import_zod.z.object({
1395
+ from: import_zod.z.string(),
1396
+ disallow: import_zod.z.array(import_zod.z.string()),
1397
+ message: import_zod.z.string().optional()
1398
+ })
1399
+ ).optional(),
1400
+ boundaries: import_zod.z.object({
1401
+ requireSchema: import_zod.z.array(import_zod.z.string()).optional()
1402
+ }).optional(),
1403
+ architecture: import_zod.z.object({
1404
+ thresholds: import_zod.z.record(import_zod.z.unknown()).optional(),
1405
+ modules: import_zod.z.record(import_zod.z.record(import_zod.z.unknown())).optional()
1406
+ }).optional(),
1407
+ security: import_zod.z.object({
1408
+ rules: import_zod.z.record(import_zod.z.string()).optional()
1409
+ }).optional()
1410
+ });
1411
+ var BundleSchema = import_zod.z.object({
1412
+ name: import_zod.z.string(),
1413
+ version: import_zod.z.string(),
1414
+ description: import_zod.z.string().optional(),
1415
+ minHarnessVersion: import_zod.z.string().optional(),
1416
+ manifest: ManifestSchema,
1417
+ constraints: BundleConstraintsSchema,
1418
+ contributions: import_zod.z.record(import_zod.z.unknown()).optional()
1419
+ });
1420
+ var ContributionsSchema = import_zod.z.record(import_zod.z.unknown());
1421
+ var LockfilePackageSchema = import_zod.z.object({
1422
+ version: import_zod.z.string(),
1423
+ source: import_zod.z.string(),
1424
+ installedAt: import_zod.z.string(),
1425
+ contributions: import_zod.z.record(import_zod.z.unknown()).optional().nullable(),
1426
+ resolved: import_zod.z.string().optional(),
1427
+ integrity: import_zod.z.string().optional(),
1428
+ provenance: import_zod.z.array(import_zod.z.string()).optional()
1429
+ });
1430
+ var LockfileSchema = import_zod.z.object({
1431
+ version: import_zod.z.literal(1).optional(),
1432
+ // Used by some tests
1433
+ lockfileVersion: import_zod.z.literal(1).optional(),
1434
+ // Standard field
1435
+ packages: import_zod.z.record(LockfilePackageSchema)
1436
+ }).refine((data) => data.version !== void 0 || data.lockfileVersion !== void 0, {
1437
+ message: "Either 'version' or 'lockfileVersion' must be present and equal to 1"
1438
+ });
1439
+ var SharableLayerSchema = import_zod.z.unknown();
1440
+ var SharableForbiddenImportSchema = import_zod.z.unknown();
1441
+ var SharableBoundaryConfigSchema = import_zod.z.unknown();
1442
+ var SharableSecurityRulesSchema = import_zod.z.unknown();
1443
+
1444
+ // src/constraints/sharing/write-config.ts
1445
+ var fs = __toESM(require("fs/promises"));
1446
+ async function writeConfig(filePath, content) {
1447
+ try {
1448
+ const json = JSON.stringify(content, null, 2) + "\n";
1449
+ await fs.writeFile(filePath, json, "utf-8");
1450
+ return (0, import_types.Ok)(void 0);
1451
+ } catch (error) {
1452
+ return (0, import_types.Err)(error instanceof Error ? error : new Error(String(error)));
1453
+ }
1454
+ }
1455
+
1456
+ // src/constraints/sharing/manifest.ts
1457
+ function parseManifest(parsed) {
1458
+ const result = ManifestSchema.safeParse(parsed);
1459
+ if (!result.success) {
1460
+ const issues = result.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
1461
+ return { ok: false, error: `Invalid manifest: ${issues}` };
1462
+ }
1463
+ return { ok: true, value: result.data };
1464
+ }
1465
+
1466
+ // src/constraints/sharing/bundle.ts
1467
+ function resolveDotPath(obj, dotPath) {
1468
+ const segments = dotPath.split(".");
1469
+ let current = obj;
1470
+ for (const segment of segments) {
1471
+ if (current === null || typeof current !== "object") {
1472
+ return void 0;
1473
+ }
1474
+ current = current[segment];
1475
+ }
1476
+ return current;
1477
+ }
1478
+ function setDotPath(obj, dotPath, value) {
1479
+ const segments = dotPath.split(".");
1480
+ const lastSegment = segments[segments.length - 1];
1481
+ const parentSegments = segments.slice(0, -1);
1482
+ let current = obj;
1483
+ for (const segment of parentSegments) {
1484
+ if (current[segment] === void 0 || current[segment] === null || typeof current[segment] !== "object") {
1485
+ current[segment] = {};
1486
+ }
1487
+ current = current[segment];
1488
+ }
1489
+ if (lastSegment !== void 0) {
1490
+ current[lastSegment] = value;
1491
+ }
1492
+ }
1493
+ function extractBundle(manifest, config) {
1494
+ const constraints = {};
1495
+ for (const includePath of manifest.include) {
1496
+ const value = resolveDotPath(config, includePath);
1497
+ if (value !== void 0) {
1498
+ setDotPath(constraints, includePath, value);
1499
+ }
1500
+ }
1501
+ const bundle = {
1502
+ name: manifest.name,
1503
+ version: manifest.version,
1504
+ ...manifest.minHarnessVersion !== void 0 && {
1505
+ minHarnessVersion: manifest.minHarnessVersion
1506
+ },
1507
+ ...manifest.description !== void 0 && {
1508
+ description: manifest.description
1509
+ },
1510
+ manifest,
1511
+ constraints
1512
+ };
1513
+ const parsed = BundleSchema.safeParse(bundle);
1514
+ if (!parsed.success) {
1515
+ const issues = parsed.error.issues.map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
1516
+ return { ok: false, error: `Invalid bundle: ${issues}` };
1517
+ }
1518
+ return { ok: true, value: parsed.data };
1519
+ }
1520
+
1521
+ // src/constraints/sharing/merge.ts
1522
+ function deepEqual(a, b) {
1523
+ if (a === b) return true;
1524
+ if (typeof a !== typeof b) return false;
1525
+ if (typeof a !== "object" || a === null || b === null) return false;
1526
+ if (Array.isArray(a) && Array.isArray(b)) {
1527
+ if (a.length !== b.length) return false;
1528
+ return a.every((val, i) => deepEqual(val, b[i]));
1529
+ }
1530
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
1531
+ const keysA = Object.keys(a);
1532
+ const keysB = Object.keys(b);
1533
+ if (keysA.length !== keysB.length) return false;
1534
+ return keysA.every(
1535
+ (key) => deepEqual(a[key], b[key])
1536
+ );
1537
+ }
1538
+ function stringArraysEqual(a, b) {
1539
+ if (a.length !== b.length) return false;
1540
+ const sortedA = [...a].sort();
1541
+ const sortedB = [...b].sort();
1542
+ return sortedA.every((val, i) => val === sortedB[i]);
1543
+ }
1544
+ function deepMergeConstraints(localConfig, bundleConstraints, _existingContributions) {
1545
+ const config = { ...localConfig };
1546
+ const contributions = {};
1547
+ const conflicts = [];
1548
+ if (bundleConstraints.layers && bundleConstraints.layers.length > 0) {
1549
+ const localLayers = Array.isArray(localConfig.layers) ? localConfig.layers : [];
1550
+ const mergedLayers = [...localLayers];
1551
+ const contributedLayerNames = [];
1552
+ for (const bundleLayer of bundleConstraints.layers) {
1553
+ const existing = localLayers.find((l) => l.name === bundleLayer.name);
1554
+ if (!existing) {
1555
+ mergedLayers.push(bundleLayer);
1556
+ contributedLayerNames.push(bundleLayer.name);
1557
+ } else {
1558
+ const same = existing.pattern === bundleLayer.pattern && stringArraysEqual(existing.allowedDependencies, bundleLayer.allowedDependencies);
1559
+ if (!same) {
1560
+ conflicts.push({
1561
+ section: "layers",
1562
+ key: bundleLayer.name,
1563
+ localValue: existing,
1564
+ packageValue: bundleLayer,
1565
+ description: `Layer '${bundleLayer.name}' already exists locally with different configuration`
1566
+ });
1567
+ }
1568
+ }
1569
+ }
1570
+ config.layers = mergedLayers;
1571
+ if (contributedLayerNames.length > 0) {
1572
+ contributions.layers = contributedLayerNames;
1573
+ }
1574
+ }
1575
+ if (bundleConstraints.forbiddenImports && bundleConstraints.forbiddenImports.length > 0) {
1576
+ const localFI = Array.isArray(localConfig.forbiddenImports) ? localConfig.forbiddenImports : [];
1577
+ const mergedFI = [...localFI];
1578
+ const contributedFromKeys = [];
1579
+ for (const bundleRule of bundleConstraints.forbiddenImports) {
1580
+ const existing = localFI.find((r) => r.from === bundleRule.from);
1581
+ if (!existing) {
1582
+ const entry = {
1583
+ from: bundleRule.from,
1584
+ disallow: bundleRule.disallow
1585
+ };
1586
+ if (bundleRule.message !== void 0) {
1587
+ entry.message = bundleRule.message;
1588
+ }
1589
+ mergedFI.push(entry);
1590
+ contributedFromKeys.push(bundleRule.from);
1591
+ } else {
1592
+ const same = stringArraysEqual(existing.disallow, bundleRule.disallow);
1593
+ if (!same) {
1594
+ conflicts.push({
1595
+ section: "forbiddenImports",
1596
+ key: bundleRule.from,
1597
+ localValue: existing,
1598
+ packageValue: bundleRule,
1599
+ description: `Forbidden import rule for '${bundleRule.from}' already exists locally with different disallow list`
1600
+ });
1601
+ }
1602
+ }
1603
+ }
1604
+ config.forbiddenImports = mergedFI;
1605
+ if (contributedFromKeys.length > 0) {
1606
+ contributions.forbiddenImports = contributedFromKeys;
1607
+ }
1608
+ }
1609
+ if (bundleConstraints.boundaries) {
1610
+ const localBoundaries = localConfig.boundaries ?? { requireSchema: [] };
1611
+ const localSchemas = new Set(localBoundaries.requireSchema ?? []);
1612
+ const bundleSchemas = bundleConstraints.boundaries.requireSchema ?? [];
1613
+ const newSchemas = [];
1614
+ for (const schema of bundleSchemas) {
1615
+ if (!localSchemas.has(schema)) {
1616
+ newSchemas.push(schema);
1617
+ localSchemas.add(schema);
1618
+ }
1619
+ }
1620
+ config.boundaries = {
1621
+ requireSchema: [...localBoundaries.requireSchema ?? [], ...newSchemas]
1622
+ };
1623
+ if (newSchemas.length > 0) {
1624
+ contributions.boundaries = newSchemas;
1625
+ }
1626
+ }
1627
+ if (bundleConstraints.architecture) {
1628
+ const localArch = localConfig.architecture ?? {
1629
+ enabled: true,
1630
+ baselinePath: ".harness/arch/baselines.json",
1631
+ thresholds: {},
1632
+ modules: {}
1633
+ };
1634
+ const mergedThresholds = { ...localArch.thresholds };
1635
+ const contributedThresholdKeys = [];
1636
+ const bundleThresholds = bundleConstraints.architecture.thresholds ?? {};
1637
+ for (const [category, value] of Object.entries(bundleThresholds)) {
1638
+ if (!(category in mergedThresholds)) {
1639
+ mergedThresholds[category] = value;
1640
+ contributedThresholdKeys.push(category);
1641
+ } else if (!deepEqual(mergedThresholds[category], value)) {
1642
+ conflicts.push({
1643
+ section: "architecture.thresholds",
1644
+ key: category,
1645
+ localValue: mergedThresholds[category],
1646
+ packageValue: value,
1647
+ description: `Architecture threshold '${category}' already exists locally with a different value`
1648
+ });
1649
+ }
1650
+ }
1651
+ const mergedModules = { ...localArch.modules };
1652
+ const contributedModuleKeys = [];
1653
+ const bundleModules = bundleConstraints.architecture.modules ?? {};
1654
+ for (const [modulePath, bundleCategoryMap] of Object.entries(bundleModules)) {
1655
+ if (!(modulePath in mergedModules)) {
1656
+ mergedModules[modulePath] = bundleCategoryMap;
1657
+ for (const cat of Object.keys(bundleCategoryMap)) {
1658
+ contributedModuleKeys.push(`${modulePath}:${cat}`);
1659
+ }
1660
+ } else {
1661
+ const localCategoryMap = mergedModules[modulePath];
1662
+ const mergedCategoryMap = { ...localCategoryMap };
1663
+ for (const [category, value] of Object.entries(bundleCategoryMap)) {
1664
+ if (!(category in mergedCategoryMap)) {
1665
+ mergedCategoryMap[category] = value;
1666
+ contributedModuleKeys.push(`${modulePath}:${category}`);
1667
+ } else if (!deepEqual(mergedCategoryMap[category], value)) {
1668
+ conflicts.push({
1669
+ section: "architecture.modules",
1670
+ key: `${modulePath}:${category}`,
1671
+ localValue: mergedCategoryMap[category],
1672
+ packageValue: value,
1673
+ description: `Architecture module override '${modulePath}' category '${category}' already exists locally with a different value`
1674
+ });
1675
+ }
1676
+ }
1677
+ mergedModules[modulePath] = mergedCategoryMap;
1678
+ }
1679
+ }
1680
+ config.architecture = {
1681
+ ...localArch,
1682
+ thresholds: mergedThresholds,
1683
+ modules: mergedModules
1684
+ };
1685
+ if (contributedThresholdKeys.length > 0) {
1686
+ contributions["architecture.thresholds"] = contributedThresholdKeys;
1687
+ }
1688
+ if (contributedModuleKeys.length > 0) {
1689
+ contributions["architecture.modules"] = contributedModuleKeys;
1690
+ }
1691
+ }
1692
+ if (bundleConstraints.security?.rules) {
1693
+ const localSecurity = localConfig.security ?? { rules: {} };
1694
+ const localRules = localSecurity.rules ?? {};
1695
+ const mergedRules = { ...localRules };
1696
+ const contributedRuleIds = [];
1697
+ for (const [ruleId, severity] of Object.entries(bundleConstraints.security.rules)) {
1698
+ if (!(ruleId in mergedRules)) {
1699
+ mergedRules[ruleId] = severity;
1700
+ contributedRuleIds.push(ruleId);
1701
+ } else if (mergedRules[ruleId] !== severity) {
1702
+ conflicts.push({
1703
+ section: "security.rules",
1704
+ key: ruleId,
1705
+ localValue: mergedRules[ruleId],
1706
+ packageValue: severity,
1707
+ description: `Security rule '${ruleId}' already exists locally with severity '${mergedRules[ruleId]}', bundle has '${severity}'`
1708
+ });
1709
+ }
1710
+ }
1711
+ config.security = { ...localSecurity, rules: mergedRules };
1712
+ if (contributedRuleIds.length > 0) {
1713
+ contributions["security.rules"] = contributedRuleIds;
1714
+ }
1715
+ }
1716
+ return { config, contributions, conflicts };
1717
+ }
1718
+
1719
+ // src/constraints/sharing/lockfile.ts
1720
+ var fs2 = __toESM(require("fs/promises"));
1721
+ async function readLockfile(lockfilePath) {
1722
+ let raw;
1723
+ try {
1724
+ raw = await fs2.readFile(lockfilePath, "utf-8");
1725
+ } catch (err) {
1726
+ if (isNodeError(err) && err.code === "ENOENT") {
1727
+ return { ok: true, value: null };
1728
+ }
1729
+ return {
1730
+ ok: false,
1731
+ error: `Failed to read lockfile: ${err instanceof Error ? err.message : String(err)}`
1732
+ };
1733
+ }
1734
+ let parsed;
1735
+ try {
1736
+ parsed = JSON.parse(raw);
1737
+ } catch {
1738
+ return {
1739
+ ok: false,
1740
+ error: `Failed to parse lockfile as JSON: file contains invalid JSON`
1741
+ };
1742
+ }
1743
+ const result = LockfileSchema.safeParse(parsed);
1744
+ if (!result.success) {
1745
+ return {
1746
+ ok: false,
1747
+ error: `Lockfile schema validation failed: ${result.error.issues.map((i) => i.message).join(", ")}`
1748
+ };
1749
+ }
1750
+ return { ok: true, value: result.data };
1751
+ }
1752
+ async function writeLockfile(lockfilePath, lockfile) {
1753
+ await writeConfig(lockfilePath, lockfile);
1754
+ }
1755
+ function addProvenance(lockfile, packageName, entry) {
1756
+ return {
1757
+ ...lockfile,
1758
+ packages: {
1759
+ ...lockfile.packages,
1760
+ [packageName]: entry
1761
+ }
1762
+ };
1763
+ }
1764
+ function removeProvenance(lockfile, packageName) {
1765
+ const existing = lockfile.packages[packageName];
1766
+ if (!existing) {
1767
+ return { lockfile, contributions: null };
1768
+ }
1769
+ const { [packageName]: _removed, ...remaining } = lockfile.packages;
1770
+ return {
1771
+ lockfile: {
1772
+ ...lockfile,
1773
+ packages: remaining
1774
+ },
1775
+ contributions: existing.contributions ?? null
1776
+ };
1777
+ }
1778
+ function isNodeError(err) {
1779
+ return err instanceof Error && "code" in err;
1780
+ }
1781
+
1317
1782
  // src/shared/parsers/typescript.ts
1318
1783
  var import_typescript_estree = require("@typescript-eslint/typescript-estree");
1319
1784
 
@@ -1339,11 +1804,11 @@ function walk(node, visitor) {
1339
1804
  var TypeScriptParser = class {
1340
1805
  name = "typescript";
1341
1806
  extensions = [".ts", ".tsx", ".mts", ".cts"];
1342
- async parseFile(path11) {
1343
- const contentResult = await readFileContent(path11);
1807
+ async parseFile(path13) {
1808
+ const contentResult = await readFileContent(path13);
1344
1809
  if (!contentResult.ok) {
1345
1810
  return (0, import_types.Err)(
1346
- createParseError("NOT_FOUND", `File not found: ${path11}`, { path: path11 }, [
1811
+ createParseError("NOT_FOUND", `File not found: ${path13}`, { path: path13 }, [
1347
1812
  "Check that the file exists",
1348
1813
  "Verify the path is correct"
1349
1814
  ])
@@ -1353,7 +1818,7 @@ var TypeScriptParser = class {
1353
1818
  const ast = (0, import_typescript_estree.parse)(contentResult.value, {
1354
1819
  loc: true,
1355
1820
  range: true,
1356
- jsx: path11.endsWith(".tsx"),
1821
+ jsx: path13.endsWith(".tsx"),
1357
1822
  errorOnUnknownASTType: false
1358
1823
  });
1359
1824
  return (0, import_types.Ok)({
@@ -1364,7 +1829,7 @@ var TypeScriptParser = class {
1364
1829
  } catch (e) {
1365
1830
  const error = e;
1366
1831
  return (0, import_types.Err)(
1367
- createParseError("SYNTAX_ERROR", `Failed to parse ${path11}: ${error.message}`, { path: path11 }, [
1832
+ createParseError("SYNTAX_ERROR", `Failed to parse ${path13}: ${error.message}`, { path: path13 }, [
1368
1833
  "Check for syntax errors in the file",
1369
1834
  "Ensure valid TypeScript syntax"
1370
1835
  ])
@@ -1531,7 +1996,7 @@ var TypeScriptParser = class {
1531
1996
 
1532
1997
  // src/entropy/snapshot.ts
1533
1998
  var import_path6 = require("path");
1534
- var import_minimatch2 = require("minimatch");
1999
+ var import_minimatch3 = require("minimatch");
1535
2000
  async function resolveEntryPoints(rootDir, explicitEntries) {
1536
2001
  if (explicitEntries && explicitEntries.length > 0) {
1537
2002
  const resolved = explicitEntries.map((e) => (0, import_path6.resolve)(rootDir, e));
@@ -1648,22 +2113,22 @@ function extractInlineRefs(content) {
1648
2113
  }
1649
2114
  return refs;
1650
2115
  }
1651
- async function parseDocumentationFile(path11) {
1652
- const contentResult = await readFileContent(path11);
2116
+ async function parseDocumentationFile(path13) {
2117
+ const contentResult = await readFileContent(path13);
1653
2118
  if (!contentResult.ok) {
1654
2119
  return (0, import_types.Err)(
1655
2120
  createEntropyError(
1656
2121
  "PARSE_ERROR",
1657
- `Failed to read documentation file: ${path11}`,
1658
- { file: path11 },
2122
+ `Failed to read documentation file: ${path13}`,
2123
+ { file: path13 },
1659
2124
  ["Check that the file exists"]
1660
2125
  )
1661
2126
  );
1662
2127
  }
1663
2128
  const content = contentResult.value;
1664
- const type = path11.endsWith(".md") ? "markdown" : "text";
2129
+ const type = path13.endsWith(".md") ? "markdown" : "text";
1665
2130
  return (0, import_types.Ok)({
1666
- path: path11,
2131
+ path: path13,
1667
2132
  type,
1668
2133
  content,
1669
2134
  codeBlocks: extractCodeBlocks(content),
@@ -1795,7 +2260,7 @@ async function buildSnapshot(config) {
1795
2260
  }
1796
2261
  sourceFilePaths = sourceFilePaths.filter((f) => {
1797
2262
  const rel = (0, import_path6.relative)(rootDir, f);
1798
- return !excludePatterns.some((p) => (0, import_minimatch2.minimatch)(rel, p));
2263
+ return !excludePatterns.some((p) => (0, import_minimatch3.minimatch)(rel, p));
1799
2264
  });
1800
2265
  const files = [];
1801
2266
  for (const filePath of sourceFilePaths) {
@@ -2325,11 +2790,11 @@ async function detectDeadCode(snapshot, graphDeadCodeData) {
2325
2790
  }
2326
2791
 
2327
2792
  // src/entropy/detectors/patterns.ts
2328
- var import_minimatch3 = require("minimatch");
2793
+ var import_minimatch4 = require("minimatch");
2329
2794
  var import_path9 = require("path");
2330
2795
  function fileMatchesPattern(filePath, pattern, rootDir) {
2331
2796
  const relativePath = (0, import_path9.relative)(rootDir, filePath);
2332
- return (0, import_minimatch3.minimatch)(relativePath, pattern);
2797
+ return (0, import_minimatch4.minimatch)(relativePath, pattern);
2333
2798
  }
2334
2799
  function checkConfigPattern(pattern, file, rootDir) {
2335
2800
  const matches = [];
@@ -3331,14 +3796,14 @@ var EntropyAnalyzer = class {
3331
3796
  };
3332
3797
 
3333
3798
  // src/entropy/fixers/safe-fixes.ts
3334
- var fs = __toESM(require("fs"));
3799
+ var fs3 = __toESM(require("fs"));
3335
3800
  var import_util2 = require("util");
3336
3801
  var import_path10 = require("path");
3337
- var readFile4 = (0, import_util2.promisify)(fs.readFile);
3338
- var writeFile2 = (0, import_util2.promisify)(fs.writeFile);
3339
- var unlink2 = (0, import_util2.promisify)(fs.unlink);
3340
- var mkdir2 = (0, import_util2.promisify)(fs.mkdir);
3341
- var copyFile2 = (0, import_util2.promisify)(fs.copyFile);
3802
+ var readFile5 = (0, import_util2.promisify)(fs3.readFile);
3803
+ var writeFile3 = (0, import_util2.promisify)(fs3.writeFile);
3804
+ var unlink2 = (0, import_util2.promisify)(fs3.unlink);
3805
+ var mkdir2 = (0, import_util2.promisify)(fs3.mkdir);
3806
+ var copyFile2 = (0, import_util2.promisify)(fs3.copyFile);
3342
3807
  var DEFAULT_FIX_CONFIG = {
3343
3808
  dryRun: false,
3344
3809
  fixTypes: ["unused-imports", "dead-files"],
@@ -3465,25 +3930,25 @@ async function applySingleFix(fix, config) {
3465
3930
  break;
3466
3931
  case "delete-lines":
3467
3932
  if (fix.line !== void 0) {
3468
- const content = await readFile4(fix.file, "utf-8");
3933
+ const content = await readFile5(fix.file, "utf-8");
3469
3934
  const lines = content.split("\n");
3470
3935
  lines.splice(fix.line - 1, 1);
3471
- await writeFile2(fix.file, lines.join("\n"));
3936
+ await writeFile3(fix.file, lines.join("\n"));
3472
3937
  }
3473
3938
  break;
3474
3939
  case "replace":
3475
3940
  if (fix.oldContent && fix.newContent !== void 0) {
3476
- const content = await readFile4(fix.file, "utf-8");
3941
+ const content = await readFile5(fix.file, "utf-8");
3477
3942
  const newContent = content.replace(fix.oldContent, fix.newContent);
3478
- await writeFile2(fix.file, newContent);
3943
+ await writeFile3(fix.file, newContent);
3479
3944
  }
3480
3945
  break;
3481
3946
  case "insert":
3482
3947
  if (fix.line !== void 0 && fix.newContent) {
3483
- const content = await readFile4(fix.file, "utf-8");
3948
+ const content = await readFile5(fix.file, "utf-8");
3484
3949
  const lines = content.split("\n");
3485
3950
  lines.splice(fix.line - 1, 0, fix.newContent);
3486
- await writeFile2(fix.file, lines.join("\n"));
3951
+ await writeFile3(fix.file, lines.join("\n"));
3487
3952
  }
3488
3953
  break;
3489
3954
  }
@@ -3660,46 +4125,46 @@ function deduplicateCleanupFindings(findings) {
3660
4125
  }
3661
4126
 
3662
4127
  // src/entropy/config/schema.ts
3663
- var import_zod = require("zod");
3664
- var MustExportRuleSchema = import_zod.z.object({
3665
- type: import_zod.z.literal("must-export"),
3666
- names: import_zod.z.array(import_zod.z.string())
4128
+ var import_zod2 = require("zod");
4129
+ var MustExportRuleSchema = import_zod2.z.object({
4130
+ type: import_zod2.z.literal("must-export"),
4131
+ names: import_zod2.z.array(import_zod2.z.string())
3667
4132
  });
3668
- var MustExportDefaultRuleSchema = import_zod.z.object({
3669
- type: import_zod.z.literal("must-export-default"),
3670
- kind: import_zod.z.enum(["class", "function", "object"]).optional()
4133
+ var MustExportDefaultRuleSchema = import_zod2.z.object({
4134
+ type: import_zod2.z.literal("must-export-default"),
4135
+ kind: import_zod2.z.enum(["class", "function", "object"]).optional()
3671
4136
  });
3672
- var NoExportRuleSchema = import_zod.z.object({
3673
- type: import_zod.z.literal("no-export"),
3674
- names: import_zod.z.array(import_zod.z.string())
4137
+ var NoExportRuleSchema = import_zod2.z.object({
4138
+ type: import_zod2.z.literal("no-export"),
4139
+ names: import_zod2.z.array(import_zod2.z.string())
3675
4140
  });
3676
- var MustImportRuleSchema = import_zod.z.object({
3677
- type: import_zod.z.literal("must-import"),
3678
- from: import_zod.z.string(),
3679
- names: import_zod.z.array(import_zod.z.string()).optional()
4141
+ var MustImportRuleSchema = import_zod2.z.object({
4142
+ type: import_zod2.z.literal("must-import"),
4143
+ from: import_zod2.z.string(),
4144
+ names: import_zod2.z.array(import_zod2.z.string()).optional()
3680
4145
  });
3681
- var NoImportRuleSchema = import_zod.z.object({
3682
- type: import_zod.z.literal("no-import"),
3683
- from: import_zod.z.string()
4146
+ var NoImportRuleSchema = import_zod2.z.object({
4147
+ type: import_zod2.z.literal("no-import"),
4148
+ from: import_zod2.z.string()
3684
4149
  });
3685
- var NamingRuleSchema = import_zod.z.object({
3686
- type: import_zod.z.literal("naming"),
3687
- match: import_zod.z.string(),
3688
- convention: import_zod.z.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
4150
+ var NamingRuleSchema = import_zod2.z.object({
4151
+ type: import_zod2.z.literal("naming"),
4152
+ match: import_zod2.z.string(),
4153
+ convention: import_zod2.z.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
3689
4154
  });
3690
- var MaxExportsRuleSchema = import_zod.z.object({
3691
- type: import_zod.z.literal("max-exports"),
3692
- count: import_zod.z.number().positive()
4155
+ var MaxExportsRuleSchema = import_zod2.z.object({
4156
+ type: import_zod2.z.literal("max-exports"),
4157
+ count: import_zod2.z.number().positive()
3693
4158
  });
3694
- var MaxLinesRuleSchema = import_zod.z.object({
3695
- type: import_zod.z.literal("max-lines"),
3696
- count: import_zod.z.number().positive()
4159
+ var MaxLinesRuleSchema = import_zod2.z.object({
4160
+ type: import_zod2.z.literal("max-lines"),
4161
+ count: import_zod2.z.number().positive()
3697
4162
  });
3698
- var RequireJSDocRuleSchema = import_zod.z.object({
3699
- type: import_zod.z.literal("require-jsdoc"),
3700
- for: import_zod.z.array(import_zod.z.enum(["function", "class", "export"]))
4163
+ var RequireJSDocRuleSchema = import_zod2.z.object({
4164
+ type: import_zod2.z.literal("require-jsdoc"),
4165
+ for: import_zod2.z.array(import_zod2.z.enum(["function", "class", "export"]))
3701
4166
  });
3702
- var RuleSchema = import_zod.z.discriminatedUnion("type", [
4167
+ var RuleSchema = import_zod2.z.discriminatedUnion("type", [
3703
4168
  MustExportRuleSchema,
3704
4169
  MustExportDefaultRuleSchema,
3705
4170
  NoExportRuleSchema,
@@ -3710,47 +4175,47 @@ var RuleSchema = import_zod.z.discriminatedUnion("type", [
3710
4175
  MaxLinesRuleSchema,
3711
4176
  RequireJSDocRuleSchema
3712
4177
  ]);
3713
- var ConfigPatternSchema = import_zod.z.object({
3714
- name: import_zod.z.string().min(1),
3715
- description: import_zod.z.string(),
3716
- severity: import_zod.z.enum(["error", "warning"]),
3717
- files: import_zod.z.array(import_zod.z.string()),
4178
+ var ConfigPatternSchema = import_zod2.z.object({
4179
+ name: import_zod2.z.string().min(1),
4180
+ description: import_zod2.z.string(),
4181
+ severity: import_zod2.z.enum(["error", "warning"]),
4182
+ files: import_zod2.z.array(import_zod2.z.string()),
3718
4183
  rule: RuleSchema,
3719
- message: import_zod.z.string().optional()
4184
+ message: import_zod2.z.string().optional()
3720
4185
  });
3721
- var PatternConfigSchema = import_zod.z.object({
3722
- patterns: import_zod.z.array(ConfigPatternSchema),
3723
- customPatterns: import_zod.z.array(import_zod.z.any()).optional(),
4186
+ var PatternConfigSchema = import_zod2.z.object({
4187
+ patterns: import_zod2.z.array(ConfigPatternSchema),
4188
+ customPatterns: import_zod2.z.array(import_zod2.z.any()).optional(),
3724
4189
  // Code patterns are functions, can't validate
3725
- ignoreFiles: import_zod.z.array(import_zod.z.string()).optional()
4190
+ ignoreFiles: import_zod2.z.array(import_zod2.z.string()).optional()
3726
4191
  });
3727
- var DriftConfigSchema = import_zod.z.object({
3728
- docPaths: import_zod.z.array(import_zod.z.string()).optional(),
3729
- checkApiSignatures: import_zod.z.boolean().optional(),
3730
- checkExamples: import_zod.z.boolean().optional(),
3731
- checkStructure: import_zod.z.boolean().optional(),
3732
- ignorePatterns: import_zod.z.array(import_zod.z.string()).optional()
4192
+ var DriftConfigSchema = import_zod2.z.object({
4193
+ docPaths: import_zod2.z.array(import_zod2.z.string()).optional(),
4194
+ checkApiSignatures: import_zod2.z.boolean().optional(),
4195
+ checkExamples: import_zod2.z.boolean().optional(),
4196
+ checkStructure: import_zod2.z.boolean().optional(),
4197
+ ignorePatterns: import_zod2.z.array(import_zod2.z.string()).optional()
3733
4198
  });
3734
- var DeadCodeConfigSchema = import_zod.z.object({
3735
- entryPoints: import_zod.z.array(import_zod.z.string()).optional(),
3736
- includeTypes: import_zod.z.boolean().optional(),
3737
- includeInternals: import_zod.z.boolean().optional(),
3738
- ignorePatterns: import_zod.z.array(import_zod.z.string()).optional(),
3739
- treatDynamicImportsAs: import_zod.z.enum(["used", "unknown"]).optional()
4199
+ var DeadCodeConfigSchema = import_zod2.z.object({
4200
+ entryPoints: import_zod2.z.array(import_zod2.z.string()).optional(),
4201
+ includeTypes: import_zod2.z.boolean().optional(),
4202
+ includeInternals: import_zod2.z.boolean().optional(),
4203
+ ignorePatterns: import_zod2.z.array(import_zod2.z.string()).optional(),
4204
+ treatDynamicImportsAs: import_zod2.z.enum(["used", "unknown"]).optional()
3740
4205
  });
3741
- var EntropyConfigSchema = import_zod.z.object({
3742
- rootDir: import_zod.z.string(),
3743
- parser: import_zod.z.any().optional(),
4206
+ var EntropyConfigSchema = import_zod2.z.object({
4207
+ rootDir: import_zod2.z.string(),
4208
+ parser: import_zod2.z.any().optional(),
3744
4209
  // LanguageParser instance, can't validate
3745
- entryPoints: import_zod.z.array(import_zod.z.string()).optional(),
3746
- analyze: import_zod.z.object({
3747
- drift: import_zod.z.union([import_zod.z.boolean(), DriftConfigSchema]).optional(),
3748
- deadCode: import_zod.z.union([import_zod.z.boolean(), DeadCodeConfigSchema]).optional(),
3749
- patterns: import_zod.z.union([import_zod.z.boolean(), PatternConfigSchema]).optional()
4210
+ entryPoints: import_zod2.z.array(import_zod2.z.string()).optional(),
4211
+ analyze: import_zod2.z.object({
4212
+ drift: import_zod2.z.union([import_zod2.z.boolean(), DriftConfigSchema]).optional(),
4213
+ deadCode: import_zod2.z.union([import_zod2.z.boolean(), DeadCodeConfigSchema]).optional(),
4214
+ patterns: import_zod2.z.union([import_zod2.z.boolean(), PatternConfigSchema]).optional()
3750
4215
  }),
3751
- include: import_zod.z.array(import_zod.z.string()).optional(),
3752
- exclude: import_zod.z.array(import_zod.z.string()).optional(),
3753
- docPaths: import_zod.z.array(import_zod.z.string()).optional()
4216
+ include: import_zod2.z.array(import_zod2.z.string()).optional(),
4217
+ exclude: import_zod2.z.array(import_zod2.z.string()).optional(),
4218
+ docPaths: import_zod2.z.array(import_zod2.z.string()).optional()
3754
4219
  });
3755
4220
  function validatePatternConfig(config) {
3756
4221
  const result = PatternConfigSchema.safeParse(config);
@@ -4007,7 +4472,7 @@ var RegressionDetector = class {
4007
4472
  };
4008
4473
 
4009
4474
  // src/performance/critical-path.ts
4010
- var fs2 = __toESM(require("fs"));
4475
+ var fs4 = __toESM(require("fs"));
4011
4476
  var path = __toESM(require("path"));
4012
4477
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git"]);
4013
4478
  var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
@@ -4059,7 +4524,7 @@ var CriticalPathResolver = class {
4059
4524
  walkDir(dir, entries) {
4060
4525
  let items;
4061
4526
  try {
4062
- items = fs2.readdirSync(dir, { withFileTypes: true });
4527
+ items = fs4.readdirSync(dir, { withFileTypes: true });
4063
4528
  } catch {
4064
4529
  return;
4065
4530
  }
@@ -4075,7 +4540,7 @@ var CriticalPathResolver = class {
4075
4540
  scanFile(filePath, entries) {
4076
4541
  let content;
4077
4542
  try {
4078
- content = fs2.readFileSync(filePath, "utf-8");
4543
+ content = fs4.readFileSync(filePath, "utf-8");
4079
4544
  } catch {
4080
4545
  return;
4081
4546
  }
@@ -4254,17 +4719,17 @@ function resetFeedbackConfig() {
4254
4719
  }
4255
4720
 
4256
4721
  // src/feedback/review/diff-analyzer.ts
4257
- function parseDiff(diff) {
4722
+ function parseDiff(diff2) {
4258
4723
  try {
4259
- if (!diff.trim()) {
4260
- return (0, import_types.Ok)({ diff, files: [] });
4724
+ if (!diff2.trim()) {
4725
+ return (0, import_types.Ok)({ diff: diff2, files: [] });
4261
4726
  }
4262
4727
  const files = [];
4263
4728
  const newFileRegex = /new file mode/;
4264
4729
  const deletedFileRegex = /deleted file mode/;
4265
4730
  const additionRegex = /^\+(?!\+\+)/gm;
4266
4731
  const deletionRegex = /^-(?!--)/gm;
4267
- const diffParts = diff.split(/(?=diff --git)/);
4732
+ const diffParts = diff2.split(/(?=diff --git)/);
4268
4733
  for (const part of diffParts) {
4269
4734
  if (!part.trim()) continue;
4270
4735
  const headerMatch = /diff --git a\/(.+?) b\/(.+?)(?:\n|$)/.exec(part);
@@ -4287,7 +4752,7 @@ function parseDiff(diff) {
4287
4752
  deletions
4288
4753
  });
4289
4754
  }
4290
- return (0, import_types.Ok)({ diff, files });
4755
+ return (0, import_types.Ok)({ diff: diff2, files });
4291
4756
  } catch (error) {
4292
4757
  return (0, import_types.Err)({
4293
4758
  code: "DIFF_PARSE_ERROR",
@@ -4891,77 +5356,1265 @@ var NoOpSink = class {
4891
5356
  }
4892
5357
  };
4893
5358
 
5359
+ // src/architecture/types.ts
5360
+ var import_zod3 = require("zod");
5361
+ var ArchMetricCategorySchema = import_zod3.z.enum([
5362
+ "circular-deps",
5363
+ "layer-violations",
5364
+ "complexity",
5365
+ "coupling",
5366
+ "forbidden-imports",
5367
+ "module-size",
5368
+ "dependency-depth"
5369
+ ]);
5370
+ var ViolationSchema = import_zod3.z.object({
5371
+ id: import_zod3.z.string(),
5372
+ // stable hash: sha256(relativePath + ':' + category + ':' + normalizedDetail)
5373
+ file: import_zod3.z.string(),
5374
+ // relative to project root
5375
+ category: ArchMetricCategorySchema.optional(),
5376
+ // context for baseline reporting
5377
+ detail: import_zod3.z.string(),
5378
+ // human-readable description
5379
+ severity: import_zod3.z.enum(["error", "warning"])
5380
+ });
5381
+ var MetricResultSchema = import_zod3.z.object({
5382
+ category: ArchMetricCategorySchema,
5383
+ scope: import_zod3.z.string(),
5384
+ // e.g., 'project', 'src/services', 'src/api/routes.ts'
5385
+ value: import_zod3.z.number(),
5386
+ // numeric metric (violation count, complexity score, etc.)
5387
+ violations: import_zod3.z.array(ViolationSchema),
5388
+ metadata: import_zod3.z.record(import_zod3.z.unknown()).optional()
5389
+ });
5390
+ var CategoryBaselineSchema = import_zod3.z.object({
5391
+ value: import_zod3.z.number(),
5392
+ // aggregate metric value at baseline time
5393
+ violationIds: import_zod3.z.array(import_zod3.z.string())
5394
+ // stable IDs of known violations (the allowlist)
5395
+ });
5396
+ var ArchBaselineSchema = import_zod3.z.object({
5397
+ version: import_zod3.z.literal(1),
5398
+ updatedAt: import_zod3.z.string().datetime(),
5399
+ // ISO 8601
5400
+ updatedFrom: import_zod3.z.string(),
5401
+ // commit hash
5402
+ metrics: import_zod3.z.record(ArchMetricCategorySchema, CategoryBaselineSchema)
5403
+ });
5404
+ var CategoryRegressionSchema = import_zod3.z.object({
5405
+ category: ArchMetricCategorySchema,
5406
+ baselineValue: import_zod3.z.number(),
5407
+ currentValue: import_zod3.z.number(),
5408
+ delta: import_zod3.z.number()
5409
+ });
5410
+ var ArchDiffResultSchema = import_zod3.z.object({
5411
+ passed: import_zod3.z.boolean(),
5412
+ newViolations: import_zod3.z.array(ViolationSchema),
5413
+ // in current but not in baseline -> FAIL
5414
+ resolvedViolations: import_zod3.z.array(import_zod3.z.string()),
5415
+ // in baseline but not in current -> celebrate
5416
+ preExisting: import_zod3.z.array(import_zod3.z.string()),
5417
+ // in both -> allowed, tracked
5418
+ regressions: import_zod3.z.array(CategoryRegressionSchema)
5419
+ // aggregate value exceeded baseline
5420
+ });
5421
+ var ThresholdConfigSchema = import_zod3.z.record(
5422
+ ArchMetricCategorySchema,
5423
+ import_zod3.z.union([import_zod3.z.number(), import_zod3.z.record(import_zod3.z.string(), import_zod3.z.number())])
5424
+ );
5425
+ var ArchConfigSchema = import_zod3.z.object({
5426
+ enabled: import_zod3.z.boolean().default(true),
5427
+ baselinePath: import_zod3.z.string().default(".harness/arch/baselines.json"),
5428
+ thresholds: ThresholdConfigSchema.default({}),
5429
+ modules: import_zod3.z.record(import_zod3.z.string(), ThresholdConfigSchema).default({})
5430
+ });
5431
+ var ConstraintRuleSchema = import_zod3.z.object({
5432
+ id: import_zod3.z.string(),
5433
+ // stable hash: sha256(category + ':' + scope + ':' + description)
5434
+ category: ArchMetricCategorySchema,
5435
+ description: import_zod3.z.string(),
5436
+ // e.g., "Layer 'services' must not import from 'ui'"
5437
+ scope: import_zod3.z.string(),
5438
+ // e.g., 'src/services/', 'project'
5439
+ targets: import_zod3.z.array(import_zod3.z.string()).optional()
5440
+ // forward-compat for governs edges
5441
+ });
5442
+
5443
+ // src/architecture/collectors/circular-deps.ts
5444
+ var import_node_path3 = require("path");
5445
+
5446
+ // src/architecture/collectors/hash.ts
5447
+ var import_node_crypto = require("crypto");
5448
+ function violationId(relativePath, category, normalizedDetail) {
5449
+ const path13 = relativePath.replace(/\\/g, "/");
5450
+ const input = `${path13}:${category}:${normalizedDetail}`;
5451
+ return (0, import_node_crypto.createHash)("sha256").update(input).digest("hex");
5452
+ }
5453
+ function constraintRuleId(category, scope, description) {
5454
+ const input = `${category}:${scope}:${description}`;
5455
+ return (0, import_node_crypto.createHash)("sha256").update(input).digest("hex");
5456
+ }
5457
+
5458
+ // src/architecture/collectors/circular-deps.ts
5459
+ var CircularDepsCollector = class {
5460
+ category = "circular-deps";
5461
+ getRules(_config, _rootDir) {
5462
+ const description = "No circular dependencies allowed";
5463
+ return [
5464
+ {
5465
+ id: constraintRuleId(this.category, "project", description),
5466
+ category: this.category,
5467
+ description,
5468
+ scope: "project"
5469
+ }
5470
+ ];
5471
+ }
5472
+ async collect(_config, rootDir) {
5473
+ const files = await findFiles("**/*.ts", rootDir);
5474
+ const stubParser = {
5475
+ name: "typescript",
5476
+ extensions: [".ts", ".tsx"],
5477
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "not needed" } }),
5478
+ extractImports: () => ({
5479
+ ok: false,
5480
+ error: { code: "EXTRACT_ERROR", message: "not needed" }
5481
+ }),
5482
+ extractExports: () => ({
5483
+ ok: false,
5484
+ error: { code: "EXTRACT_ERROR", message: "not needed" }
5485
+ }),
5486
+ health: async () => ({ ok: true, value: { available: true } })
5487
+ };
5488
+ const graphResult = await buildDependencyGraph(files, stubParser);
5489
+ if (!graphResult.ok) {
5490
+ return [
5491
+ {
5492
+ category: this.category,
5493
+ scope: "project",
5494
+ value: 0,
5495
+ violations: [],
5496
+ metadata: { error: "Failed to build dependency graph" }
5497
+ }
5498
+ ];
5499
+ }
5500
+ const result = detectCircularDeps(graphResult.value);
5501
+ if (!result.ok) {
5502
+ return [
5503
+ {
5504
+ category: this.category,
5505
+ scope: "project",
5506
+ value: 0,
5507
+ violations: [],
5508
+ metadata: { error: "Failed to detect circular deps" }
5509
+ }
5510
+ ];
5511
+ }
5512
+ const { cycles, largestCycle } = result.value;
5513
+ const violations = cycles.map((cycle) => {
5514
+ const cyclePath = cycle.cycle.map((f) => (0, import_node_path3.relative)(rootDir, f)).join(" -> ");
5515
+ const firstFile = (0, import_node_path3.relative)(rootDir, cycle.cycle[0]);
5516
+ return {
5517
+ id: violationId(firstFile, this.category, cyclePath),
5518
+ file: firstFile,
5519
+ detail: `Circular dependency: ${cyclePath}`,
5520
+ severity: cycle.severity
5521
+ };
5522
+ });
5523
+ return [
5524
+ {
5525
+ category: this.category,
5526
+ scope: "project",
5527
+ value: cycles.length,
5528
+ violations,
5529
+ metadata: { largestCycle, cycleCount: cycles.length }
5530
+ }
5531
+ ];
5532
+ }
5533
+ };
5534
+
5535
+ // src/architecture/collectors/layer-violations.ts
5536
+ var import_node_path4 = require("path");
5537
+ var LayerViolationCollector = class {
5538
+ category = "layer-violations";
5539
+ getRules(_config, _rootDir) {
5540
+ const description = "No layer boundary violations allowed";
5541
+ return [
5542
+ {
5543
+ id: constraintRuleId(this.category, "project", description),
5544
+ category: this.category,
5545
+ description,
5546
+ scope: "project"
5547
+ }
5548
+ ];
5549
+ }
5550
+ async collect(_config, rootDir) {
5551
+ const stubParser = {
5552
+ name: "typescript",
5553
+ extensions: [".ts", ".tsx"],
5554
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "" } }),
5555
+ extractImports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5556
+ extractExports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5557
+ health: async () => ({ ok: true, value: { available: true } })
5558
+ };
5559
+ const result = await validateDependencies({
5560
+ layers: [],
5561
+ rootDir,
5562
+ parser: stubParser,
5563
+ fallbackBehavior: "skip"
5564
+ });
5565
+ if (!result.ok) {
5566
+ return [
5567
+ {
5568
+ category: this.category,
5569
+ scope: "project",
5570
+ value: 0,
5571
+ violations: [],
5572
+ metadata: { error: "Failed to validate dependencies" }
5573
+ }
5574
+ ];
5575
+ }
5576
+ const layerViolations = result.value.violations.filter(
5577
+ (v) => v.reason === "WRONG_LAYER"
5578
+ );
5579
+ const violations = layerViolations.map((v) => {
5580
+ const relFile = (0, import_node_path4.relative)(rootDir, v.file);
5581
+ const relImport = (0, import_node_path4.relative)(rootDir, v.imports);
5582
+ const detail = `${v.fromLayer} -> ${v.toLayer}: ${relFile} imports ${relImport}`;
5583
+ return {
5584
+ id: violationId(relFile, this.category, detail),
5585
+ file: relFile,
5586
+ category: this.category,
5587
+ detail,
5588
+ severity: "error"
5589
+ };
5590
+ });
5591
+ return [
5592
+ {
5593
+ category: this.category,
5594
+ scope: "project",
5595
+ value: violations.length,
5596
+ violations
5597
+ }
5598
+ ];
5599
+ }
5600
+ };
5601
+
5602
+ // src/architecture/collectors/complexity.ts
5603
+ var import_node_path5 = require("path");
5604
+ var ComplexityCollector = class {
5605
+ category = "complexity";
5606
+ getRules(_config, _rootDir) {
5607
+ const description = "Cyclomatic complexity must stay within thresholds";
5608
+ return [
5609
+ {
5610
+ id: constraintRuleId(this.category, "project", description),
5611
+ category: this.category,
5612
+ description,
5613
+ scope: "project"
5614
+ }
5615
+ ];
5616
+ }
5617
+ async collect(_config, rootDir) {
5618
+ const files = await findFiles("**/*.ts", rootDir);
5619
+ const snapshot = {
5620
+ files: files.map((f) => ({
5621
+ path: f,
5622
+ ast: { type: "Program", body: null, language: "typescript" },
5623
+ imports: [],
5624
+ exports: [],
5625
+ internalSymbols: [],
5626
+ jsDocComments: []
5627
+ })),
5628
+ dependencyGraph: { nodes: [], edges: [] },
5629
+ exportMap: { byFile: /* @__PURE__ */ new Map(), byName: /* @__PURE__ */ new Map() },
5630
+ docs: [],
5631
+ codeReferences: [],
5632
+ entryPoints: [],
5633
+ rootDir,
5634
+ config: { rootDir, analyze: {} },
5635
+ buildTime: 0
5636
+ };
5637
+ const complexityThreshold = _config.thresholds.complexity;
5638
+ const maxComplexity = typeof complexityThreshold === "number" ? complexityThreshold : complexityThreshold?.max ?? 15;
5639
+ const complexityConfig = {
5640
+ thresholds: {
5641
+ cyclomaticComplexity: {
5642
+ error: maxComplexity,
5643
+ warn: Math.floor(maxComplexity * 0.7)
5644
+ }
5645
+ }
5646
+ };
5647
+ const result = await detectComplexityViolations(snapshot, complexityConfig);
5648
+ if (!result.ok) {
5649
+ return [
5650
+ {
5651
+ category: this.category,
5652
+ scope: "project",
5653
+ value: 0,
5654
+ violations: [],
5655
+ metadata: { error: "Failed to detect complexity violations" }
5656
+ }
5657
+ ];
5658
+ }
5659
+ const { violations: complexityViolations, stats } = result.value;
5660
+ const filtered = complexityViolations.filter(
5661
+ (v) => v.severity === "error" || v.severity === "warning"
5662
+ );
5663
+ const violations = filtered.map((v) => {
5664
+ const relFile = (0, import_node_path5.relative)(rootDir, v.file);
5665
+ const idDetail = `${v.metric}:${v.function}`;
5666
+ return {
5667
+ id: violationId(relFile, this.category, idDetail),
5668
+ file: relFile,
5669
+ category: this.category,
5670
+ detail: `${v.metric}=${v.value} in ${v.function} (threshold: ${v.threshold})`,
5671
+ severity: v.severity
5672
+ };
5673
+ });
5674
+ return [
5675
+ {
5676
+ category: this.category,
5677
+ scope: "project",
5678
+ value: violations.length,
5679
+ violations,
5680
+ metadata: {
5681
+ filesAnalyzed: stats.filesAnalyzed,
5682
+ functionsAnalyzed: stats.functionsAnalyzed
5683
+ }
5684
+ }
5685
+ ];
5686
+ }
5687
+ };
5688
+
5689
+ // src/architecture/collectors/coupling.ts
5690
+ var import_node_path6 = require("path");
5691
+ var CouplingCollector = class {
5692
+ category = "coupling";
5693
+ getRules(_config, _rootDir) {
5694
+ const description = "Coupling metrics must stay within thresholds";
5695
+ return [
5696
+ {
5697
+ id: constraintRuleId(this.category, "project", description),
5698
+ category: this.category,
5699
+ description,
5700
+ scope: "project"
5701
+ }
5702
+ ];
5703
+ }
5704
+ async collect(_config, rootDir) {
5705
+ const files = await findFiles("**/*.ts", rootDir);
5706
+ const snapshot = {
5707
+ files: files.map((f) => ({
5708
+ path: f,
5709
+ ast: { type: "Program", body: null, language: "typescript" },
5710
+ imports: [],
5711
+ exports: [],
5712
+ internalSymbols: [],
5713
+ jsDocComments: []
5714
+ })),
5715
+ dependencyGraph: { nodes: [], edges: [] },
5716
+ exportMap: { byFile: /* @__PURE__ */ new Map(), byName: /* @__PURE__ */ new Map() },
5717
+ docs: [],
5718
+ codeReferences: [],
5719
+ entryPoints: [],
5720
+ rootDir,
5721
+ config: { rootDir, analyze: {} },
5722
+ buildTime: 0
5723
+ };
5724
+ const result = await detectCouplingViolations(snapshot);
5725
+ if (!result.ok) {
5726
+ return [
5727
+ {
5728
+ category: this.category,
5729
+ scope: "project",
5730
+ value: 0,
5731
+ violations: [],
5732
+ metadata: { error: "Failed to detect coupling violations" }
5733
+ }
5734
+ ];
5735
+ }
5736
+ const { violations: couplingViolations, stats } = result.value;
5737
+ const filtered = couplingViolations.filter(
5738
+ (v) => v.severity === "error" || v.severity === "warning"
5739
+ );
5740
+ const violations = filtered.map((v) => {
5741
+ const relFile = (0, import_node_path6.relative)(rootDir, v.file);
5742
+ const idDetail = `${v.metric}`;
5743
+ return {
5744
+ id: violationId(relFile, this.category, idDetail),
5745
+ file: relFile,
5746
+ category: this.category,
5747
+ detail: `${v.metric}=${v.value} (threshold: ${v.threshold})`,
5748
+ severity: v.severity
5749
+ };
5750
+ });
5751
+ return [
5752
+ {
5753
+ category: this.category,
5754
+ scope: "project",
5755
+ value: violations.length,
5756
+ violations,
5757
+ metadata: { filesAnalyzed: stats.filesAnalyzed }
5758
+ }
5759
+ ];
5760
+ }
5761
+ };
5762
+
5763
+ // src/architecture/collectors/forbidden-imports.ts
5764
+ var import_node_path7 = require("path");
5765
+ var ForbiddenImportCollector = class {
5766
+ category = "forbidden-imports";
5767
+ getRules(_config, _rootDir) {
5768
+ const description = "No forbidden imports allowed";
5769
+ return [
5770
+ {
5771
+ id: constraintRuleId(this.category, "project", description),
5772
+ category: this.category,
5773
+ description,
5774
+ scope: "project"
5775
+ }
5776
+ ];
5777
+ }
5778
+ async collect(_config, rootDir) {
5779
+ const stubParser = {
5780
+ name: "typescript",
5781
+ extensions: [".ts", ".tsx"],
5782
+ parseFile: async () => ({ ok: false, error: { code: "PARSE_ERROR", message: "" } }),
5783
+ extractImports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5784
+ extractExports: () => ({ ok: false, error: { code: "EXTRACT_ERROR", message: "" } }),
5785
+ health: async () => ({ ok: true, value: { available: true } })
5786
+ };
5787
+ const result = await validateDependencies({
5788
+ layers: [],
5789
+ rootDir,
5790
+ parser: stubParser,
5791
+ fallbackBehavior: "skip"
5792
+ });
5793
+ if (!result.ok) {
5794
+ return [
5795
+ {
5796
+ category: this.category,
5797
+ scope: "project",
5798
+ value: 0,
5799
+ violations: [],
5800
+ metadata: { error: "Failed to validate dependencies" }
5801
+ }
5802
+ ];
5803
+ }
5804
+ const forbidden = result.value.violations.filter(
5805
+ (v) => v.reason === "FORBIDDEN_IMPORT"
5806
+ );
5807
+ const violations = forbidden.map((v) => {
5808
+ const relFile = (0, import_node_path7.relative)(rootDir, v.file);
5809
+ const relImport = (0, import_node_path7.relative)(rootDir, v.imports);
5810
+ const detail = `forbidden import: ${relFile} -> ${relImport}`;
5811
+ return {
5812
+ id: violationId(relFile, this.category, detail),
5813
+ file: relFile,
5814
+ category: this.category,
5815
+ detail,
5816
+ severity: "error"
5817
+ };
5818
+ });
5819
+ return [
5820
+ {
5821
+ category: this.category,
5822
+ scope: "project",
5823
+ value: violations.length,
5824
+ violations
5825
+ }
5826
+ ];
5827
+ }
5828
+ };
5829
+
5830
+ // src/architecture/collectors/module-size.ts
5831
+ var import_promises2 = require("fs/promises");
5832
+ var import_node_path8 = require("path");
5833
+ async function discoverModules(rootDir) {
5834
+ const modules = [];
5835
+ async function scanDir(dir) {
5836
+ let entries;
5837
+ try {
5838
+ entries = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
5839
+ } catch {
5840
+ return;
5841
+ }
5842
+ const tsFiles = [];
5843
+ const subdirs = [];
5844
+ for (const entry of entries) {
5845
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist") {
5846
+ continue;
5847
+ }
5848
+ const fullPath = (0, import_node_path8.join)(dir, entry.name);
5849
+ if (entry.isDirectory()) {
5850
+ subdirs.push(fullPath);
5851
+ } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".test.tsx") && !entry.name.endsWith(".spec.ts")) {
5852
+ tsFiles.push(fullPath);
5853
+ }
5854
+ }
5855
+ if (tsFiles.length > 0) {
5856
+ let totalLoc = 0;
5857
+ for (const f of tsFiles) {
5858
+ try {
5859
+ const content = await (0, import_promises2.readFile)(f, "utf-8");
5860
+ totalLoc += content.split("\n").filter((line) => line.trim().length > 0).length;
5861
+ } catch {
5862
+ }
5863
+ }
5864
+ modules.push({
5865
+ modulePath: (0, import_node_path8.relative)(rootDir, dir),
5866
+ fileCount: tsFiles.length,
5867
+ totalLoc,
5868
+ files: tsFiles.map((f) => (0, import_node_path8.relative)(rootDir, f))
5869
+ });
5870
+ }
5871
+ for (const sub of subdirs) {
5872
+ await scanDir(sub);
5873
+ }
5874
+ }
5875
+ await scanDir(rootDir);
5876
+ return modules;
5877
+ }
5878
+ var ModuleSizeCollector = class {
5879
+ category = "module-size";
5880
+ getRules(config, _rootDir) {
5881
+ const thresholds = config.thresholds["module-size"];
5882
+ let maxLoc = Infinity;
5883
+ let maxFiles = Infinity;
5884
+ if (typeof thresholds === "object" && thresholds !== null) {
5885
+ const t = thresholds;
5886
+ if (t.maxLoc !== void 0) maxLoc = t.maxLoc;
5887
+ if (t.maxFiles !== void 0) maxFiles = t.maxFiles;
5888
+ }
5889
+ const rules = [];
5890
+ if (maxLoc < Infinity) {
5891
+ const desc = `Module LOC must not exceed ${maxLoc}`;
5892
+ rules.push({
5893
+ id: constraintRuleId(this.category, "project", desc),
5894
+ category: this.category,
5895
+ description: desc,
5896
+ scope: "project"
5897
+ });
5898
+ }
5899
+ if (maxFiles < Infinity) {
5900
+ const desc = `Module file count must not exceed ${maxFiles}`;
5901
+ rules.push({
5902
+ id: constraintRuleId(this.category, "project", desc),
5903
+ category: this.category,
5904
+ description: desc,
5905
+ scope: "project"
5906
+ });
5907
+ }
5908
+ if (rules.length === 0) {
5909
+ const desc = "Module size must stay within thresholds";
5910
+ rules.push({
5911
+ id: constraintRuleId(this.category, "project", desc),
5912
+ category: this.category,
5913
+ description: desc,
5914
+ scope: "project"
5915
+ });
5916
+ }
5917
+ return rules;
5918
+ }
5919
+ async collect(config, rootDir) {
5920
+ const modules = await discoverModules(rootDir);
5921
+ const thresholds = config.thresholds["module-size"];
5922
+ let maxLoc = Infinity;
5923
+ let maxFiles = Infinity;
5924
+ if (typeof thresholds === "object" && thresholds !== null) {
5925
+ const t = thresholds;
5926
+ if (t.maxLoc !== void 0) maxLoc = t.maxLoc;
5927
+ if (t.maxFiles !== void 0) maxFiles = t.maxFiles;
5928
+ }
5929
+ return modules.map((mod) => {
5930
+ const violations = [];
5931
+ if (mod.totalLoc > maxLoc) {
5932
+ violations.push({
5933
+ id: violationId(mod.modulePath, this.category, "totalLoc-exceeded"),
5934
+ file: mod.modulePath,
5935
+ detail: `Module has ${mod.totalLoc} lines of code (threshold: ${maxLoc})`,
5936
+ severity: "warning"
5937
+ });
5938
+ }
5939
+ if (mod.fileCount > maxFiles) {
5940
+ violations.push({
5941
+ id: violationId(mod.modulePath, this.category, "fileCount-exceeded"),
5942
+ file: mod.modulePath,
5943
+ detail: `Module has ${mod.fileCount} files (threshold: ${maxFiles})`,
5944
+ severity: "warning"
5945
+ });
5946
+ }
5947
+ return {
5948
+ category: this.category,
5949
+ scope: mod.modulePath,
5950
+ value: mod.totalLoc,
5951
+ violations,
5952
+ metadata: { fileCount: mod.fileCount, totalLoc: mod.totalLoc }
5953
+ };
5954
+ });
5955
+ }
5956
+ };
5957
+
5958
+ // src/architecture/collectors/dep-depth.ts
5959
+ var import_promises3 = require("fs/promises");
5960
+ var import_node_path9 = require("path");
5961
+ function extractImportSources(content, filePath) {
5962
+ const importRegex = /(?:import|export)\s+.*?from\s+['"](\.[^'"]+)['"]/g;
5963
+ const dynamicRegex = /import\s*\(\s*['"](\.[^'"]+)['"]\s*\)/g;
5964
+ const sources = [];
5965
+ const dir = (0, import_node_path9.dirname)(filePath);
5966
+ for (const regex of [importRegex, dynamicRegex]) {
5967
+ let match;
5968
+ while ((match = regex.exec(content)) !== null) {
5969
+ let resolved = (0, import_node_path9.resolve)(dir, match[1]);
5970
+ if (!resolved.endsWith(".ts") && !resolved.endsWith(".tsx")) {
5971
+ resolved += ".ts";
5972
+ }
5973
+ sources.push(resolved);
5974
+ }
5975
+ }
5976
+ return sources;
5977
+ }
5978
+ async function collectTsFiles(dir) {
5979
+ const results = [];
5980
+ async function scan(d) {
5981
+ let entries;
5982
+ try {
5983
+ entries = await (0, import_promises3.readdir)(d, { withFileTypes: true });
5984
+ } catch {
5985
+ return;
5986
+ }
5987
+ for (const entry of entries) {
5988
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist")
5989
+ continue;
5990
+ const fullPath = (0, import_node_path9.join)(d, entry.name);
5991
+ if (entry.isDirectory()) {
5992
+ await scan(fullPath);
5993
+ } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) && !entry.name.endsWith(".test.ts") && !entry.name.endsWith(".test.tsx") && !entry.name.endsWith(".spec.ts")) {
5994
+ results.push(fullPath);
5995
+ }
5996
+ }
5997
+ }
5998
+ await scan(dir);
5999
+ return results;
6000
+ }
6001
+ function computeLongestChain(file, graph, visited, memo) {
6002
+ if (memo.has(file)) return memo.get(file);
6003
+ if (visited.has(file)) return 0;
6004
+ visited.add(file);
6005
+ const deps = graph.get(file) || [];
6006
+ let maxDepth = 0;
6007
+ for (const dep of deps) {
6008
+ const depth = 1 + computeLongestChain(dep, graph, visited, memo);
6009
+ if (depth > maxDepth) maxDepth = depth;
6010
+ }
6011
+ visited.delete(file);
6012
+ memo.set(file, maxDepth);
6013
+ return maxDepth;
6014
+ }
6015
+ var DepDepthCollector = class {
6016
+ category = "dependency-depth";
6017
+ getRules(config, _rootDir) {
6018
+ const threshold = typeof config.thresholds["dependency-depth"] === "number" ? config.thresholds["dependency-depth"] : null;
6019
+ const desc = threshold !== null ? `Dependency chain depth must not exceed ${threshold}` : "Dependency chain depth must stay within thresholds";
6020
+ return [
6021
+ {
6022
+ id: constraintRuleId(this.category, "project", desc),
6023
+ category: this.category,
6024
+ description: desc,
6025
+ scope: "project"
6026
+ }
6027
+ ];
6028
+ }
6029
+ async collect(config, rootDir) {
6030
+ const allFiles = await collectTsFiles(rootDir);
6031
+ const graph = /* @__PURE__ */ new Map();
6032
+ const fileSet = new Set(allFiles);
6033
+ for (const file of allFiles) {
6034
+ try {
6035
+ const content = await (0, import_promises3.readFile)(file, "utf-8");
6036
+ const imports = extractImportSources(content, file).filter((imp) => fileSet.has(imp));
6037
+ graph.set(file, imports);
6038
+ } catch {
6039
+ graph.set(file, []);
6040
+ }
6041
+ }
6042
+ const moduleMap = /* @__PURE__ */ new Map();
6043
+ for (const file of allFiles) {
6044
+ const relDir = (0, import_node_path9.relative)(rootDir, (0, import_node_path9.dirname)(file));
6045
+ if (!moduleMap.has(relDir)) moduleMap.set(relDir, []);
6046
+ moduleMap.get(relDir).push(file);
6047
+ }
6048
+ const memo = /* @__PURE__ */ new Map();
6049
+ const threshold = typeof config.thresholds["dependency-depth"] === "number" ? config.thresholds["dependency-depth"] : Infinity;
6050
+ const results = [];
6051
+ for (const [modulePath, files] of moduleMap) {
6052
+ let longestChain = 0;
6053
+ for (const file of files) {
6054
+ const depth = computeLongestChain(file, graph, /* @__PURE__ */ new Set(), memo);
6055
+ if (depth > longestChain) longestChain = depth;
6056
+ }
6057
+ const violations = [];
6058
+ if (longestChain > threshold) {
6059
+ violations.push({
6060
+ id: violationId(modulePath, this.category, "depth-exceeded"),
6061
+ file: modulePath,
6062
+ detail: `Import chain depth is ${longestChain} (threshold: ${threshold})`,
6063
+ severity: "warning"
6064
+ });
6065
+ }
6066
+ results.push({
6067
+ category: this.category,
6068
+ scope: modulePath,
6069
+ value: longestChain,
6070
+ violations,
6071
+ metadata: { longestChain }
6072
+ });
6073
+ }
6074
+ return results;
6075
+ }
6076
+ };
6077
+
6078
+ // src/architecture/collectors/index.ts
6079
+ var defaultCollectors = [
6080
+ new CircularDepsCollector(),
6081
+ new LayerViolationCollector(),
6082
+ new ComplexityCollector(),
6083
+ new CouplingCollector(),
6084
+ new ForbiddenImportCollector(),
6085
+ new ModuleSizeCollector(),
6086
+ new DepDepthCollector()
6087
+ ];
6088
+ async function runAll(config, rootDir, collectors = defaultCollectors) {
6089
+ const results = await Promise.allSettled(collectors.map((c) => c.collect(config, rootDir)));
6090
+ const allResults = [];
6091
+ for (let i = 0; i < results.length; i++) {
6092
+ const result = results[i];
6093
+ if (result.status === "fulfilled") {
6094
+ allResults.push(...result.value);
6095
+ } else {
6096
+ allResults.push({
6097
+ category: collectors[i].category,
6098
+ scope: "project",
6099
+ value: 0,
6100
+ violations: [],
6101
+ metadata: { error: String(result.reason) }
6102
+ });
6103
+ }
6104
+ }
6105
+ return allResults;
6106
+ }
6107
+
6108
+ // src/architecture/sync-constraints.ts
6109
+ function syncConstraintNodes(store, rules, violations) {
6110
+ const now = (/* @__PURE__ */ new Date()).toISOString();
6111
+ const ruleIds = new Set(rules.map((r) => r.id));
6112
+ const violationsByCategory = /* @__PURE__ */ new Map();
6113
+ for (const result of violations) {
6114
+ const files = result.violations.map((v) => v.file);
6115
+ const existing = violationsByCategory.get(result.category) ?? [];
6116
+ violationsByCategory.set(result.category, [...existing, ...files]);
6117
+ }
6118
+ const existingNodesById = /* @__PURE__ */ new Map();
6119
+ for (const node of store.findNodes({ type: "constraint" })) {
6120
+ existingNodesById.set(node.id, node);
6121
+ }
6122
+ for (const rule of rules) {
6123
+ const existing = existingNodesById.get(rule.id);
6124
+ const createdAt = existing?.createdAt ?? now;
6125
+ const previousLastViolatedAt = existing?.lastViolatedAt ?? null;
6126
+ const hasViolation = hasMatchingViolation(rule, violationsByCategory);
6127
+ const lastViolatedAt = hasViolation ? now : previousLastViolatedAt;
6128
+ store.upsertNode({
6129
+ id: rule.id,
6130
+ type: "constraint",
6131
+ name: rule.description,
6132
+ category: rule.category,
6133
+ scope: rule.scope,
6134
+ createdAt,
6135
+ lastViolatedAt
6136
+ });
6137
+ }
6138
+ const existingConstraints = store.findNodes({ type: "constraint" });
6139
+ for (const node of existingConstraints) {
6140
+ if (!ruleIds.has(node.id)) {
6141
+ store.removeNode(node.id);
6142
+ }
6143
+ }
6144
+ }
6145
+ function hasMatchingViolation(rule, violationsByCategory) {
6146
+ const files = violationsByCategory.get(rule.category);
6147
+ if (!files || files.length === 0) return false;
6148
+ if (rule.scope === "project") return true;
6149
+ return files.some((file) => file.startsWith(rule.scope));
6150
+ }
6151
+
6152
+ // src/architecture/detect-stale.ts
6153
+ function detectStaleConstraints(store, windowDays = 30, category) {
6154
+ const now = Date.now();
6155
+ const windowMs = windowDays * 24 * 60 * 60 * 1e3;
6156
+ const cutoff = now - windowMs;
6157
+ let constraints = store.findNodes({ type: "constraint" });
6158
+ if (category) {
6159
+ constraints = constraints.filter((n) => n.category === category);
6160
+ }
6161
+ const totalConstraints = constraints.length;
6162
+ const staleConstraints = [];
6163
+ for (const node of constraints) {
6164
+ const lastViolatedAt = node.lastViolatedAt ?? null;
6165
+ const createdAt = node.createdAt;
6166
+ const comparisonTimestamp = lastViolatedAt ?? createdAt;
6167
+ if (!comparisonTimestamp) continue;
6168
+ const timestampMs = new Date(comparisonTimestamp).getTime();
6169
+ if (timestampMs < cutoff) {
6170
+ const daysSince = Math.floor((now - timestampMs) / (24 * 60 * 60 * 1e3));
6171
+ staleConstraints.push({
6172
+ id: node.id,
6173
+ category: node.category,
6174
+ description: node.name ?? "",
6175
+ scope: node.scope ?? "project",
6176
+ lastViolatedAt,
6177
+ daysSinceLastViolation: daysSince
6178
+ });
6179
+ }
6180
+ }
6181
+ staleConstraints.sort((a, b) => b.daysSinceLastViolation - a.daysSinceLastViolation);
6182
+ return { staleConstraints, totalConstraints, windowDays };
6183
+ }
6184
+
6185
+ // src/architecture/baseline-manager.ts
6186
+ var import_node_fs3 = require("fs");
6187
+ var import_node_crypto2 = require("crypto");
6188
+ var import_node_path10 = require("path");
6189
+ var ArchBaselineManager = class {
6190
+ baselinesPath;
6191
+ constructor(projectRoot, baselinePath) {
6192
+ this.baselinesPath = baselinePath ? (0, import_node_path10.join)(projectRoot, baselinePath) : (0, import_node_path10.join)(projectRoot, ".harness", "arch", "baselines.json");
6193
+ }
6194
+ /**
6195
+ * Snapshot the current metric results into an ArchBaseline.
6196
+ * Aggregates multiple MetricResults for the same category by summing values
6197
+ * and concatenating violation IDs.
6198
+ */
6199
+ capture(results, commitHash) {
6200
+ const metrics = {};
6201
+ for (const result of results) {
6202
+ const existing = metrics[result.category];
6203
+ if (existing) {
6204
+ existing.value += result.value;
6205
+ existing.violationIds.push(...result.violations.map((v) => v.id));
6206
+ } else {
6207
+ metrics[result.category] = {
6208
+ value: result.value,
6209
+ violationIds: result.violations.map((v) => v.id)
6210
+ };
6211
+ }
6212
+ }
6213
+ return {
6214
+ version: 1,
6215
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6216
+ updatedFrom: commitHash,
6217
+ metrics
6218
+ };
6219
+ }
6220
+ /**
6221
+ * Load the baselines file from disk.
6222
+ * Returns null if the file does not exist, contains invalid JSON,
6223
+ * or fails ArchBaselineSchema validation.
6224
+ */
6225
+ load() {
6226
+ if (!(0, import_node_fs3.existsSync)(this.baselinesPath)) {
6227
+ console.error(`Baseline file not found at: ${this.baselinesPath}`);
6228
+ return null;
6229
+ }
6230
+ try {
6231
+ const raw = (0, import_node_fs3.readFileSync)(this.baselinesPath, "utf-8");
6232
+ const data = JSON.parse(raw);
6233
+ const parsed = ArchBaselineSchema.safeParse(data);
6234
+ if (!parsed.success) {
6235
+ console.error(
6236
+ `Baseline validation failed for ${this.baselinesPath}:`,
6237
+ parsed.error.format()
6238
+ );
6239
+ return null;
6240
+ }
6241
+ return parsed.data;
6242
+ } catch (error) {
6243
+ console.error(`Error loading baseline from ${this.baselinesPath}:`, error);
6244
+ return null;
6245
+ }
6246
+ }
6247
+ /**
6248
+ * Save an ArchBaseline to disk.
6249
+ * Creates parent directories if they do not exist.
6250
+ * Uses atomic write (write to temp file, then rename) to prevent corruption.
6251
+ */
6252
+ save(baseline) {
6253
+ const dir = (0, import_node_path10.dirname)(this.baselinesPath);
6254
+ if (!(0, import_node_fs3.existsSync)(dir)) {
6255
+ (0, import_node_fs3.mkdirSync)(dir, { recursive: true });
6256
+ }
6257
+ const tmp = this.baselinesPath + "." + (0, import_node_crypto2.randomBytes)(4).toString("hex") + ".tmp";
6258
+ (0, import_node_fs3.writeFileSync)(tmp, JSON.stringify(baseline, null, 2));
6259
+ (0, import_node_fs3.renameSync)(tmp, this.baselinesPath);
6260
+ }
6261
+ };
6262
+
6263
+ // src/architecture/diff.ts
6264
+ function aggregateByCategory(results) {
6265
+ const map = /* @__PURE__ */ new Map();
6266
+ for (const result of results) {
6267
+ const existing = map.get(result.category);
6268
+ if (existing) {
6269
+ existing.value += result.value;
6270
+ existing.violations.push(...result.violations);
6271
+ } else {
6272
+ map.set(result.category, {
6273
+ value: result.value,
6274
+ violations: [...result.violations]
6275
+ });
6276
+ }
6277
+ }
6278
+ return map;
6279
+ }
6280
+ function diff(current, baseline) {
6281
+ const aggregated = aggregateByCategory(current);
6282
+ const newViolations = [];
6283
+ const resolvedViolations = [];
6284
+ const preExisting = [];
6285
+ const regressions = [];
6286
+ const visitedCategories = /* @__PURE__ */ new Set();
6287
+ for (const [category, agg] of aggregated) {
6288
+ visitedCategories.add(category);
6289
+ const baselineCategory = baseline.metrics[category];
6290
+ const baselineViolationIds = new Set(baselineCategory?.violationIds ?? []);
6291
+ const baselineValue = baselineCategory?.value ?? 0;
6292
+ for (const violation of agg.violations) {
6293
+ if (baselineViolationIds.has(violation.id)) {
6294
+ preExisting.push(violation.id);
6295
+ } else {
6296
+ newViolations.push(violation);
6297
+ }
6298
+ }
6299
+ const currentViolationIds = new Set(agg.violations.map((v) => v.id));
6300
+ if (baselineCategory) {
6301
+ for (const id of baselineCategory.violationIds) {
6302
+ if (!currentViolationIds.has(id)) {
6303
+ resolvedViolations.push(id);
6304
+ }
6305
+ }
6306
+ }
6307
+ if (baselineCategory && agg.value > baselineValue) {
6308
+ regressions.push({
6309
+ category,
6310
+ baselineValue,
6311
+ currentValue: agg.value,
6312
+ delta: agg.value - baselineValue
6313
+ });
6314
+ }
6315
+ }
6316
+ for (const [category, baselineCategory] of Object.entries(baseline.metrics)) {
6317
+ if (!visitedCategories.has(category) && baselineCategory) {
6318
+ for (const id of baselineCategory.violationIds) {
6319
+ resolvedViolations.push(id);
6320
+ }
6321
+ }
6322
+ }
6323
+ const passed = newViolations.length === 0 && regressions.length === 0;
6324
+ return {
6325
+ passed,
6326
+ newViolations,
6327
+ resolvedViolations,
6328
+ preExisting,
6329
+ regressions
6330
+ };
6331
+ }
6332
+
6333
+ // src/architecture/config.ts
6334
+ function resolveThresholds(scope, config) {
6335
+ const projectThresholds = {};
6336
+ for (const [key, val] of Object.entries(config.thresholds)) {
6337
+ projectThresholds[key] = typeof val === "object" && val !== null && !Array.isArray(val) ? { ...val } : val;
6338
+ }
6339
+ if (scope === "project") {
6340
+ return projectThresholds;
6341
+ }
6342
+ const moduleOverrides = config.modules[scope];
6343
+ if (!moduleOverrides) {
6344
+ return projectThresholds;
6345
+ }
6346
+ const merged = { ...projectThresholds };
6347
+ for (const [category, moduleValue] of Object.entries(moduleOverrides)) {
6348
+ const projectValue = projectThresholds[category];
6349
+ if (projectValue !== void 0 && typeof projectValue === "object" && !Array.isArray(projectValue) && typeof moduleValue === "object" && !Array.isArray(moduleValue)) {
6350
+ merged[category] = {
6351
+ ...projectValue,
6352
+ ...moduleValue
6353
+ };
6354
+ } else {
6355
+ merged[category] = moduleValue;
6356
+ }
6357
+ }
6358
+ return merged;
6359
+ }
6360
+
6361
+ // src/architecture/matchers.ts
6362
+ function architecture(options) {
6363
+ return {
6364
+ kind: "arch-handle",
6365
+ scope: "project",
6366
+ rootDir: options?.rootDir ?? process.cwd(),
6367
+ config: options?.config
6368
+ };
6369
+ }
6370
+ function archModule(modulePath, options) {
6371
+ return {
6372
+ kind: "arch-handle",
6373
+ scope: modulePath,
6374
+ rootDir: options?.rootDir ?? process.cwd(),
6375
+ config: options?.config
6376
+ };
6377
+ }
6378
+ function resolveConfig(handle) {
6379
+ return ArchConfigSchema.parse(handle.config ?? {});
6380
+ }
6381
+ async function collectCategory(handle, collector) {
6382
+ if ("_mockResults" in handle && handle._mockResults) {
6383
+ return handle._mockResults;
6384
+ }
6385
+ const config = resolveConfig(handle);
6386
+ return collector.collect(config, handle.rootDir);
6387
+ }
6388
+ function formatViolationList(violations, limit = 10) {
6389
+ const lines = violations.slice(0, limit).map((v) => ` - ${v.file}: ${v.detail}`);
6390
+ if (violations.length > limit) {
6391
+ lines.push(` ... and ${violations.length - limit} more`);
6392
+ }
6393
+ return lines.join("\n");
6394
+ }
6395
+ async function toHaveNoCircularDeps(received) {
6396
+ const results = await collectCategory(received, new CircularDepsCollector());
6397
+ const violations = results.flatMap((r) => r.violations);
6398
+ const pass = violations.length === 0;
6399
+ return {
6400
+ pass,
6401
+ message: () => pass ? "Expected circular dependencies but found none" : `Found ${violations.length} circular dependenc${violations.length === 1 ? "y" : "ies"}:
6402
+ ${formatViolationList(violations)}`
6403
+ };
6404
+ }
6405
+ async function toHaveNoLayerViolations(received) {
6406
+ const results = await collectCategory(received, new LayerViolationCollector());
6407
+ const violations = results.flatMap((r) => r.violations);
6408
+ const pass = violations.length === 0;
6409
+ return {
6410
+ pass,
6411
+ message: () => pass ? "Expected layer violations but found none" : `Found ${violations.length} layer violation${violations.length === 1 ? "" : "s"}:
6412
+ ${formatViolationList(violations)}`
6413
+ };
6414
+ }
6415
+ async function toMatchBaseline(received, options) {
6416
+ let diffResult;
6417
+ if ("_mockDiff" in received && received._mockDiff) {
6418
+ diffResult = received._mockDiff;
6419
+ } else {
6420
+ const config = resolveConfig(received);
6421
+ const results = await runAll(config, received.rootDir);
6422
+ const manager = new ArchBaselineManager(received.rootDir, config.baselinePath);
6423
+ const baseline = manager.load();
6424
+ if (!baseline) {
6425
+ return {
6426
+ pass: false,
6427
+ message: () => "No baseline found. Run `harness check-arch --update-baseline` to create one."
6428
+ };
6429
+ }
6430
+ diffResult = diff(results, baseline);
6431
+ }
6432
+ const tolerance = options?.tolerance ?? 0;
6433
+ const effectiveNewCount = Math.max(0, diffResult.newViolations.length - tolerance);
6434
+ const pass = effectiveNewCount === 0 && diffResult.regressions.length === 0;
6435
+ return {
6436
+ pass,
6437
+ message: () => {
6438
+ if (pass) {
6439
+ return "Expected baseline regression but architecture matches baseline";
6440
+ }
6441
+ const parts = [];
6442
+ if (diffResult.newViolations.length > 0) {
6443
+ parts.push(
6444
+ `${diffResult.newViolations.length} new violation${diffResult.newViolations.length === 1 ? "" : "s"}${tolerance > 0 ? ` (tolerance: ${tolerance})` : ""}:
6445
+ ${formatViolationList(diffResult.newViolations)}`
6446
+ );
6447
+ }
6448
+ if (diffResult.regressions.length > 0) {
6449
+ const regLines = diffResult.regressions.map(
6450
+ (r) => ` - ${r.category}: ${r.baselineValue} -> ${r.currentValue} (+${r.delta})`
6451
+ );
6452
+ parts.push(`Regressions:
6453
+ ${regLines.join("\n")}`);
6454
+ }
6455
+ return `Baseline check failed:
6456
+ ${parts.join("\n\n")}`;
6457
+ }
6458
+ };
6459
+ }
6460
+ function filterByScope(results, scope) {
6461
+ return results.filter(
6462
+ (r) => r.scope === scope || r.scope.startsWith(scope + "/") || r.scope === "project"
6463
+ );
6464
+ }
6465
+ async function toHaveMaxComplexity(received, maxComplexity) {
6466
+ const results = await collectCategory(received, new ComplexityCollector());
6467
+ const scoped = filterByScope(results, received.scope);
6468
+ const violations = scoped.flatMap((r) => r.violations);
6469
+ const totalValue = scoped.reduce((sum, r) => sum + r.value, 0);
6470
+ const pass = totalValue <= maxComplexity && violations.length === 0;
6471
+ return {
6472
+ pass,
6473
+ message: () => pass ? `Expected complexity to exceed ${maxComplexity} but it was within limits` : `Module '${received.scope}' has complexity violations (${violations.length} violation${violations.length === 1 ? "" : "s"}):
6474
+ ${formatViolationList(violations)}`
6475
+ };
6476
+ }
6477
+ async function toHaveMaxCoupling(received, limits) {
6478
+ const config = resolveConfig(received);
6479
+ if (limits.fanIn !== void 0 || limits.fanOut !== void 0) {
6480
+ config.thresholds.coupling = {
6481
+ ...typeof config.thresholds.coupling === "object" ? config.thresholds.coupling : {},
6482
+ ...limits.fanIn !== void 0 ? { maxFanIn: limits.fanIn } : {},
6483
+ ...limits.fanOut !== void 0 ? { maxFanOut: limits.fanOut } : {}
6484
+ };
6485
+ }
6486
+ const collector = new CouplingCollector();
6487
+ const results = "_mockResults" in received && received._mockResults ? received._mockResults : await collector.collect(config, received.rootDir);
6488
+ const scoped = filterByScope(results, received.scope);
6489
+ const violations = scoped.flatMap((r) => r.violations);
6490
+ const pass = violations.length === 0;
6491
+ return {
6492
+ pass,
6493
+ message: () => pass ? `Expected coupling violations in '${received.scope}' but found none` : `Module '${received.scope}' has ${violations.length} coupling violation${violations.length === 1 ? "" : "s"} (fanIn limit: ${limits.fanIn ?? "none"}, fanOut limit: ${limits.fanOut ?? "none"}):
6494
+ ${formatViolationList(violations)}`
6495
+ };
6496
+ }
6497
+ async function toHaveMaxFileCount(received, maxFiles) {
6498
+ const results = await collectCategory(received, new ModuleSizeCollector());
6499
+ const scoped = filterByScope(results, received.scope);
6500
+ const fileCount = scoped.reduce((max, r) => {
6501
+ const meta = r.metadata;
6502
+ const fc = typeof meta?.fileCount === "number" ? meta.fileCount : 0;
6503
+ return fc > max ? fc : max;
6504
+ }, 0);
6505
+ const pass = fileCount <= maxFiles;
6506
+ return {
6507
+ pass,
6508
+ message: () => pass ? `Expected file count in '${received.scope}' to exceed ${maxFiles} but it was ${fileCount}` : `Module '${received.scope}' has ${fileCount} files (limit: ${maxFiles})`
6509
+ };
6510
+ }
6511
+ async function toNotDependOn(received, forbiddenModule) {
6512
+ const results = await collectCategory(received, new ForbiddenImportCollector());
6513
+ const allViolations = results.flatMap((r) => r.violations);
6514
+ const scopePrefix = received.scope.replace(/\/+$/, "");
6515
+ const forbiddenPrefix = forbiddenModule.replace(/\/+$/, "");
6516
+ const relevantViolations = allViolations.filter(
6517
+ (v) => (v.file === scopePrefix || v.file.startsWith(scopePrefix + "/")) && (v.detail.includes(forbiddenPrefix + "/") || v.detail.endsWith(forbiddenPrefix))
6518
+ );
6519
+ const pass = relevantViolations.length === 0;
6520
+ return {
6521
+ pass,
6522
+ message: () => pass ? `Expected '${received.scope}' to depend on '${forbiddenModule}' but no such imports found` : `Module '${received.scope}' depends on '${forbiddenModule}' (${relevantViolations.length} import${relevantViolations.length === 1 ? "" : "s"}):
6523
+ ${formatViolationList(relevantViolations)}`
6524
+ };
6525
+ }
6526
+ async function toHaveMaxDepDepth(received, maxDepth) {
6527
+ const results = await collectCategory(received, new DepDepthCollector());
6528
+ const scoped = filterByScope(results, received.scope);
6529
+ const maxActual = scoped.reduce((max, r) => r.value > max ? r.value : max, 0);
6530
+ const pass = maxActual <= maxDepth;
6531
+ return {
6532
+ pass,
6533
+ message: () => pass ? `Expected dependency depth in '${received.scope}' to exceed ${maxDepth} but it was ${maxActual}` : `Module '${received.scope}' has dependency depth ${maxActual} (limit: ${maxDepth})`
6534
+ };
6535
+ }
6536
+ var archMatchers = {
6537
+ toHaveNoCircularDeps,
6538
+ toHaveNoLayerViolations,
6539
+ toMatchBaseline,
6540
+ toHaveMaxComplexity,
6541
+ toHaveMaxCoupling,
6542
+ toHaveMaxFileCount,
6543
+ toNotDependOn,
6544
+ toHaveMaxDepDepth
6545
+ };
6546
+
4894
6547
  // src/state/types.ts
4895
- var import_zod2 = require("zod");
4896
- var FailureEntrySchema = import_zod2.z.object({
4897
- date: import_zod2.z.string(),
4898
- skill: import_zod2.z.string(),
4899
- type: import_zod2.z.string(),
4900
- description: import_zod2.z.string()
6548
+ var import_zod4 = require("zod");
6549
+ var FailureEntrySchema = import_zod4.z.object({
6550
+ date: import_zod4.z.string(),
6551
+ skill: import_zod4.z.string(),
6552
+ type: import_zod4.z.string(),
6553
+ description: import_zod4.z.string()
4901
6554
  });
4902
- var HandoffSchema = import_zod2.z.object({
4903
- timestamp: import_zod2.z.string(),
4904
- fromSkill: import_zod2.z.string(),
4905
- phase: import_zod2.z.string(),
4906
- summary: import_zod2.z.string(),
4907
- completed: import_zod2.z.array(import_zod2.z.string()).default([]),
4908
- pending: import_zod2.z.array(import_zod2.z.string()).default([]),
4909
- concerns: import_zod2.z.array(import_zod2.z.string()).default([]),
4910
- decisions: import_zod2.z.array(
4911
- import_zod2.z.object({
4912
- what: import_zod2.z.string(),
4913
- why: import_zod2.z.string()
6555
+ var HandoffSchema = import_zod4.z.object({
6556
+ timestamp: import_zod4.z.string(),
6557
+ fromSkill: import_zod4.z.string(),
6558
+ phase: import_zod4.z.string(),
6559
+ summary: import_zod4.z.string(),
6560
+ completed: import_zod4.z.array(import_zod4.z.string()).default([]),
6561
+ pending: import_zod4.z.array(import_zod4.z.string()).default([]),
6562
+ concerns: import_zod4.z.array(import_zod4.z.string()).default([]),
6563
+ decisions: import_zod4.z.array(
6564
+ import_zod4.z.object({
6565
+ what: import_zod4.z.string(),
6566
+ why: import_zod4.z.string()
4914
6567
  })
4915
6568
  ).default([]),
4916
- blockers: import_zod2.z.array(import_zod2.z.string()).default([]),
4917
- contextKeywords: import_zod2.z.array(import_zod2.z.string()).default([])
6569
+ blockers: import_zod4.z.array(import_zod4.z.string()).default([]),
6570
+ contextKeywords: import_zod4.z.array(import_zod4.z.string()).default([])
4918
6571
  });
4919
- var GateCheckSchema = import_zod2.z.object({
4920
- name: import_zod2.z.string(),
4921
- passed: import_zod2.z.boolean(),
4922
- command: import_zod2.z.string(),
4923
- output: import_zod2.z.string().optional(),
4924
- duration: import_zod2.z.number().optional()
6572
+ var GateCheckSchema = import_zod4.z.object({
6573
+ name: import_zod4.z.string(),
6574
+ passed: import_zod4.z.boolean(),
6575
+ command: import_zod4.z.string(),
6576
+ output: import_zod4.z.string().optional(),
6577
+ duration: import_zod4.z.number().optional()
4925
6578
  });
4926
- var GateResultSchema = import_zod2.z.object({
4927
- passed: import_zod2.z.boolean(),
4928
- checks: import_zod2.z.array(GateCheckSchema)
6579
+ var GateResultSchema = import_zod4.z.object({
6580
+ passed: import_zod4.z.boolean(),
6581
+ checks: import_zod4.z.array(GateCheckSchema)
4929
6582
  });
4930
- var GateConfigSchema = import_zod2.z.object({
4931
- checks: import_zod2.z.array(
4932
- import_zod2.z.object({
4933
- name: import_zod2.z.string(),
4934
- command: import_zod2.z.string()
6583
+ var GateConfigSchema = import_zod4.z.object({
6584
+ checks: import_zod4.z.array(
6585
+ import_zod4.z.object({
6586
+ name: import_zod4.z.string(),
6587
+ command: import_zod4.z.string()
4935
6588
  })
4936
6589
  ).optional(),
4937
- trace: import_zod2.z.boolean().optional()
6590
+ trace: import_zod4.z.boolean().optional()
4938
6591
  });
4939
- var HarnessStateSchema = import_zod2.z.object({
4940
- schemaVersion: import_zod2.z.literal(1),
4941
- position: import_zod2.z.object({
4942
- phase: import_zod2.z.string().optional(),
4943
- task: import_zod2.z.string().optional()
6592
+ var HarnessStateSchema = import_zod4.z.object({
6593
+ schemaVersion: import_zod4.z.literal(1),
6594
+ position: import_zod4.z.object({
6595
+ phase: import_zod4.z.string().optional(),
6596
+ task: import_zod4.z.string().optional()
4944
6597
  }).default({}),
4945
- decisions: import_zod2.z.array(
4946
- import_zod2.z.object({
4947
- date: import_zod2.z.string(),
4948
- decision: import_zod2.z.string(),
4949
- context: import_zod2.z.string()
6598
+ decisions: import_zod4.z.array(
6599
+ import_zod4.z.object({
6600
+ date: import_zod4.z.string(),
6601
+ decision: import_zod4.z.string(),
6602
+ context: import_zod4.z.string()
4950
6603
  })
4951
6604
  ).default([]),
4952
- blockers: import_zod2.z.array(
4953
- import_zod2.z.object({
4954
- id: import_zod2.z.string(),
4955
- description: import_zod2.z.string(),
4956
- status: import_zod2.z.enum(["open", "resolved"])
6605
+ blockers: import_zod4.z.array(
6606
+ import_zod4.z.object({
6607
+ id: import_zod4.z.string(),
6608
+ description: import_zod4.z.string(),
6609
+ status: import_zod4.z.enum(["open", "resolved"])
4957
6610
  })
4958
6611
  ).default([]),
4959
- progress: import_zod2.z.record(import_zod2.z.enum(["pending", "in_progress", "complete"])).default({}),
4960
- lastSession: import_zod2.z.object({
4961
- date: import_zod2.z.string(),
4962
- summary: import_zod2.z.string(),
4963
- lastSkill: import_zod2.z.string().optional(),
4964
- pendingTasks: import_zod2.z.array(import_zod2.z.string()).optional()
6612
+ progress: import_zod4.z.record(import_zod4.z.enum(["pending", "in_progress", "complete"])).default({}),
6613
+ lastSession: import_zod4.z.object({
6614
+ date: import_zod4.z.string(),
6615
+ summary: import_zod4.z.string(),
6616
+ lastSkill: import_zod4.z.string().optional(),
6617
+ pendingTasks: import_zod4.z.array(import_zod4.z.string()).optional()
4965
6618
  }).optional()
4966
6619
  });
4967
6620
  var DEFAULT_STATE = {
@@ -4973,27 +6626,27 @@ var DEFAULT_STATE = {
4973
6626
  };
4974
6627
 
4975
6628
  // src/state/state-manager.ts
4976
- var fs4 = __toESM(require("fs"));
6629
+ var fs6 = __toESM(require("fs"));
4977
6630
  var path3 = __toESM(require("path"));
4978
6631
  var import_child_process2 = require("child_process");
4979
6632
 
4980
6633
  // src/state/stream-resolver.ts
4981
- var fs3 = __toESM(require("fs"));
6634
+ var fs5 = __toESM(require("fs"));
4982
6635
  var path2 = __toESM(require("path"));
4983
6636
  var import_child_process = require("child_process");
4984
6637
 
4985
6638
  // src/state/stream-types.ts
4986
- var import_zod3 = require("zod");
4987
- var StreamInfoSchema = import_zod3.z.object({
4988
- name: import_zod3.z.string(),
4989
- branch: import_zod3.z.string().optional(),
4990
- createdAt: import_zod3.z.string(),
4991
- lastActiveAt: import_zod3.z.string()
6639
+ var import_zod5 = require("zod");
6640
+ var StreamInfoSchema = import_zod5.z.object({
6641
+ name: import_zod5.z.string(),
6642
+ branch: import_zod5.z.string().optional(),
6643
+ createdAt: import_zod5.z.string(),
6644
+ lastActiveAt: import_zod5.z.string()
4992
6645
  });
4993
- var StreamIndexSchema = import_zod3.z.object({
4994
- schemaVersion: import_zod3.z.literal(1),
4995
- activeStream: import_zod3.z.string().nullable(),
4996
- streams: import_zod3.z.record(StreamInfoSchema)
6646
+ var StreamIndexSchema = import_zod5.z.object({
6647
+ schemaVersion: import_zod5.z.literal(1),
6648
+ activeStream: import_zod5.z.string().nullable(),
6649
+ streams: import_zod5.z.record(StreamInfoSchema)
4997
6650
  });
4998
6651
  var DEFAULT_STREAM_INDEX = {
4999
6652
  schemaVersion: 1,
@@ -5024,11 +6677,11 @@ function validateStreamName(name) {
5024
6677
  }
5025
6678
  async function loadStreamIndex(projectPath) {
5026
6679
  const idxPath = indexPath(projectPath);
5027
- if (!fs3.existsSync(idxPath)) {
6680
+ if (!fs5.existsSync(idxPath)) {
5028
6681
  return (0, import_types.Ok)({ ...DEFAULT_STREAM_INDEX, streams: {} });
5029
6682
  }
5030
6683
  try {
5031
- const raw = fs3.readFileSync(idxPath, "utf-8");
6684
+ const raw = fs5.readFileSync(idxPath, "utf-8");
5032
6685
  const parsed = JSON.parse(raw);
5033
6686
  const result = StreamIndexSchema.safeParse(parsed);
5034
6687
  if (!result.success) {
@@ -5046,8 +6699,8 @@ async function loadStreamIndex(projectPath) {
5046
6699
  async function saveStreamIndex(projectPath, index) {
5047
6700
  const dir = streamsDir(projectPath);
5048
6701
  try {
5049
- fs3.mkdirSync(dir, { recursive: true });
5050
- fs3.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
6702
+ fs5.mkdirSync(dir, { recursive: true });
6703
+ fs5.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
5051
6704
  return (0, import_types.Ok)(void 0);
5052
6705
  } catch (error) {
5053
6706
  return (0, import_types.Err)(
@@ -5129,7 +6782,7 @@ async function createStream(projectPath, name, branch) {
5129
6782
  }
5130
6783
  const streamPath = path2.join(streamsDir(projectPath), name);
5131
6784
  try {
5132
- fs3.mkdirSync(streamPath, { recursive: true });
6785
+ fs5.mkdirSync(streamPath, { recursive: true });
5133
6786
  } catch (error) {
5134
6787
  return (0, import_types.Err)(
5135
6788
  new Error(
@@ -5173,9 +6826,9 @@ async function archiveStream(projectPath, name) {
5173
6826
  const streamPath = path2.join(streamsDir(projectPath), name);
5174
6827
  const archiveDir = path2.join(projectPath, HARNESS_DIR, "archive", "streams");
5175
6828
  try {
5176
- fs3.mkdirSync(archiveDir, { recursive: true });
6829
+ fs5.mkdirSync(archiveDir, { recursive: true });
5177
6830
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5178
- fs3.renameSync(streamPath, path2.join(archiveDir, `${name}-${date}`));
6831
+ fs5.renameSync(streamPath, path2.join(archiveDir, `${name}-${date}`));
5179
6832
  } catch (error) {
5180
6833
  return (0, import_types.Err)(
5181
6834
  new Error(
@@ -5198,18 +6851,18 @@ function getStreamForBranch(index, branch) {
5198
6851
  var STATE_FILES = ["state.json", "handoff.json", "learnings.md", "failures.md"];
5199
6852
  async function migrateToStreams(projectPath) {
5200
6853
  const harnessDir = path2.join(projectPath, HARNESS_DIR);
5201
- if (fs3.existsSync(indexPath(projectPath))) {
6854
+ if (fs5.existsSync(indexPath(projectPath))) {
5202
6855
  return (0, import_types.Ok)(void 0);
5203
6856
  }
5204
- const filesToMove = STATE_FILES.filter((f) => fs3.existsSync(path2.join(harnessDir, f)));
6857
+ const filesToMove = STATE_FILES.filter((f) => fs5.existsSync(path2.join(harnessDir, f)));
5205
6858
  if (filesToMove.length === 0) {
5206
6859
  return (0, import_types.Ok)(void 0);
5207
6860
  }
5208
6861
  const defaultDir = path2.join(streamsDir(projectPath), "default");
5209
6862
  try {
5210
- fs3.mkdirSync(defaultDir, { recursive: true });
6863
+ fs5.mkdirSync(defaultDir, { recursive: true });
5211
6864
  for (const file of filesToMove) {
5212
- fs3.renameSync(path2.join(harnessDir, file), path2.join(defaultDir, file));
6865
+ fs5.renameSync(path2.join(harnessDir, file), path2.join(defaultDir, file));
5213
6866
  }
5214
6867
  } catch (error) {
5215
6868
  return (0, import_types.Err)(
@@ -5250,7 +6903,7 @@ function evictIfNeeded(map) {
5250
6903
  }
5251
6904
  async function getStateDir(projectPath, stream) {
5252
6905
  const streamsIndexPath = path3.join(projectPath, HARNESS_DIR2, "streams", INDEX_FILE2);
5253
- const hasStreams = fs4.existsSync(streamsIndexPath);
6906
+ const hasStreams = fs6.existsSync(streamsIndexPath);
5254
6907
  if (stream || hasStreams) {
5255
6908
  const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
5256
6909
  if (result.ok) {
@@ -5268,10 +6921,10 @@ async function loadState(projectPath, stream) {
5268
6921
  if (!dirResult.ok) return dirResult;
5269
6922
  const stateDir = dirResult.value;
5270
6923
  const statePath = path3.join(stateDir, STATE_FILE);
5271
- if (!fs4.existsSync(statePath)) {
6924
+ if (!fs6.existsSync(statePath)) {
5272
6925
  return (0, import_types.Ok)({ ...DEFAULT_STATE });
5273
6926
  }
5274
- const raw = fs4.readFileSync(statePath, "utf-8");
6927
+ const raw = fs6.readFileSync(statePath, "utf-8");
5275
6928
  const parsed = JSON.parse(raw);
5276
6929
  const result = HarnessStateSchema.safeParse(parsed);
5277
6930
  if (!result.success) {
@@ -5290,8 +6943,8 @@ async function saveState(projectPath, state, stream) {
5290
6943
  if (!dirResult.ok) return dirResult;
5291
6944
  const stateDir = dirResult.value;
5292
6945
  const statePath = path3.join(stateDir, STATE_FILE);
5293
- fs4.mkdirSync(stateDir, { recursive: true });
5294
- fs4.writeFileSync(statePath, JSON.stringify(state, null, 2));
6946
+ fs6.mkdirSync(stateDir, { recursive: true });
6947
+ fs6.writeFileSync(statePath, JSON.stringify(state, null, 2));
5295
6948
  return (0, import_types.Ok)(void 0);
5296
6949
  } catch (error) {
5297
6950
  return (0, import_types.Err)(
@@ -5305,7 +6958,7 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
5305
6958
  if (!dirResult.ok) return dirResult;
5306
6959
  const stateDir = dirResult.value;
5307
6960
  const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
5308
- fs4.mkdirSync(stateDir, { recursive: true });
6961
+ fs6.mkdirSync(stateDir, { recursive: true });
5309
6962
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5310
6963
  let entry;
5311
6964
  if (skillName && outcome) {
@@ -5321,12 +6974,13 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
5321
6974
  - **${timestamp}:** ${learning}
5322
6975
  `;
5323
6976
  }
5324
- if (!fs4.existsSync(learningsPath)) {
5325
- fs4.writeFileSync(learningsPath, `# Learnings
6977
+ if (!fs6.existsSync(learningsPath)) {
6978
+ fs6.writeFileSync(learningsPath, `# Learnings
5326
6979
  ${entry}`);
5327
6980
  } else {
5328
- fs4.appendFileSync(learningsPath, entry);
6981
+ fs6.appendFileSync(learningsPath, entry);
5329
6982
  }
6983
+ learningsCacheMap.delete(learningsPath);
5330
6984
  return (0, import_types.Ok)(void 0);
5331
6985
  } catch (error) {
5332
6986
  return (0, import_types.Err)(
@@ -5342,17 +6996,17 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
5342
6996
  if (!dirResult.ok) return dirResult;
5343
6997
  const stateDir = dirResult.value;
5344
6998
  const learningsPath = path3.join(stateDir, LEARNINGS_FILE);
5345
- if (!fs4.existsSync(learningsPath)) {
6999
+ if (!fs6.existsSync(learningsPath)) {
5346
7000
  return (0, import_types.Ok)([]);
5347
7001
  }
5348
- const stats = fs4.statSync(learningsPath);
7002
+ const stats = fs6.statSync(learningsPath);
5349
7003
  const cacheKey = learningsPath;
5350
7004
  const cached = learningsCacheMap.get(cacheKey);
5351
7005
  let entries;
5352
7006
  if (cached && cached.mtimeMs === stats.mtimeMs) {
5353
7007
  entries = cached.entries;
5354
7008
  } else {
5355
- const content = fs4.readFileSync(learningsPath, "utf-8");
7009
+ const content = fs6.readFileSync(learningsPath, "utf-8");
5356
7010
  const lines = content.split("\n");
5357
7011
  entries = [];
5358
7012
  let currentBlock = [];
@@ -5395,17 +7049,18 @@ async function appendFailure(projectPath, description, skillName, type, stream)
5395
7049
  if (!dirResult.ok) return dirResult;
5396
7050
  const stateDir = dirResult.value;
5397
7051
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5398
- fs4.mkdirSync(stateDir, { recursive: true });
7052
+ fs6.mkdirSync(stateDir, { recursive: true });
5399
7053
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5400
7054
  const entry = `
5401
7055
  - **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
5402
7056
  `;
5403
- if (!fs4.existsSync(failuresPath)) {
5404
- fs4.writeFileSync(failuresPath, `# Failures
7057
+ if (!fs6.existsSync(failuresPath)) {
7058
+ fs6.writeFileSync(failuresPath, `# Failures
5405
7059
  ${entry}`);
5406
7060
  } else {
5407
- fs4.appendFileSync(failuresPath, entry);
7061
+ fs6.appendFileSync(failuresPath, entry);
5408
7062
  }
7063
+ failuresCacheMap.delete(failuresPath);
5409
7064
  return (0, import_types.Ok)(void 0);
5410
7065
  } catch (error) {
5411
7066
  return (0, import_types.Err)(
@@ -5421,16 +7076,16 @@ async function loadFailures(projectPath, stream) {
5421
7076
  if (!dirResult.ok) return dirResult;
5422
7077
  const stateDir = dirResult.value;
5423
7078
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5424
- if (!fs4.existsSync(failuresPath)) {
7079
+ if (!fs6.existsSync(failuresPath)) {
5425
7080
  return (0, import_types.Ok)([]);
5426
7081
  }
5427
- const stats = fs4.statSync(failuresPath);
7082
+ const stats = fs6.statSync(failuresPath);
5428
7083
  const cacheKey = failuresPath;
5429
7084
  const cached = failuresCacheMap.get(cacheKey);
5430
7085
  if (cached && cached.mtimeMs === stats.mtimeMs) {
5431
7086
  return (0, import_types.Ok)(cached.entries);
5432
7087
  }
5433
- const content = fs4.readFileSync(failuresPath, "utf-8");
7088
+ const content = fs6.readFileSync(failuresPath, "utf-8");
5434
7089
  const entries = [];
5435
7090
  for (const line of content.split("\n")) {
5436
7091
  const match = line.match(FAILURE_LINE_REGEX);
@@ -5460,19 +7115,20 @@ async function archiveFailures(projectPath, stream) {
5460
7115
  if (!dirResult.ok) return dirResult;
5461
7116
  const stateDir = dirResult.value;
5462
7117
  const failuresPath = path3.join(stateDir, FAILURES_FILE);
5463
- if (!fs4.existsSync(failuresPath)) {
7118
+ if (!fs6.existsSync(failuresPath)) {
5464
7119
  return (0, import_types.Ok)(void 0);
5465
7120
  }
5466
7121
  const archiveDir = path3.join(stateDir, "archive");
5467
- fs4.mkdirSync(archiveDir, { recursive: true });
7122
+ fs6.mkdirSync(archiveDir, { recursive: true });
5468
7123
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5469
7124
  let archiveName = `failures-${date}.md`;
5470
7125
  let counter = 2;
5471
- while (fs4.existsSync(path3.join(archiveDir, archiveName))) {
7126
+ while (fs6.existsSync(path3.join(archiveDir, archiveName))) {
5472
7127
  archiveName = `failures-${date}-${counter}.md`;
5473
7128
  counter++;
5474
7129
  }
5475
- fs4.renameSync(failuresPath, path3.join(archiveDir, archiveName));
7130
+ fs6.renameSync(failuresPath, path3.join(archiveDir, archiveName));
7131
+ failuresCacheMap.delete(failuresPath);
5476
7132
  return (0, import_types.Ok)(void 0);
5477
7133
  } catch (error) {
5478
7134
  return (0, import_types.Err)(
@@ -5488,8 +7144,8 @@ async function saveHandoff(projectPath, handoff, stream) {
5488
7144
  if (!dirResult.ok) return dirResult;
5489
7145
  const stateDir = dirResult.value;
5490
7146
  const handoffPath = path3.join(stateDir, HANDOFF_FILE);
5491
- fs4.mkdirSync(stateDir, { recursive: true });
5492
- fs4.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
7147
+ fs6.mkdirSync(stateDir, { recursive: true });
7148
+ fs6.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
5493
7149
  return (0, import_types.Ok)(void 0);
5494
7150
  } catch (error) {
5495
7151
  return (0, import_types.Err)(
@@ -5503,10 +7159,10 @@ async function loadHandoff(projectPath, stream) {
5503
7159
  if (!dirResult.ok) return dirResult;
5504
7160
  const stateDir = dirResult.value;
5505
7161
  const handoffPath = path3.join(stateDir, HANDOFF_FILE);
5506
- if (!fs4.existsSync(handoffPath)) {
7162
+ if (!fs6.existsSync(handoffPath)) {
5507
7163
  return (0, import_types.Ok)(null);
5508
7164
  }
5509
- const raw = fs4.readFileSync(handoffPath, "utf-8");
7165
+ const raw = fs6.readFileSync(handoffPath, "utf-8");
5510
7166
  const parsed = JSON.parse(raw);
5511
7167
  const result = HandoffSchema.safeParse(parsed);
5512
7168
  if (!result.success) {
@@ -5524,8 +7180,8 @@ async function runMechanicalGate(projectPath) {
5524
7180
  const gateConfigPath = path3.join(harnessDir, GATE_CONFIG_FILE);
5525
7181
  try {
5526
7182
  let checks = [];
5527
- if (fs4.existsSync(gateConfigPath)) {
5528
- const raw = JSON.parse(fs4.readFileSync(gateConfigPath, "utf-8"));
7183
+ if (fs6.existsSync(gateConfigPath)) {
7184
+ const raw = JSON.parse(fs6.readFileSync(gateConfigPath, "utf-8"));
5529
7185
  const config = GateConfigSchema.safeParse(raw);
5530
7186
  if (config.success && config.data.checks) {
5531
7187
  checks = config.data.checks;
@@ -5533,19 +7189,19 @@ async function runMechanicalGate(projectPath) {
5533
7189
  }
5534
7190
  if (checks.length === 0) {
5535
7191
  const packageJsonPath = path3.join(projectPath, "package.json");
5536
- if (fs4.existsSync(packageJsonPath)) {
5537
- const pkg = JSON.parse(fs4.readFileSync(packageJsonPath, "utf-8"));
7192
+ if (fs6.existsSync(packageJsonPath)) {
7193
+ const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
5538
7194
  const scripts = pkg.scripts || {};
5539
7195
  if (scripts.test) checks.push({ name: "test", command: "npm test" });
5540
7196
  if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
5541
7197
  if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
5542
7198
  if (scripts.build) checks.push({ name: "build", command: "npm run build" });
5543
7199
  }
5544
- if (fs4.existsSync(path3.join(projectPath, "go.mod"))) {
7200
+ if (fs6.existsSync(path3.join(projectPath, "go.mod"))) {
5545
7201
  checks.push({ name: "test", command: "go test ./..." });
5546
7202
  checks.push({ name: "build", command: "go build ./..." });
5547
7203
  }
5548
- if (fs4.existsSync(path3.join(projectPath, "pyproject.toml")) || fs4.existsSync(path3.join(projectPath, "setup.py"))) {
7204
+ if (fs6.existsSync(path3.join(projectPath, "pyproject.toml")) || fs6.existsSync(path3.join(projectPath, "setup.py"))) {
5549
7205
  checks.push({ name: "test", command: "python -m pytest" });
5550
7206
  }
5551
7207
  }
@@ -5748,7 +7404,7 @@ async function runMultiTurnPipeline(initialContext, turnExecutor, options) {
5748
7404
  }
5749
7405
 
5750
7406
  // src/security/scanner.ts
5751
- var fs6 = __toESM(require("fs/promises"));
7407
+ var fs8 = __toESM(require("fs/promises"));
5752
7408
 
5753
7409
  // src/security/rules/registry.ts
5754
7410
  var RuleRegistry = class {
@@ -5779,7 +7435,7 @@ var RuleRegistry = class {
5779
7435
  };
5780
7436
 
5781
7437
  // src/security/config.ts
5782
- var import_zod4 = require("zod");
7438
+ var import_zod6 = require("zod");
5783
7439
 
5784
7440
  // src/security/types.ts
5785
7441
  var DEFAULT_SECURITY_CONFIG = {
@@ -5790,19 +7446,19 @@ var DEFAULT_SECURITY_CONFIG = {
5790
7446
  };
5791
7447
 
5792
7448
  // src/security/config.ts
5793
- var RuleOverrideSchema = import_zod4.z.enum(["off", "error", "warning", "info"]);
5794
- var SecurityConfigSchema = import_zod4.z.object({
5795
- enabled: import_zod4.z.boolean().default(true),
5796
- strict: import_zod4.z.boolean().default(false),
5797
- rules: import_zod4.z.record(import_zod4.z.string(), RuleOverrideSchema).optional().default({}),
5798
- exclude: import_zod4.z.array(import_zod4.z.string()).optional().default(["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]),
5799
- external: import_zod4.z.object({
5800
- semgrep: import_zod4.z.object({
5801
- enabled: import_zod4.z.union([import_zod4.z.literal("auto"), import_zod4.z.boolean()]).default("auto"),
5802
- rulesets: import_zod4.z.array(import_zod4.z.string()).optional()
7449
+ var RuleOverrideSchema = import_zod6.z.enum(["off", "error", "warning", "info"]);
7450
+ var SecurityConfigSchema = import_zod6.z.object({
7451
+ enabled: import_zod6.z.boolean().default(true),
7452
+ strict: import_zod6.z.boolean().default(false),
7453
+ rules: import_zod6.z.record(import_zod6.z.string(), RuleOverrideSchema).optional().default({}),
7454
+ exclude: import_zod6.z.array(import_zod6.z.string()).optional().default(["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]),
7455
+ external: import_zod6.z.object({
7456
+ semgrep: import_zod6.z.object({
7457
+ enabled: import_zod6.z.union([import_zod6.z.literal("auto"), import_zod6.z.boolean()]).default("auto"),
7458
+ rulesets: import_zod6.z.array(import_zod6.z.string()).optional()
5803
7459
  }).optional(),
5804
- gitleaks: import_zod4.z.object({
5805
- enabled: import_zod4.z.union([import_zod4.z.literal("auto"), import_zod4.z.boolean()]).default("auto")
7460
+ gitleaks: import_zod6.z.object({
7461
+ enabled: import_zod6.z.union([import_zod6.z.literal("auto"), import_zod6.z.boolean()]).default("auto")
5806
7462
  }).optional()
5807
7463
  }).optional()
5808
7464
  });
@@ -5835,15 +7491,15 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
5835
7491
  }
5836
7492
 
5837
7493
  // src/security/stack-detector.ts
5838
- var fs5 = __toESM(require("fs"));
7494
+ var fs7 = __toESM(require("fs"));
5839
7495
  var path4 = __toESM(require("path"));
5840
7496
  function detectStack(projectRoot) {
5841
7497
  const stacks = [];
5842
7498
  const pkgJsonPath = path4.join(projectRoot, "package.json");
5843
- if (fs5.existsSync(pkgJsonPath)) {
7499
+ if (fs7.existsSync(pkgJsonPath)) {
5844
7500
  stacks.push("node");
5845
7501
  try {
5846
- const pkgJson = JSON.parse(fs5.readFileSync(pkgJsonPath, "utf-8"));
7502
+ const pkgJson = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
5847
7503
  const allDeps = {
5848
7504
  ...pkgJson.dependencies,
5849
7505
  ...pkgJson.devDependencies
@@ -5859,12 +7515,12 @@ function detectStack(projectRoot) {
5859
7515
  }
5860
7516
  }
5861
7517
  const goModPath = path4.join(projectRoot, "go.mod");
5862
- if (fs5.existsSync(goModPath)) {
7518
+ if (fs7.existsSync(goModPath)) {
5863
7519
  stacks.push("go");
5864
7520
  }
5865
7521
  const requirementsPath = path4.join(projectRoot, "requirements.txt");
5866
7522
  const pyprojectPath = path4.join(projectRoot, "pyproject.toml");
5867
- if (fs5.existsSync(requirementsPath) || fs5.existsSync(pyprojectPath)) {
7523
+ if (fs7.existsSync(requirementsPath) || fs7.existsSync(pyprojectPath)) {
5868
7524
  stacks.push("python");
5869
7525
  }
5870
7526
  return stacks;
@@ -6291,7 +7947,7 @@ var SecurityScanner = class {
6291
7947
  }
6292
7948
  async scanFile(filePath) {
6293
7949
  if (!this.config.enabled) return [];
6294
- const content = await fs6.readFile(filePath, "utf-8");
7950
+ const content = await fs8.readFile(filePath, "utf-8");
6295
7951
  return this.scanContent(content, filePath, 1);
6296
7952
  }
6297
7953
  async scanFiles(filePaths) {
@@ -6324,7 +7980,8 @@ var ALL_CHECKS = [
6324
7980
  "entropy",
6325
7981
  "security",
6326
7982
  "perf",
6327
- "phase-gate"
7983
+ "phase-gate",
7984
+ "arch"
6328
7985
  ];
6329
7986
  async function runSingleCheck(name, projectRoot, config) {
6330
7987
  const start = Date.now();
@@ -6388,7 +8045,17 @@ async function runSingleCheck(name, projectRoot, config) {
6388
8045
  }
6389
8046
  case "docs": {
6390
8047
  const docsDir = path5.join(projectRoot, config.docsDir ?? "docs");
6391
- const result = await checkDocCoverage("project", { docsDir });
8048
+ const entropyConfig = config.entropy || {};
8049
+ const result = await checkDocCoverage("project", {
8050
+ docsDir,
8051
+ sourceDir: projectRoot,
8052
+ excludePatterns: entropyConfig.excludePatterns || [
8053
+ "**/node_modules/**",
8054
+ "**/dist/**",
8055
+ "**/*.test.ts",
8056
+ "**/fixtures/**"
8057
+ ]
8058
+ });
6392
8059
  if (!result.ok) {
6393
8060
  issues.push({ severity: "warning", message: result.error.message });
6394
8061
  } else if (result.value.gaps.length > 0) {
@@ -6463,11 +8130,13 @@ async function runSingleCheck(name, projectRoot, config) {
6463
8130
  break;
6464
8131
  }
6465
8132
  case "perf": {
8133
+ const perfConfig = config.performance || {};
6466
8134
  const perfAnalyzer = new EntropyAnalyzer({
6467
8135
  rootDir: projectRoot,
6468
8136
  analyze: {
6469
- complexity: true,
6470
- coupling: true
8137
+ complexity: perfConfig.complexity || true,
8138
+ coupling: perfConfig.coupling || true,
8139
+ sizeBudget: perfConfig.sizeBudget || false
6471
8140
  }
6472
8141
  });
6473
8142
  const perfResult = await perfAnalyzer.analyze();
@@ -6508,6 +8177,43 @@ async function runSingleCheck(name, projectRoot, config) {
6508
8177
  });
6509
8178
  break;
6510
8179
  }
8180
+ case "arch": {
8181
+ const rawArchConfig = config.architecture;
8182
+ const archConfig = ArchConfigSchema.parse(rawArchConfig ?? {});
8183
+ if (!archConfig.enabled) break;
8184
+ const results = await runAll(archConfig, projectRoot);
8185
+ const baselineManager = new ArchBaselineManager(projectRoot, archConfig.baselinePath);
8186
+ const baseline = baselineManager.load();
8187
+ if (baseline) {
8188
+ const diffResult = diff(results, baseline);
8189
+ if (!diffResult.passed) {
8190
+ for (const v of diffResult.newViolations) {
8191
+ issues.push({
8192
+ severity: v.severity,
8193
+ message: `[${v.category || "arch"}] NEW: ${v.detail}`,
8194
+ file: v.file
8195
+ });
8196
+ }
8197
+ for (const r of diffResult.regressions) {
8198
+ issues.push({
8199
+ severity: "error",
8200
+ message: `[${r.category}] REGRESSION: ${r.currentValue} > ${r.baselineValue} (delta: ${r.delta})`
8201
+ });
8202
+ }
8203
+ }
8204
+ } else {
8205
+ for (const result of results) {
8206
+ for (const v of result.violations) {
8207
+ issues.push({
8208
+ severity: v.severity,
8209
+ message: `[${result.category}] ${v.detail}`,
8210
+ file: v.file
8211
+ });
8212
+ }
8213
+ }
8214
+ }
8215
+ break;
8216
+ }
6511
8217
  }
6512
8218
  } catch (error) {
6513
8219
  issues.push({
@@ -6842,22 +8548,22 @@ var PREFIX_PATTERNS = [
6842
8548
  ];
6843
8549
  var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|js|jsx|mts|cts)$/;
6844
8550
  var MD_FILE_PATTERN = /\.md$/;
6845
- function detectChangeType(commitMessage, diff) {
8551
+ function detectChangeType(commitMessage, diff2) {
6846
8552
  const trimmed = commitMessage.trim();
6847
8553
  for (const { pattern, type } of PREFIX_PATTERNS) {
6848
8554
  if (pattern.test(trimmed)) {
6849
8555
  return type;
6850
8556
  }
6851
8557
  }
6852
- if (diff.changedFiles.length > 0 && diff.changedFiles.every((f) => MD_FILE_PATTERN.test(f))) {
8558
+ if (diff2.changedFiles.length > 0 && diff2.changedFiles.every((f) => MD_FILE_PATTERN.test(f))) {
6853
8559
  return "docs";
6854
8560
  }
6855
- const newNonTestFiles = diff.newFiles.filter((f) => !TEST_FILE_PATTERN.test(f));
8561
+ const newNonTestFiles = diff2.newFiles.filter((f) => !TEST_FILE_PATTERN.test(f));
6856
8562
  if (newNonTestFiles.length > 0) {
6857
8563
  return "feature";
6858
8564
  }
6859
- const hasNewTestFile = diff.newFiles.some((f) => TEST_FILE_PATTERN.test(f));
6860
- if (diff.totalDiffLines < 20 && hasNewTestFile) {
8565
+ const hasNewTestFile = diff2.newFiles.some((f) => TEST_FILE_PATTERN.test(f));
8566
+ if (diff2.totalDiffLines < 20 && hasNewTestFile) {
6861
8567
  return "bugfix";
6862
8568
  }
6863
8569
  return "feature";
@@ -6886,7 +8592,7 @@ async function readContextFile(projectRoot, filePath, reason) {
6886
8592
  const relPath = path7.isAbsolute(filePath) ? path7.relative(projectRoot, filePath) : filePath;
6887
8593
  return { path: relPath, content, reason, lines };
6888
8594
  }
6889
- function extractImportSources(content) {
8595
+ function extractImportSources2(content) {
6890
8596
  const sources = [];
6891
8597
  const importRegex = /(?:import\s+(?:.*?\s+from\s+)?['"]([^'"]+)['"]|require\(\s*['"]([^'"]+)['"]\s*\))/g;
6892
8598
  let match;
@@ -6928,7 +8634,7 @@ async function gatherImportContext(projectRoot, changedFiles, budget) {
6928
8634
  const seen = new Set(changedFiles.map((f) => f.path));
6929
8635
  for (const cf of changedFiles) {
6930
8636
  if (linesGathered >= budget) break;
6931
- const sources = extractImportSources(cf.content);
8637
+ const sources = extractImportSources2(cf.content);
6932
8638
  for (const source of sources) {
6933
8639
  if (linesGathered >= budget) break;
6934
8640
  const resolved = await resolveImportPath2(projectRoot, cf.path, source);
@@ -7095,11 +8801,11 @@ async function scopeArchitectureContext(projectRoot, changedFiles, budget, optio
7095
8801
  return contextFiles;
7096
8802
  }
7097
8803
  async function scopeContext(options) {
7098
- const { projectRoot, diff, commitMessage } = options;
7099
- const changeType = detectChangeType(commitMessage, diff);
7100
- const budget = computeContextBudget(diff.totalDiffLines);
8804
+ const { projectRoot, diff: diff2, commitMessage } = options;
8805
+ const changeType = detectChangeType(commitMessage, diff2);
8806
+ const budget = computeContextBudget(diff2.totalDiffLines);
7101
8807
  const changedFiles = [];
7102
- for (const filePath of diff.changedFiles) {
8808
+ for (const filePath of diff2.changedFiles) {
7103
8809
  const cf = await readContextFile(projectRoot, filePath, "changed");
7104
8810
  if (cf) changedFiles.push(cf);
7105
8811
  }
@@ -7119,7 +8825,7 @@ async function scopeContext(options) {
7119
8825
  changedFiles: [...changedFiles],
7120
8826
  contextFiles,
7121
8827
  commitHistory: options.commitHistory ?? [],
7122
- diffLines: diff.totalDiffLines,
8828
+ diffLines: diff2.totalDiffLines,
7123
8829
  contextLines
7124
8830
  });
7125
8831
  }
@@ -8129,7 +9835,7 @@ function formatGitHubSummary(options) {
8129
9835
  async function runReviewPipeline(options) {
8130
9836
  const {
8131
9837
  projectRoot,
8132
- diff,
9838
+ diff: diff2,
8133
9839
  commitMessage,
8134
9840
  flags,
8135
9841
  graph,
@@ -8163,7 +9869,7 @@ async function runReviewPipeline(options) {
8163
9869
  const mechResult = await runMechanicalChecks({
8164
9870
  projectRoot,
8165
9871
  config,
8166
- changedFiles: diff.changedFiles
9872
+ changedFiles: diff2.changedFiles
8167
9873
  });
8168
9874
  if (mechResult.ok) {
8169
9875
  mechanicalResult = mechResult.value;
@@ -8202,7 +9908,7 @@ async function runReviewPipeline(options) {
8202
9908
  try {
8203
9909
  contextBundles = await scopeContext({
8204
9910
  projectRoot,
8205
- diff,
9911
+ diff: diff2,
8206
9912
  commitMessage,
8207
9913
  ...graph != null ? { graph } : {},
8208
9914
  ...conventionFiles != null ? { conventionFiles } : {},
@@ -8216,14 +9922,14 @@ async function runReviewPipeline(options) {
8216
9922
  changedFiles: [],
8217
9923
  contextFiles: [],
8218
9924
  commitHistory: [],
8219
- diffLines: diff.totalDiffLines,
9925
+ diffLines: diff2.totalDiffLines,
8220
9926
  contextLines: 0
8221
9927
  }));
8222
9928
  }
8223
9929
  const agentResults = await fanOutReview({ bundles: contextBundles });
8224
9930
  const rawFindings = agentResults.flatMap((r) => r.findings);
8225
9931
  const fileContents = /* @__PURE__ */ new Map();
8226
- for (const [file, content] of diff.fileDiffs) {
9932
+ for (const [file, content] of diff2.fileDiffs) {
8227
9933
  fileContents.set(file, content);
8228
9934
  }
8229
9935
  const validatedFindings = await validateFindings({
@@ -8259,7 +9965,7 @@ async function runReviewPipeline(options) {
8259
9965
  }
8260
9966
 
8261
9967
  // src/roadmap/parse.ts
8262
- var import_types9 = require("@harness-engineering/types");
9968
+ var import_types16 = require("@harness-engineering/types");
8263
9969
  var VALID_STATUSES = /* @__PURE__ */ new Set([
8264
9970
  "backlog",
8265
9971
  "planned",
@@ -8271,14 +9977,14 @@ var EM_DASH = "\u2014";
8271
9977
  function parseRoadmap(markdown) {
8272
9978
  const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
8273
9979
  if (!fmMatch) {
8274
- return (0, import_types9.Err)(new Error("Missing or malformed YAML frontmatter"));
9980
+ return (0, import_types16.Err)(new Error("Missing or malformed YAML frontmatter"));
8275
9981
  }
8276
9982
  const fmResult = parseFrontmatter(fmMatch[1]);
8277
9983
  if (!fmResult.ok) return fmResult;
8278
9984
  const body = markdown.slice(fmMatch[0].length);
8279
9985
  const milestonesResult = parseMilestones(body);
8280
9986
  if (!milestonesResult.ok) return milestonesResult;
8281
- return (0, import_types9.Ok)({
9987
+ return (0, import_types16.Ok)({
8282
9988
  frontmatter: fmResult.value,
8283
9989
  milestones: milestonesResult.value
8284
9990
  });
@@ -8298,7 +10004,7 @@ function parseFrontmatter(raw) {
8298
10004
  const lastSynced = map.get("last_synced");
8299
10005
  const lastManualEdit = map.get("last_manual_edit");
8300
10006
  if (!project || !versionStr || !lastSynced || !lastManualEdit) {
8301
- return (0, import_types9.Err)(
10007
+ return (0, import_types16.Err)(
8302
10008
  new Error(
8303
10009
  "Frontmatter missing required fields: project, version, last_synced, last_manual_edit"
8304
10010
  )
@@ -8306,9 +10012,9 @@ function parseFrontmatter(raw) {
8306
10012
  }
8307
10013
  const version = parseInt(versionStr, 10);
8308
10014
  if (isNaN(version)) {
8309
- return (0, import_types9.Err)(new Error("Frontmatter version must be a number"));
10015
+ return (0, import_types16.Err)(new Error("Frontmatter version must be a number"));
8310
10016
  }
8311
- return (0, import_types9.Ok)({ project, version, lastSynced, lastManualEdit });
10017
+ return (0, import_types16.Ok)({ project, version, lastSynced, lastManualEdit });
8312
10018
  }
8313
10019
  function parseMilestones(body) {
8314
10020
  const milestones = [];
@@ -8332,7 +10038,7 @@ function parseMilestones(body) {
8332
10038
  features: featuresResult.value
8333
10039
  });
8334
10040
  }
8335
- return (0, import_types9.Ok)(milestones);
10041
+ return (0, import_types16.Ok)(milestones);
8336
10042
  }
8337
10043
  function parseFeatures(sectionBody) {
8338
10044
  const features = [];
@@ -8353,7 +10059,7 @@ function parseFeatures(sectionBody) {
8353
10059
  if (!featureResult.ok) return featureResult;
8354
10060
  features.push(featureResult.value);
8355
10061
  }
8356
- return (0, import_types9.Ok)(features);
10062
+ return (0, import_types16.Ok)(features);
8357
10063
  }
8358
10064
  function parseFeatureFields(name, body) {
8359
10065
  const fieldMap = /* @__PURE__ */ new Map();
@@ -8364,7 +10070,7 @@ function parseFeatureFields(name, body) {
8364
10070
  }
8365
10071
  const statusRaw = fieldMap.get("Status");
8366
10072
  if (!statusRaw || !VALID_STATUSES.has(statusRaw)) {
8367
- return (0, import_types9.Err)(
10073
+ return (0, import_types16.Err)(
8368
10074
  new Error(
8369
10075
  `Feature "${name}" has invalid status: "${statusRaw ?? "(missing)"}". Valid statuses: ${[...VALID_STATUSES].join(", ")}`
8370
10076
  )
@@ -8378,7 +10084,7 @@ function parseFeatureFields(name, body) {
8378
10084
  const blockedByRaw = fieldMap.get("Blocked by") ?? EM_DASH;
8379
10085
  const blockedBy = blockedByRaw === EM_DASH ? [] : blockedByRaw.split(",").map((b) => b.trim());
8380
10086
  const summary = fieldMap.get("Summary") ?? "";
8381
- return (0, import_types9.Ok)({ name, status, spec, plans, blockedBy, summary });
10087
+ return (0, import_types16.Ok)({ name, status, spec, plans, blockedBy, summary });
8382
10088
  }
8383
10089
 
8384
10090
  // src/roadmap/serialize.ts
@@ -8422,9 +10128,9 @@ function serializeFeature(feature) {
8422
10128
  }
8423
10129
 
8424
10130
  // src/roadmap/sync.ts
8425
- var fs7 = __toESM(require("fs"));
10131
+ var fs9 = __toESM(require("fs"));
8426
10132
  var path9 = __toESM(require("path"));
8427
- var import_types10 = require("@harness-engineering/types");
10133
+ var import_types17 = require("@harness-engineering/types");
8428
10134
  function inferStatus(feature, projectPath, allFeatures) {
8429
10135
  if (feature.blockedBy.length > 0) {
8430
10136
  const blockerNotDone = feature.blockedBy.some((blockerName) => {
@@ -8439,9 +10145,9 @@ function inferStatus(feature, projectPath, allFeatures) {
8439
10145
  const useRootState = featuresWithPlans.length <= 1;
8440
10146
  if (useRootState) {
8441
10147
  const rootStatePath = path9.join(projectPath, ".harness", "state.json");
8442
- if (fs7.existsSync(rootStatePath)) {
10148
+ if (fs9.existsSync(rootStatePath)) {
8443
10149
  try {
8444
- const raw = fs7.readFileSync(rootStatePath, "utf-8");
10150
+ const raw = fs9.readFileSync(rootStatePath, "utf-8");
8445
10151
  const state = JSON.parse(raw);
8446
10152
  if (state.progress) {
8447
10153
  for (const status of Object.values(state.progress)) {
@@ -8453,15 +10159,15 @@ function inferStatus(feature, projectPath, allFeatures) {
8453
10159
  }
8454
10160
  }
8455
10161
  const sessionsDir = path9.join(projectPath, ".harness", "sessions");
8456
- if (fs7.existsSync(sessionsDir)) {
10162
+ if (fs9.existsSync(sessionsDir)) {
8457
10163
  try {
8458
- const sessionDirs = fs7.readdirSync(sessionsDir, { withFileTypes: true });
10164
+ const sessionDirs = fs9.readdirSync(sessionsDir, { withFileTypes: true });
8459
10165
  for (const entry of sessionDirs) {
8460
10166
  if (!entry.isDirectory()) continue;
8461
10167
  const autopilotPath = path9.join(sessionsDir, entry.name, "autopilot-state.json");
8462
- if (!fs7.existsSync(autopilotPath)) continue;
10168
+ if (!fs9.existsSync(autopilotPath)) continue;
8463
10169
  try {
8464
- const raw = fs7.readFileSync(autopilotPath, "utf-8");
10170
+ const raw = fs9.readFileSync(autopilotPath, "utf-8");
8465
10171
  const autopilot = JSON.parse(raw);
8466
10172
  if (!autopilot.phases) continue;
8467
10173
  const linkedPhases = autopilot.phases.filter(
@@ -8508,46 +10214,214 @@ function syncRoadmap(options) {
8508
10214
  to: inferred
8509
10215
  });
8510
10216
  }
8511
- return (0, import_types10.Ok)(changes);
10217
+ return (0, import_types17.Ok)(changes);
8512
10218
  }
8513
10219
 
8514
10220
  // src/interaction/types.ts
8515
- var import_zod5 = require("zod");
8516
- var InteractionTypeSchema = import_zod5.z.enum(["question", "confirmation", "transition"]);
8517
- var QuestionSchema = import_zod5.z.object({
8518
- text: import_zod5.z.string(),
8519
- options: import_zod5.z.array(import_zod5.z.string()).optional(),
8520
- default: import_zod5.z.string().optional()
10221
+ var import_zod7 = require("zod");
10222
+ var InteractionTypeSchema = import_zod7.z.enum(["question", "confirmation", "transition"]);
10223
+ var QuestionSchema = import_zod7.z.object({
10224
+ text: import_zod7.z.string(),
10225
+ options: import_zod7.z.array(import_zod7.z.string()).optional(),
10226
+ default: import_zod7.z.string().optional()
8521
10227
  });
8522
- var ConfirmationSchema = import_zod5.z.object({
8523
- text: import_zod5.z.string(),
8524
- context: import_zod5.z.string()
10228
+ var ConfirmationSchema = import_zod7.z.object({
10229
+ text: import_zod7.z.string(),
10230
+ context: import_zod7.z.string()
8525
10231
  });
8526
- var TransitionSchema = import_zod5.z.object({
8527
- completedPhase: import_zod5.z.string(),
8528
- suggestedNext: import_zod5.z.string(),
8529
- reason: import_zod5.z.string(),
8530
- artifacts: import_zod5.z.array(import_zod5.z.string()),
8531
- requiresConfirmation: import_zod5.z.boolean(),
8532
- summary: import_zod5.z.string()
10232
+ var TransitionSchema = import_zod7.z.object({
10233
+ completedPhase: import_zod7.z.string(),
10234
+ suggestedNext: import_zod7.z.string(),
10235
+ reason: import_zod7.z.string(),
10236
+ artifacts: import_zod7.z.array(import_zod7.z.string()),
10237
+ requiresConfirmation: import_zod7.z.boolean(),
10238
+ summary: import_zod7.z.string()
8533
10239
  });
8534
- var EmitInteractionInputSchema = import_zod5.z.object({
8535
- path: import_zod5.z.string(),
10240
+ var EmitInteractionInputSchema = import_zod7.z.object({
10241
+ path: import_zod7.z.string(),
8536
10242
  type: InteractionTypeSchema,
8537
- stream: import_zod5.z.string().optional(),
10243
+ stream: import_zod7.z.string().optional(),
8538
10244
  question: QuestionSchema.optional(),
8539
10245
  confirmation: ConfirmationSchema.optional(),
8540
10246
  transition: TransitionSchema.optional()
8541
10247
  });
8542
10248
 
8543
- // src/update-checker.ts
8544
- var fs8 = __toESM(require("fs"));
10249
+ // src/blueprint/scanner.ts
10250
+ var fs10 = __toESM(require("fs/promises"));
8545
10251
  var path10 = __toESM(require("path"));
10252
+ var ProjectScanner = class {
10253
+ constructor(rootDir) {
10254
+ this.rootDir = rootDir;
10255
+ }
10256
+ async scan() {
10257
+ let projectName = path10.basename(this.rootDir);
10258
+ try {
10259
+ const pkgPath = path10.join(this.rootDir, "package.json");
10260
+ const pkgRaw = await fs10.readFile(pkgPath, "utf-8");
10261
+ const pkg = JSON.parse(pkgRaw);
10262
+ if (pkg.name) projectName = pkg.name;
10263
+ } catch {
10264
+ }
10265
+ return {
10266
+ projectName,
10267
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
10268
+ modules: [
10269
+ {
10270
+ id: "foundations",
10271
+ title: "Foundations",
10272
+ description: "Utility files and basic types.",
10273
+ files: []
10274
+ },
10275
+ {
10276
+ id: "core-logic",
10277
+ title: "Core Logic",
10278
+ description: "Mid-level services and domain logic.",
10279
+ files: []
10280
+ },
10281
+ {
10282
+ id: "interaction-surface",
10283
+ title: "Interaction Surface",
10284
+ description: "APIs, CLIs, and Entry Points.",
10285
+ files: []
10286
+ },
10287
+ {
10288
+ id: "cross-cutting",
10289
+ title: "Cross-Cutting Concerns",
10290
+ description: "Security, Logging, and Observability.",
10291
+ files: []
10292
+ }
10293
+ ],
10294
+ hotspots: [],
10295
+ dependencies: []
10296
+ };
10297
+ }
10298
+ };
10299
+
10300
+ // src/blueprint/generator.ts
10301
+ var fs11 = __toESM(require("fs/promises"));
10302
+ var path11 = __toESM(require("path"));
10303
+ var ejs = __toESM(require("ejs"));
10304
+
10305
+ // src/blueprint/templates.ts
10306
+ var SHELL_TEMPLATE = `
10307
+ <!DOCTYPE html>
10308
+ <html lang="en">
10309
+ <head>
10310
+ <meta charset="UTF-8">
10311
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
10312
+ <title>Blueprint: <%= projectName %></title>
10313
+ <style><%- styles %></style>
10314
+ </head>
10315
+ <body>
10316
+ <div id="app">
10317
+ <header>
10318
+ <h1>Blueprint: <%= projectName %></h1>
10319
+ <p>Generated at: <%= generatedAt %></p>
10320
+ </header>
10321
+ <main>
10322
+ <section class="modules">
10323
+ <% modules.forEach(module => { %>
10324
+ <article class="module" id="<%= module.id %>">
10325
+ <h2><%= module.title %></h2>
10326
+ <p><%= module.description %></p>
10327
+ <div class="content">
10328
+ <h3>Code Translation</h3>
10329
+ <div class="translation"><%- module.content.codeTranslation %></div>
10330
+ <h3>Knowledge Check</h3>
10331
+ <div class="quiz">
10332
+ <% module.content.quiz.questions.forEach((q, i) => { %>
10333
+ <div class="question">
10334
+ <p><%= q.question %></p>
10335
+ <button onclick="reveal(this)">Reveal Answer</button>
10336
+ <p class="answer" style="display:none;"><%= q.answer %></p>
10337
+ </div>
10338
+ <% }) %>
10339
+ </div>
10340
+ </div>
10341
+ </article>
10342
+
10343
+ <% }) %>
10344
+ </section>
10345
+ </main>
10346
+ </div>
10347
+ <script><%- scripts %></script>
10348
+ </body>
10349
+ </html>
10350
+ `;
10351
+ var STYLES = `
10352
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; margin: 0; padding: 20px; color: #333; }
10353
+ header { border-bottom: 2px solid #eee; margin-bottom: 20px; padding-bottom: 10px; }
10354
+ .module { background: #f9f9f9; border: 1px solid #ddd; padding: 15px; margin-bottom: 15px; border-radius: 4px; }
10355
+ .module h2 { margin-top: 0; color: #0066cc; }
10356
+ `;
10357
+ var SCRIPTS = `
10358
+ function reveal(btn) {
10359
+ btn.nextElementSibling.style.display = 'block';
10360
+ btn.style.display = 'none';
10361
+ }
10362
+ console.log('Blueprint player initialized.');
10363
+ `;
10364
+
10365
+ // src/shared/llm.ts
10366
+ var MockLLMService = class {
10367
+ async generate(prompt) {
10368
+ return "This is a mock LLM response for: " + prompt;
10369
+ }
10370
+ };
10371
+ var llmService = new MockLLMService();
10372
+
10373
+ // src/blueprint/content-pipeline.ts
10374
+ var ContentPipeline = class {
10375
+ async generateModuleContent(module2) {
10376
+ const codeContext = module2.files.join("\n");
10377
+ const translation = await llmService.generate(
10378
+ `You are a technical educator. Explain the following code clearly and concisely: ${codeContext}`
10379
+ );
10380
+ const quizJson = await llmService.generate(
10381
+ `Create 3 technical quiz questions for this code. Return ONLY valid JSON in this format: { "questions": [{ "question": "...", "answer": "..." }] }. Code: ${codeContext}`
10382
+ );
10383
+ let quiz;
10384
+ try {
10385
+ const cleanJson = quizJson.replace(/```json/g, "").replace(/```/g, "").trim();
10386
+ quiz = JSON.parse(cleanJson);
10387
+ } catch (e) {
10388
+ console.error("Failed to parse quiz JSON", e);
10389
+ quiz = { questions: [{ question: "Failed to generate quiz", answer: "N/A" }] };
10390
+ }
10391
+ return {
10392
+ codeTranslation: translation,
10393
+ quiz
10394
+ };
10395
+ }
10396
+ };
10397
+
10398
+ // src/blueprint/generator.ts
10399
+ var BlueprintGenerator = class {
10400
+ contentPipeline = new ContentPipeline();
10401
+ async generate(data, options) {
10402
+ await Promise.all(
10403
+ data.modules.map(async (module2) => {
10404
+ module2.content = await this.contentPipeline.generateModuleContent(module2);
10405
+ })
10406
+ );
10407
+ const html = ejs.render(SHELL_TEMPLATE, {
10408
+ ...data,
10409
+ styles: STYLES,
10410
+ scripts: SCRIPTS
10411
+ });
10412
+ await fs11.mkdir(options.outputDir, { recursive: true });
10413
+ await fs11.writeFile(path11.join(options.outputDir, "index.html"), html);
10414
+ }
10415
+ };
10416
+
10417
+ // src/update-checker.ts
10418
+ var fs12 = __toESM(require("fs"));
10419
+ var path12 = __toESM(require("path"));
8546
10420
  var os = __toESM(require("os"));
8547
10421
  var import_child_process3 = require("child_process");
8548
10422
  function getStatePath() {
8549
10423
  const home = process.env["HOME"] || os.homedir();
8550
- return path10.join(home, ".harness", "update-check.json");
10424
+ return path12.join(home, ".harness", "update-check.json");
8551
10425
  }
8552
10426
  function isUpdateCheckEnabled(configInterval) {
8553
10427
  if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
@@ -8560,7 +10434,7 @@ function shouldRunCheck(state, intervalMs) {
8560
10434
  }
8561
10435
  function readCheckState() {
8562
10436
  try {
8563
- const raw = fs8.readFileSync(getStatePath(), "utf-8");
10437
+ const raw = fs12.readFileSync(getStatePath(), "utf-8");
8564
10438
  const parsed = JSON.parse(raw);
8565
10439
  if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
8566
10440
  const state = parsed;
@@ -8577,7 +10451,7 @@ function readCheckState() {
8577
10451
  }
8578
10452
  function spawnBackgroundCheck(currentVersion) {
8579
10453
  const statePath = getStatePath();
8580
- const stateDir = path10.dirname(statePath);
10454
+ const stateDir = path12.dirname(statePath);
8581
10455
  const script = `
8582
10456
  const { execSync } = require('child_process');
8583
10457
  const fs = require('fs');
@@ -8631,39 +10505,64 @@ Run "harness update" to upgrade.`;
8631
10505
  }
8632
10506
 
8633
10507
  // src/index.ts
8634
- var VERSION = "1.8.2";
10508
+ var VERSION = "0.11.0";
8635
10509
  // Annotate the CommonJS export names for ESM import in node:
8636
10510
  0 && (module.exports = {
8637
10511
  AGENT_DESCRIPTORS,
8638
10512
  ARCHITECTURE_DESCRIPTOR,
8639
10513
  AgentActionEmitter,
10514
+ ArchBaselineManager,
10515
+ ArchBaselineSchema,
10516
+ ArchConfigSchema,
10517
+ ArchDiffResultSchema,
10518
+ ArchMetricCategorySchema,
8640
10519
  BUG_DETECTION_DESCRIPTOR,
8641
10520
  BaselineManager,
8642
10521
  BenchmarkRunner,
10522
+ BlueprintGenerator,
10523
+ BundleConstraintsSchema,
10524
+ BundleSchema,
8643
10525
  COMPLIANCE_DESCRIPTOR,
10526
+ CategoryBaselineSchema,
10527
+ CategoryRegressionSchema,
8644
10528
  ChecklistBuilder,
10529
+ CircularDepsCollector,
10530
+ ComplexityCollector,
8645
10531
  ConfirmationSchema,
8646
10532
  ConsoleSink,
10533
+ ConstraintRuleSchema,
10534
+ ContentPipeline,
10535
+ ContributionsSchema,
10536
+ CouplingCollector,
8647
10537
  CriticalPathResolver,
8648
10538
  DEFAULT_PROVIDER_TIERS,
8649
10539
  DEFAULT_SECURITY_CONFIG,
8650
10540
  DEFAULT_STATE,
8651
10541
  DEFAULT_STREAM_INDEX,
10542
+ DepDepthCollector,
8652
10543
  EmitInteractionInputSchema,
8653
10544
  EntropyAnalyzer,
8654
10545
  EntropyConfigSchema,
8655
10546
  ExclusionSet,
8656
10547
  FailureEntrySchema,
8657
10548
  FileSink,
10549
+ ForbiddenImportCollector,
8658
10550
  GateConfigSchema,
8659
10551
  GateResultSchema,
8660
10552
  HandoffSchema,
8661
10553
  HarnessStateSchema,
8662
10554
  InteractionTypeSchema,
10555
+ LayerViolationCollector,
10556
+ LockfilePackageSchema,
10557
+ LockfileSchema,
10558
+ ManifestSchema,
10559
+ MetricResultSchema,
10560
+ ModuleSizeCollector,
8663
10561
  NoOpExecutor,
8664
10562
  NoOpSink,
8665
10563
  NoOpTelemetryAdapter,
8666
10564
  PatternConfigSchema,
10565
+ ProjectScanner,
8667
10566
  QuestionSchema,
8668
10567
  REQUIRED_SECTIONS,
8669
10568
  RegressionDetector,
@@ -8671,16 +10570,26 @@ var VERSION = "1.8.2";
8671
10570
  SECURITY_DESCRIPTOR,
8672
10571
  SecurityConfigSchema,
8673
10572
  SecurityScanner,
10573
+ SharableBoundaryConfigSchema,
10574
+ SharableForbiddenImportSchema,
10575
+ SharableLayerSchema,
10576
+ SharableSecurityRulesSchema,
8674
10577
  StreamIndexSchema,
8675
10578
  StreamInfoSchema,
10579
+ ThresholdConfigSchema,
8676
10580
  TransitionSchema,
8677
10581
  TypeScriptParser,
8678
10582
  VERSION,
10583
+ ViolationSchema,
10584
+ addProvenance,
8679
10585
  analyzeDiff,
8680
10586
  appendFailure,
8681
10587
  appendLearning,
8682
10588
  applyFixes,
8683
10589
  applyHotspotDowngrade,
10590
+ archMatchers,
10591
+ archModule,
10592
+ architecture,
8684
10593
  archiveFailures,
8685
10594
  archiveStream,
8686
10595
  buildDependencyGraph,
@@ -8690,6 +10599,7 @@ var VERSION = "1.8.2";
8690
10599
  checkEligibility,
8691
10600
  classifyFinding,
8692
10601
  configureFeedback,
10602
+ constraintRuleId,
8693
10603
  contextBudget,
8694
10604
  contextFilter,
8695
10605
  createBoundaryValidator,
@@ -8704,6 +10614,8 @@ var VERSION = "1.8.2";
8704
10614
  cryptoRules,
8705
10615
  deduplicateCleanupFindings,
8706
10616
  deduplicateFindings,
10617
+ deepMergeConstraints,
10618
+ defaultCollectors,
8707
10619
  defineLayer,
8708
10620
  deserializationRules,
8709
10621
  detectChangeType,
@@ -8716,9 +10628,12 @@ var VERSION = "1.8.2";
8716
10628
  detectPatternViolations,
8717
10629
  detectSizeBudgetViolations,
8718
10630
  detectStack,
10631
+ detectStaleConstraints,
8719
10632
  determineAssessment,
10633
+ diff,
8720
10634
  executeWorkflow,
8721
10635
  expressRules,
10636
+ extractBundle,
8722
10637
  extractMarkdownLinks,
8723
10638
  extractSections,
8724
10639
  fanOutReview,
@@ -8749,6 +10664,7 @@ var VERSION = "1.8.2";
8749
10664
  networkRules,
8750
10665
  nodeRules,
8751
10666
  parseDiff,
10667
+ parseManifest,
8752
10668
  parseRoadmap,
8753
10669
  parseSecurityConfig,
8754
10670
  parseSize,
@@ -8756,6 +10672,8 @@ var VERSION = "1.8.2";
8756
10672
  previewFix,
8757
10673
  reactRules,
8758
10674
  readCheckState,
10675
+ readLockfile,
10676
+ removeProvenance,
8759
10677
  requestMultiplePeerReviews,
8760
10678
  requestPeerReview,
8761
10679
  resetFeedbackConfig,
@@ -8763,6 +10681,8 @@ var VERSION = "1.8.2";
8763
10681
  resolveModelTier,
8764
10682
  resolveRuleSeverity,
8765
10683
  resolveStreamPath,
10684
+ resolveThresholds,
10685
+ runAll,
8766
10686
  runArchitectureAgent,
8767
10687
  runBugDetectionAgent,
8768
10688
  runCIChecks,
@@ -8782,6 +10702,7 @@ var VERSION = "1.8.2";
8782
10702
  setActiveStream,
8783
10703
  shouldRunCheck,
8784
10704
  spawnBackgroundCheck,
10705
+ syncConstraintNodes,
8785
10706
  syncRoadmap,
8786
10707
  touchStream,
8787
10708
  trackAction,
@@ -8794,6 +10715,9 @@ var VERSION = "1.8.2";
8794
10715
  validateFindings,
8795
10716
  validateKnowledgeMap,
8796
10717
  validatePatternConfig,
10718
+ violationId,
10719
+ writeConfig,
10720
+ writeLockfile,
8797
10721
  xssRules,
8798
10722
  ...require("@harness-engineering/types")
8799
10723
  });