@harness-engineering/cli 2.7.1 → 2.8.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.
Files changed (139) hide show
  1. package/dist/agents/personas/adversarial-reviewer.yaml +43 -0
  2. package/dist/agents/personas/frontend-races-reviewer.yaml +54 -0
  3. package/dist/agents/personas/typescript-strict-reviewer.yaml +53 -0
  4. package/dist/agents/skills/CHANGELOG.md +12 -0
  5. package/dist/agents/skills/claude-code/align-design-system/SKILL.md +18 -0
  6. package/dist/agents/skills/claude-code/align-design-system/skill.yaml +3 -0
  7. package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +37 -5
  8. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +107 -22
  9. package/dist/agents/skills/claude-code/harness-code-review/references/confidence-rubric.md +45 -0
  10. package/dist/agents/skills/claude-code/harness-code-review/references/risk-keywords.md +40 -0
  11. package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +3 -0
  12. package/dist/agents/skills/claude-code/harness-compound/SKILL.md +7 -4
  13. package/dist/agents/skills/claude-code/harness-ideate/SKILL.md +277 -0
  14. package/dist/agents/skills/claude-code/harness-ideate/references/scoring.md +88 -0
  15. package/dist/agents/skills/claude-code/harness-ideate/skill.yaml +66 -0
  16. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +14 -4
  17. package/dist/agents/skills/claude-code/harness-pulse/SKILL.md +12 -16
  18. package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +34 -6
  19. package/dist/agents/skills/claude-code/harness-strategy/SKILL.md +152 -0
  20. package/dist/agents/skills/claude-code/harness-strategy/references/interview.md +122 -0
  21. package/dist/agents/skills/claude-code/harness-strategy/skill.yaml +54 -0
  22. package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +76 -10
  23. package/dist/agents/skills/codex/align-design-system/SKILL.md +18 -0
  24. package/dist/agents/skills/codex/align-design-system/skill.yaml +3 -0
  25. package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +37 -5
  26. package/dist/agents/skills/codex/harness-code-review/SKILL.md +107 -22
  27. package/dist/agents/skills/codex/harness-code-review/references/confidence-rubric.md +45 -0
  28. package/dist/agents/skills/codex/harness-code-review/references/risk-keywords.md +40 -0
  29. package/dist/agents/skills/codex/harness-code-review/skill.yaml +3 -0
  30. package/dist/agents/skills/codex/harness-compound/SKILL.md +7 -4
  31. package/dist/agents/skills/codex/harness-ideate/SKILL.md +277 -0
  32. package/dist/agents/skills/codex/harness-ideate/references/scoring.md +88 -0
  33. package/dist/agents/skills/codex/harness-ideate/skill.yaml +66 -0
  34. package/dist/agents/skills/codex/harness-planning/SKILL.md +14 -4
  35. package/dist/agents/skills/codex/harness-pulse/SKILL.md +12 -16
  36. package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +34 -6
  37. package/dist/agents/skills/codex/harness-strategy/SKILL.md +152 -0
  38. package/dist/agents/skills/codex/harness-strategy/references/interview.md +122 -0
  39. package/dist/agents/skills/codex/harness-strategy/skill.yaml +54 -0
  40. package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +76 -10
  41. package/dist/agents/skills/cursor/align-design-system/SKILL.md +18 -0
  42. package/dist/agents/skills/cursor/align-design-system/skill.yaml +3 -0
  43. package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +37 -5
  44. package/dist/agents/skills/cursor/harness-code-review/SKILL.md +107 -22
  45. package/dist/agents/skills/cursor/harness-code-review/references/confidence-rubric.md +45 -0
  46. package/dist/agents/skills/cursor/harness-code-review/references/risk-keywords.md +40 -0
  47. package/dist/agents/skills/cursor/harness-code-review/skill.yaml +3 -0
  48. package/dist/agents/skills/cursor/harness-compound/SKILL.md +7 -4
  49. package/dist/agents/skills/cursor/harness-ideate/SKILL.md +277 -0
  50. package/dist/agents/skills/cursor/harness-ideate/references/scoring.md +88 -0
  51. package/dist/agents/skills/cursor/harness-ideate/skill.yaml +66 -0
  52. package/dist/agents/skills/cursor/harness-planning/SKILL.md +14 -4
  53. package/dist/agents/skills/cursor/harness-pulse/SKILL.md +12 -16
  54. package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +34 -6
  55. package/dist/agents/skills/cursor/harness-strategy/SKILL.md +152 -0
  56. package/dist/agents/skills/cursor/harness-strategy/references/interview.md +122 -0
  57. package/dist/agents/skills/cursor/harness-strategy/skill.yaml +54 -0
  58. package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +76 -10
  59. package/dist/agents/skills/gemini-cli/align-design-system/SKILL.md +18 -0
  60. package/dist/agents/skills/gemini-cli/align-design-system/skill.yaml +3 -0
  61. package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +37 -5
  62. package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +107 -22
  63. package/dist/agents/skills/gemini-cli/harness-code-review/references/confidence-rubric.md +45 -0
  64. package/dist/agents/skills/gemini-cli/harness-code-review/references/risk-keywords.md +40 -0
  65. package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +3 -0
  66. package/dist/agents/skills/gemini-cli/harness-compound/SKILL.md +7 -4
  67. package/dist/agents/skills/gemini-cli/harness-ideate/SKILL.md +277 -0
  68. package/dist/agents/skills/gemini-cli/harness-ideate/references/scoring.md +88 -0
  69. package/dist/agents/skills/gemini-cli/harness-ideate/skill.yaml +66 -0
  70. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +14 -4
  71. package/dist/agents/skills/gemini-cli/harness-pulse/SKILL.md +12 -16
  72. package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +34 -6
  73. package/dist/agents/skills/gemini-cli/harness-strategy/SKILL.md +152 -0
  74. package/dist/agents/skills/gemini-cli/harness-strategy/references/interview.md +122 -0
  75. package/dist/agents/skills/gemini-cli/harness-strategy/skill.yaml +54 -0
  76. package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +76 -10
  77. package/dist/agents/skills/package.json +1 -1
  78. package/dist/agents/skills/tests/harness-strategy.test.ts +145 -0
  79. package/dist/agents-md-WMJO5HSM.js +13 -0
  80. package/dist/analyzer-V633GUHY-MBCQWKDU.js +26 -0
  81. package/dist/{architecture-LUR2I67H.js → architecture-EVYVGPRZ.js} +6 -6
  82. package/dist/{assess-project-OJWECOP2.js → assess-project-KZG563SO.js} +1 -1
  83. package/dist/bin/harness-mcp.js +17 -17
  84. package/dist/bin/harness.js +27 -27
  85. package/dist/{check-phase-gate-HS5KYLRO.js → check-phase-gate-MK364VXS.js} +6 -6
  86. package/dist/{chunk-6UHDQP2N.js → chunk-2N4OENGE.js} +1 -1
  87. package/dist/{chunk-FC4JPM6W.js → chunk-2SNRCAC3.js} +3 -3
  88. package/dist/{chunk-LGYXR2KZ.js → chunk-4BDOSCNY.js} +137 -71
  89. package/dist/{chunk-N5NSSLMU.js → chunk-5XL5QJ5B.js} +2 -2
  90. package/dist/{chunk-LECXPXS3.js → chunk-7EQAZKJ5.js} +2 -2
  91. package/dist/{chunk-LYIPI2SJ.js → chunk-ACOOG3UW.js} +6 -6
  92. package/dist/{chunk-FXZI6NJ2.js → chunk-B7KMTHN3.js} +51 -4
  93. package/dist/{chunk-OH3NXXUY.js → chunk-BDGTZRZ6.js} +11 -1
  94. package/dist/{chunk-NIVWM7OW.js → chunk-C7J55C6E.js} +10 -10
  95. package/dist/{chunk-7PLIWP7U.js → chunk-CAQPSFPT.js} +4042 -738
  96. package/dist/{chunk-TIH53QXY.js → chunk-CLI4K2CH.js} +1 -1
  97. package/dist/{chunk-INBTDBV2.js → chunk-EEFRPEWT.js} +1 -1
  98. package/dist/{chunk-VI4IS55S.js → chunk-FZ5MBXEG.js} +6 -6
  99. package/dist/{chunk-WZY5U6NS.js → chunk-GOHWCHCS.js} +1809 -812
  100. package/dist/{chunk-VSIRUZQP.js → chunk-HRSE6IQD.js} +2 -2
  101. package/dist/{chunk-CZPOPMMI.js → chunk-KBCPELBC.js} +3 -3
  102. package/dist/{chunk-7AF6C5GE.js → chunk-KI6PHH6Q.js} +3 -3
  103. package/dist/{chunk-KN3EMDZ5.js → chunk-LADPYELQ.js} +23 -3
  104. package/dist/{chunk-Y4T6ENKQ.js → chunk-N55WOGN3.js} +86 -17
  105. package/dist/{chunk-H2ZJOJ7A.js → chunk-PP6ZRL5T.js} +316 -42
  106. package/dist/{chunk-RJ2WEES5.js → chunk-STESM73J.js} +2 -2
  107. package/dist/{chunk-B2VAYLYI.js → chunk-UBZQE3JN.js} +2 -2
  108. package/dist/{chunk-GGSNWH7P.js → chunk-UJFNIWC7.js} +5 -5
  109. package/dist/{chunk-FEKBXX2J.js → chunk-UUO4K7NX.js} +9 -9
  110. package/dist/{chunk-Z3TMCCZB.js → chunk-UXPWSV3B.js} +9 -9
  111. package/dist/{chunk-O2ASYKA6.js → chunk-VBNJAKUH.js} +2 -2
  112. package/dist/{chunk-PTHX545Q.js → chunk-X4EFYVCZ.js} +2 -2
  113. package/dist/{ci-workflow-MDSTHJOY.js → ci-workflow-IW2H5DC4.js} +5 -5
  114. package/dist/{dist-E3SAAHCM.js → dist-ICW4QUEW.js} +5 -1
  115. package/dist/{dist-72ID44UE.js → dist-JCFK263L.js} +52 -4
  116. package/dist/{dist-R3TOOPJ7.js → dist-OWHMNL4W.js} +1 -1
  117. package/dist/{docs-2RFOJ6XY.js → docs-7CP6VV42.js} +6 -6
  118. package/dist/engine-MKJEBWU7.js +13 -0
  119. package/dist/{entropy-3UX6644G.js → entropy-6JOZJYG7.js} +5 -5
  120. package/dist/{feedback-CVCFYND4.js → feedback-T65AEJEG.js} +1 -1
  121. package/dist/{generate-agent-definitions-HZ3XENWO.js → generate-agent-definitions-4VI4YTO2.js} +6 -6
  122. package/dist/{glob-helper-EX4YZKZO.js → glob-helper-GLZAURJC.js} +1 -1
  123. package/dist/{graph-loader-EIFEOUVI.js → graph-loader-HHXN5FLP.js} +1 -1
  124. package/dist/index.d.ts +202 -0
  125. package/dist/index.js +27 -27
  126. package/dist/{loader-6RHWU5FH.js → loader-AWTHHE3A.js} +5 -5
  127. package/dist/{mcp-OYR26JDI.js → mcp-JBIBINO2.js} +17 -17
  128. package/dist/{performance-JJMIRXCH.js → performance-NCOUDOMW.js} +6 -6
  129. package/dist/{review-pipeline-BHQZIXWZ.js → review-pipeline-YIC6JPMY.js} +5 -5
  130. package/dist/{runtime-DALTAVTN.js → runtime-5MHV4UEE.js} +5 -5
  131. package/dist/{scan-LYKLM4EQ.js → scan-P7YFKB77.js} +1 -1
  132. package/dist/{security-RMLMYFPB.js → security-VZPJNIDP.js} +1 -1
  133. package/dist/{validate-R7DZRDFK.js → validate-7BE4VTFI.js} +6 -6
  134. package/dist/validate-cross-check-F7GEBRDH.js +13 -0
  135. package/package.json +7 -7
  136. package/dist/agents-md-FKMKH6ZF.js +0 -13
  137. package/dist/analyzer-H3AHBFSL-KPOPJYRB.js +0 -26
  138. package/dist/engine-BSFKOGPN.js +0 -13
  139. package/dist/validate-cross-check-AUUZFNVL.js +0 -13
@@ -22,8 +22,8 @@ import {
22
22
  getDefaultRegistry,
23
23
  getOutline,
24
24
  getParser,
25
- init_chunk_I2JUTTPH,
26
25
  init_chunk_MUWJHO2S,
26
+ init_chunk_S4DTDJ6J,
27
27
  parseFile,
28
28
  parseSize,
29
29
  readFileContent,
@@ -31,7 +31,7 @@ import {
31
31
  resetParserCache,
32
32
  resolveFileToLayer,
33
33
  validateDependencies
34
- } from "./chunk-Y4T6ENKQ.js";
34
+ } from "./chunk-N55WOGN3.js";
35
35
  import {
36
36
  AuthAuditEntrySchema,
37
37
  AuthTokenPublicSchema,
@@ -51,6 +51,7 @@ import {
51
51
  NotificationSinkConfigSchema,
52
52
  NotificationSinkKindSchema,
53
53
  NotificationsConfigSchema,
54
+ OPTIONAL_STRATEGY_SECTIONS,
54
55
  OTLPKeyValueSchema,
55
56
  OTLPSpanSchema,
56
57
  Ok,
@@ -62,6 +63,7 @@ import {
62
63
  ProposalKindSchema,
63
64
  ProposalSourceSchema,
64
65
  ProposalStatusSchema,
66
+ REQUIRED_STRATEGY_SECTIONS,
65
67
  SESSIONS_DEFAULTS,
66
68
  SESSION_SECTION_NAMES,
67
69
  STANDARD_COGNITIVE_MODES,
@@ -77,7 +79,7 @@ import {
77
79
  init_dist,
78
80
  isErr,
79
81
  isOk
80
- } from "./chunk-OH3NXXUY.js";
82
+ } from "./chunk-BDGTZRZ6.js";
81
83
  import {
82
84
  DEFAULT_SKIP_DIRS,
83
85
  GraphStore,
@@ -85,7 +87,7 @@ import {
85
87
  init_dist as init_dist2,
86
88
  queryTraceability,
87
89
  skipDirGlobs
88
- } from "./chunk-H2ZJOJ7A.js";
90
+ } from "./chunk-PP6ZRL5T.js";
89
91
  import {
90
92
  ArchBaselineSchema,
91
93
  ArchConfigSchema,
@@ -1244,6 +1246,7 @@ var init_chunk_FYTJQ2ZY = __esm({
1244
1246
  // ../core/dist/index.mjs
1245
1247
  var dist_exports = {};
1246
1248
  __export(dist_exports, {
1249
+ ADVERSARIAL_DESCRIPTOR: () => ADVERSARIAL_DESCRIPTOR,
1247
1250
  AGENT_DESCRIPTORS: () => AGENT_DESCRIPTORS,
1248
1251
  AGREEMENT_LINE_GAP: () => AGREEMENT_LINE_GAP,
1249
1252
  ALLOWED_FIELD_KEYS: () => ALLOWED_FIELD_KEYS,
@@ -1271,6 +1274,7 @@ __export(dist_exports, {
1271
1274
  CACHE_TTL_MS: () => CACHE_TTL_MS,
1272
1275
  CINotifier: () => CINotifier,
1273
1276
  COMPLIANCE_DESCRIPTOR: () => COMPLIANCE_DESCRIPTOR,
1277
+ CONDITIONAL_SUBAGENT_DESCRIPTORS: () => CONDITIONAL_SUBAGENT_DESCRIPTORS,
1274
1278
  CORROBORATED_AGREEMENT: () => CORROBORATED_AGREEMENT,
1275
1279
  CacheMetricsRecorder: () => CacheMetricsRecorder,
1276
1280
  CategoryBaselineSchema: () => CategoryBaselineSchema,
@@ -1318,6 +1322,7 @@ __export(dist_exports, {
1318
1322
  Err: () => Err,
1319
1323
  ExclusionSet: () => ExclusionSet,
1320
1324
  FACTOR_WEIGHTS: () => FACTOR_WEIGHTS,
1325
+ FRONTEND_RACES_DESCRIPTOR: () => FRONTEND_RACES_DESCRIPTOR,
1321
1326
  FailureEntrySchema: () => FailureEntrySchema,
1322
1327
  FileSink: () => FileSink,
1323
1328
  FindingLifecycleSchema: () => FindingLifecycleSchema,
@@ -1351,6 +1356,7 @@ __export(dist_exports, {
1351
1356
  NotificationSinkConfigSchema: () => NotificationSinkConfigSchema,
1352
1357
  NotificationSinkKindSchema: () => NotificationSinkKindSchema,
1353
1358
  NotificationsConfigSchema: () => NotificationsConfigSchema,
1359
+ OPTIONAL_STRATEGY_SECTIONS: () => OPTIONAL_STRATEGY_SECTIONS,
1354
1360
  OTLPExporter: () => OTLPExporter,
1355
1361
  OTLPKeyValueSchema: () => OTLPKeyValueSchema,
1356
1362
  OTLPSpanSchema: () => OTLPSpanSchema,
@@ -1382,6 +1388,8 @@ __export(dist_exports, {
1382
1388
  PulseSourcesSchema: () => PulseSourcesSchema,
1383
1389
  QuestionSchema: () => QuestionSchema,
1384
1390
  REQUIRED_SECTIONS: () => REQUIRED_SECTIONS,
1391
+ REQUIRED_STRATEGY_SECTIONS: () => REQUIRED_STRATEGY_SECTIONS,
1392
+ RISK_KEYWORDS: () => RISK_KEYWORDS,
1385
1393
  RegressionDetector: () => RegressionDetector,
1386
1394
  RuleRegistry: () => RuleRegistry,
1387
1395
  SECURITY_DESCRIPTOR: () => SECURITY_DESCRIPTOR,
@@ -1391,6 +1399,7 @@ __export(dist_exports, {
1391
1399
  STANDALONE_AGREEMENT: () => STANDALONE_AGREEMENT,
1392
1400
  STANDARD_COGNITIVE_MODES: () => STANDARD_COGNITIVE_MODES,
1393
1401
  STATUS_RANK: () => STATUS_RANK,
1402
+ SUBAGENT_ORDER: () => SUBAGENT_ORDER,
1394
1403
  SecurityCategorySnapshotSchema: () => SecurityCategorySnapshotSchema,
1395
1404
  SecurityConfigSchema: () => SecurityConfigSchema,
1396
1405
  SecurityDirectionSchema: () => DirectionSchema2,
@@ -1414,10 +1423,13 @@ __export(dist_exports, {
1414
1423
  SpecImpactEstimator: () => SpecImpactEstimator,
1415
1424
  SpecImpactSignalsSchema: () => SpecImpactSignalsSchema,
1416
1425
  StabilityForecastSchema: () => StabilityForecastSchema,
1426
+ StrategyDocSchema: () => StrategyDocSchema,
1427
+ StrategyFrontmatterSchema: () => StrategyFrontmatterSchema,
1417
1428
  StreamIndexSchema: () => StreamIndexSchema,
1418
1429
  StreamInfoSchema: () => StreamInfoSchema,
1419
1430
  StructuralStrategy: () => StructuralStrategy,
1420
1431
  SupplyChainSnapshotSchema: () => SupplyChainSnapshotSchema,
1432
+ TYPESCRIPT_STRICT_DESCRIPTOR: () => TYPESCRIPT_STRICT_DESCRIPTOR,
1421
1433
  ThresholdConfigSchema: () => ThresholdConfigSchema,
1422
1434
  TimeToFixResultSchema: () => TimeToFixResultSchema,
1423
1435
  TimeToFixStatsSchema: () => TimeToFixStatsSchema,
@@ -1467,6 +1479,7 @@ __export(dist_exports, {
1467
1479
  archiveLearnings: () => archiveLearnings,
1468
1480
  archiveSession: () => archiveSession,
1469
1481
  archiveStream: () => archiveStream,
1482
+ asStrategyDoc: () => asStrategyDoc,
1470
1483
  assembleCandidateReport: () => assembleCandidateReport,
1471
1484
  assembleReport: () => assembleReport,
1472
1485
  assertPortUsable: () => assertPortUsable,
@@ -1477,6 +1490,7 @@ __export(dist_exports, {
1477
1490
  buildSnapshot: () => buildSnapshot,
1478
1491
  calculateCacheSavings: () => calculateCacheSavings,
1479
1492
  calculateCost: () => calculateCost,
1493
+ calibrateDepth: () => calibrateDepth,
1480
1494
  checkDocCoverage: () => checkDocCoverage,
1481
1495
  checkEligibility: () => checkEligibility,
1482
1496
  checkEvidenceCoverage: () => checkEvidenceCoverage,
@@ -1492,7 +1506,9 @@ __export(dist_exports, {
1492
1506
  clusterViolations: () => clusterViolations,
1493
1507
  collectEvents: () => collectEvents,
1494
1508
  composeInsights: () => composeInsights,
1509
+ computeActivations: () => computeActivations,
1495
1510
  computeContentHash: () => computeContentHash,
1511
+ computeDepth: () => computeDepth,
1496
1512
  computeHotspots: () => computeHotspots,
1497
1513
  computeLexicalSimilarity: () => computeLexicalSimilarity,
1498
1514
  computeLoadPlan: () => computeLoadPlan,
@@ -1504,6 +1520,7 @@ __export(dist_exports, {
1504
1520
  constraintRuleId: () => constraintRuleId,
1505
1521
  contextBudget: () => contextBudget,
1506
1522
  contextFilter: () => contextFilter,
1523
+ countChangedLines: () => countChangedLines,
1507
1524
  countLearningEntries: () => countLearningEntries,
1508
1525
  createBoundaryValidator: () => createBoundaryValidator,
1509
1526
  createCommentedCodeFixes: () => createCommentedCodeFixes,
@@ -1537,6 +1554,7 @@ __export(dist_exports, {
1537
1554
  detectEmergentConstraints: () => detectEmergentConstraints,
1538
1555
  detectLanguage: () => detectLanguage,
1539
1556
  detectPatternViolations: () => detectPatternViolations,
1557
+ detectRiskKeywords: () => detectRiskKeywords,
1540
1558
  detectSizeBudgetViolations: () => detectSizeBudgetViolations,
1541
1559
  detectStack: () => detectStack,
1542
1560
  detectStaleConstraints: () => detectStaleConstraints,
@@ -1555,6 +1573,7 @@ __export(dist_exports, {
1555
1573
  extractLevel: () => extractLevel,
1556
1574
  extractMarkdownLinks: () => extractMarkdownLinks,
1557
1575
  extractSections: () => extractSections,
1576
+ fanOutConditionalSubagents: () => fanOutConditionalSubagents,
1558
1577
  fanOutReview: () => fanOutReview,
1559
1578
  findParallelGroups: () => findParallelGroups,
1560
1579
  formatCIReportAsMarkdown: () => formatCIReportAsMarkdown,
@@ -1647,6 +1666,7 @@ __export(dist_exports, {
1647
1666
  parseSections: () => parseSections,
1648
1667
  parseSecurityConfig: () => parseSecurityConfig,
1649
1668
  parseSize: () => parseSize,
1669
+ parseStrategyDoc: () => parseStrategyDoc,
1650
1670
  pathTraversalRules: () => pathTraversalRules,
1651
1671
  previewFix: () => previewFix,
1652
1672
  projectValue: () => projectValue,
@@ -1679,12 +1699,14 @@ __export(dist_exports, {
1679
1699
  resolveStability: () => resolveStability,
1680
1700
  resolveStreamPath: () => resolveStreamPath,
1681
1701
  resolveThresholds: () => resolveThresholds,
1702
+ runAdversarialAgent: () => runAdversarialAgent,
1682
1703
  runAgentConfigFallbackRules: () => runFallbackRules,
1683
1704
  runAll: () => runAll,
1684
1705
  runArchitectureAgent: () => runArchitectureAgent,
1685
1706
  runBugDetectionAgent: () => runBugDetectionAgent,
1686
1707
  runCIChecks: () => runCIChecks,
1687
1708
  runComplianceAgent: () => runComplianceAgent,
1709
+ runFrontendRacesAgent: () => runFrontendRacesAgent,
1688
1710
  runMechanicalChecks: () => runMechanicalChecks,
1689
1711
  runMechanicalGate: () => runMechanicalGate,
1690
1712
  runMultiTurnPipeline: () => runMultiTurnPipeline,
@@ -1692,6 +1714,7 @@ __export(dist_exports, {
1692
1714
  runPulse: () => runPulse,
1693
1715
  runReviewPipeline: () => runReviewPipeline,
1694
1716
  runSecurityAgent: () => runSecurityAgent,
1717
+ runTypescriptStrictAgent: () => runTypescriptStrictAgent,
1695
1718
  saveHandoff: () => saveHandoff,
1696
1719
  saveState: () => saveState,
1697
1720
  saveStreamIndex: () => saveStreamIndex,
@@ -1707,6 +1730,7 @@ __export(dist_exports, {
1707
1730
  send: () => send,
1708
1731
  serializeEnvelope: () => serializeEnvelope,
1709
1732
  serializeRoadmap: () => serializeRoadmap,
1733
+ serializeStrategyDoc: () => serializeStrategyDoc,
1710
1734
  setActiveStream: () => setActiveStream,
1711
1735
  sharpEdgesRules: () => sharpEdgesRules,
1712
1736
  shouldRunCheck: () => shouldRunCheck,
@@ -1741,6 +1765,7 @@ __export(dist_exports, {
1741
1765
  validatePulseConfig: () => validatePulseConfig,
1742
1766
  validateRoadmapMode: () => validateRoadmapMode,
1743
1767
  validateSolutionsDir: () => validateSolutionsDir,
1768
+ validateStrategy: () => validateStrategy,
1744
1769
  violationId: () => violationId,
1745
1770
  weeksUntilThreshold: () => weeksUntilThreshold,
1746
1771
  weightedLinearRegression: () => weightedLinearRegression,
@@ -1748,6 +1773,7 @@ __export(dist_exports, {
1748
1773
  writeLockfile: () => writeLockfile,
1749
1774
  writePulseConfig: () => writePulseConfig,
1750
1775
  writeSessionSummary: () => writeSessionSummary,
1776
+ writeStrategyDoc: () => writeStrategyDoc,
1751
1777
  writeTaint: () => writeTaint,
1752
1778
  xssRules: () => xssRules
1753
1779
  });
@@ -1758,84 +1784,86 @@ import * as fs2 from "fs/promises";
1758
1784
  import * as path2 from "path";
1759
1785
  import matter from "gray-matter";
1760
1786
  import { z as z2 } from "zod";
1787
+ import * as fs3 from "fs/promises";
1788
+ import * as path3 from "path";
1789
+ import matter2 from "gray-matter";
1790
+ import { z as z3 } from "zod";
1761
1791
  import { spawn } from "child_process";
1762
1792
  import { existsSync as existsSync2 } from "fs";
1763
- import { join as join32, isAbsolute } from "path";
1793
+ import { join as join42, isAbsolute } from "path";
1764
1794
  import { existsSync as existsSync22 } from "fs";
1765
- import { join as join42 } from "path";
1795
+ import { join as join52 } from "path";
1766
1796
  import { readFileSync as readFileSync2, statSync } from "fs";
1767
1797
  import { glob } from "glob";
1768
1798
  import { existsSync as existsSync3 } from "fs";
1769
- import { isAbsolute as isAbsolute2, resolve as resolve2, join as join52 } from "path";
1799
+ import { isAbsolute as isAbsolute2, resolve as resolve2, join as join62 } from "path";
1770
1800
  import { glob as glob2 } from "glob";
1771
1801
  import { existsSync as existsSync4 } from "fs";
1772
- import { basename, dirname as dirname3, join as join6 } from "path";
1802
+ import { basename, dirname as dirname3, join as join7 } from "path";
1773
1803
  import { existsSync as existsSync5 } from "fs";
1774
- import { join as join7 } from "path";
1804
+ import { join as join8 } from "path";
1775
1805
  import { existsSync as existsSync6 } from "fs";
1776
- import { join as join9 } from "path";
1777
- import { join as join8, dirname as dirname22 } from "path";
1806
+ import { join as join10 } from "path";
1807
+ import { join as join9, dirname as dirname22 } from "path";
1778
1808
  import { glob as glob3 } from "glob";
1779
1809
  import { existsSync as existsSync7 } from "fs";
1780
1810
  import { dirname as dirname32, resolve as resolve22, isAbsolute as isAbsolute3 } from "path";
1781
1811
  import { existsSync as existsSync8 } from "fs";
1782
- import { join as join10 } from "path";
1812
+ import { join as join11 } from "path";
1783
1813
  import { glob as glob4 } from "glob";
1784
1814
  import { existsSync as existsSync9 } from "fs";
1785
- import { join as join11 } from "path";
1786
- import { existsSync as existsSync10 } from "fs";
1787
1815
  import { join as join12 } from "path";
1788
- import * as fs3 from "fs";
1789
- import * as path3 from "path";
1816
+ import { existsSync as existsSync10 } from "fs";
1817
+ import { join as join13 } from "path";
1818
+ import * as fs4 from "fs";
1819
+ import * as path4 from "path";
1790
1820
  import { minimatch } from "minimatch";
1791
1821
  import { minimatch as minimatch2 } from "minimatch";
1792
1822
  import { basename as basename2 } from "path";
1793
- import { join as join14, basename as basename3 } from "path";
1823
+ import { join as join15, basename as basename3 } from "path";
1794
1824
  import { basename as basename4, dirname as dirname4 } from "path";
1795
- import { z as z3 } from "zod";
1796
- import * as fs4 from "fs/promises";
1797
- import * as fs5 from "fs/promises";
1798
- import * as fs6 from "fs";
1799
- import { promisify } from "util";
1800
- import { dirname as dirname5, basename as basename5, join as join15 } from "path";
1801
1825
  import { z as z4 } from "zod";
1826
+ import * as fs5 from "fs/promises";
1827
+ import * as fs6 from "fs/promises";
1802
1828
  import * as fs7 from "fs";
1803
- import * as path4 from "path";
1804
- import { readFileSync as readFileSync32, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync13 } from "fs";
1805
- import { join as join17, dirname as dirname6 } from "path";
1806
- import { execFileSync } from "child_process";
1829
+ import { promisify } from "util";
1830
+ import { dirname as dirname5, basename as basename5, join as join16 } from "path";
1831
+ import { z as z5 } from "zod";
1807
1832
  import * as fs8 from "fs";
1808
1833
  import * as path5 from "path";
1809
- import { appendFileSync, writeFileSync as writeFileSync22, existsSync as existsSync14, mkdirSync as mkdirSync22 } from "fs";
1810
- import { dirname as dirname7 } from "path";
1811
- import { z as z5 } from "zod";
1834
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync13 } from "fs";
1835
+ import { join as join18, dirname as dirname6 } from "path";
1836
+ import { execFileSync } from "child_process";
1812
1837
  import * as fs9 from "fs";
1813
1838
  import * as path6 from "path";
1839
+ import { appendFileSync, writeFileSync as writeFileSync22, existsSync as existsSync14, mkdirSync as mkdirSync22 } from "fs";
1840
+ import { dirname as dirname7 } from "path";
1841
+ import { z as z6 } from "zod";
1814
1842
  import * as fs10 from "fs";
1815
1843
  import * as path7 from "path";
1816
1844
  import * as fs11 from "fs";
1817
1845
  import * as path8 from "path";
1818
- import { z as z6 } from "zod";
1819
- import * as fs15 from "fs";
1820
- import * as path12 from "path";
1821
- import * as fs14 from "fs";
1822
- import * as path11 from "path";
1823
1846
  import * as fs12 from "fs";
1824
1847
  import * as path9 from "path";
1825
- import { execSync } from "child_process";
1826
1848
  import { z as z7 } from "zod";
1827
- import * as fs13 from "fs";
1828
- import * as path10 from "path";
1829
1849
  import * as fs16 from "fs";
1830
1850
  import * as path13 from "path";
1831
- import * as crypto2 from "crypto";
1851
+ import * as fs15 from "fs";
1852
+ import * as path12 from "path";
1853
+ import * as fs13 from "fs";
1854
+ import * as path10 from "path";
1855
+ import { execSync } from "child_process";
1856
+ import { z as z8 } from "zod";
1857
+ import * as fs14 from "fs";
1858
+ import * as path11 from "path";
1832
1859
  import * as fs17 from "fs";
1833
1860
  import * as path14 from "path";
1861
+ import * as crypto2 from "crypto";
1834
1862
  import * as fs18 from "fs";
1835
1863
  import * as path15 from "path";
1836
- import * as crypto3 from "crypto";
1837
1864
  import * as fs19 from "fs";
1838
1865
  import * as path16 from "path";
1866
+ import * as crypto3 from "crypto";
1839
1867
  import * as fs20 from "fs";
1840
1868
  import * as path17 from "path";
1841
1869
  import * as fs21 from "fs";
@@ -1844,90 +1872,94 @@ import * as fs22 from "fs";
1844
1872
  import * as path19 from "path";
1845
1873
  import * as fs23 from "fs";
1846
1874
  import * as path20 from "path";
1847
- import { execSync as execSync2 } from "child_process";
1848
1875
  import * as fs24 from "fs";
1849
1876
  import * as path21 from "path";
1877
+ import { execSync as execSync2 } from "child_process";
1850
1878
  import * as fs25 from "fs";
1851
1879
  import * as path22 from "path";
1852
1880
  import * as fs26 from "fs";
1853
1881
  import * as path23 from "path";
1854
1882
  import * as fs27 from "fs";
1855
1883
  import * as path24 from "path";
1856
- import { z as z8 } from "zod";
1857
- import * as fs29 from "fs/promises";
1858
- import { minimatch as minimatch3 } from "minimatch";
1859
- import { z as z9 } from "zod";
1860
1884
  import * as fs28 from "fs";
1861
1885
  import * as path25 from "path";
1862
- import * as fs30 from "fs";
1886
+ import { z as z9 } from "zod";
1887
+ import * as fs30 from "fs/promises";
1888
+ import { minimatch as minimatch3 } from "minimatch";
1889
+ import { z as z10 } from "zod";
1890
+ import * as fs29 from "fs";
1863
1891
  import * as path26 from "path";
1892
+ import * as fs31 from "fs";
1893
+ import * as path27 from "path";
1864
1894
  import { readFileSync as readFileSync22, writeFileSync as writeFileSync14, unlinkSync as unlinkSync2, mkdirSync as mkdirSync13, readdirSync as readdirSync3 } from "fs";
1865
- import { join as join382, dirname as dirname10 } from "path";
1895
+ import { join as join39, dirname as dirname10 } from "path";
1866
1896
  import { createHash as createHash32 } from "crypto";
1867
- import { z as z10 } from "zod";
1897
+ import { z as z11 } from "zod";
1868
1898
  import { readFileSync as readFileSync23, writeFileSync as writeFileSync15, renameSync as renameSync72, mkdirSync as mkdirSync14, existsSync as existsSync342 } from "fs";
1869
1899
  import { execSync as execSync3 } from "child_process";
1870
1900
  import { randomBytes as randomBytes2 } from "crypto";
1871
- import { isAbsolute as isAbsolute4, join as join39, relative as relative3, dirname as dirname11 } from "path";
1872
- import * as path27 from "path";
1901
+ import { isAbsolute as isAbsolute4, join as join40, relative as relative3, dirname as dirname11 } from "path";
1873
1902
  import * as path28 from "path";
1874
1903
  import * as path29 from "path";
1875
1904
  import * as path30 from "path";
1876
- import * as fs31 from "fs";
1877
1905
  import * as path31 from "path";
1878
1906
  import * as fs32 from "fs";
1879
1907
  import * as path32 from "path";
1880
1908
  import * as fs33 from "fs";
1881
- import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
1882
- import * as fs34 from "fs";
1883
1909
  import * as path33 from "path";
1910
+ import * as fs34 from "fs";
1911
+ import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
1884
1912
  import * as fs35 from "fs";
1885
1913
  import * as path34 from "path";
1886
- import { createHash as createHash4 } from "crypto";
1914
+ import * as fs36 from "fs";
1887
1915
  import * as path35 from "path";
1888
- import { z as z11 } from "zod";
1889
- import * as fs36 from "fs/promises";
1916
+ import { createHash as createHash4 } from "crypto";
1890
1917
  import * as path36 from "path";
1918
+ import { z as z12 } from "zod";
1891
1919
  import * as fs37 from "fs/promises";
1892
1920
  import * as path37 from "path";
1893
- import * as ejs from "ejs";
1894
- import * as fs38 from "fs";
1921
+ import * as fs38 from "fs/promises";
1895
1922
  import * as path38 from "path";
1923
+ import * as ejs from "ejs";
1924
+ import * as fs39 from "fs";
1925
+ import * as path39 from "path";
1896
1926
  import * as os from "os";
1897
1927
  import { spawn as spawn2 } from "child_process";
1898
- import * as fs39 from "fs/promises";
1899
- import * as path39 from "path";
1900
- import * as fs40 from "fs";
1928
+ import * as fs40 from "fs/promises";
1901
1929
  import * as path40 from "path";
1902
1930
  import * as fs41 from "fs";
1903
1931
  import * as path41 from "path";
1904
- import * as os2 from "os";
1905
1932
  import * as fs42 from "fs";
1906
1933
  import * as path42 from "path";
1934
+ import * as os2 from "os";
1907
1935
  import * as fs43 from "fs";
1908
1936
  import * as path43 from "path";
1909
- import * as fs45 from "fs";
1910
- import * as path45 from "path";
1911
- import { execFileSync as execFileSync2 } from "child_process";
1912
1937
  import * as fs44 from "fs";
1913
1938
  import * as path44 from "path";
1914
- import * as crypto4 from "crypto";
1915
1939
  import * as fs46 from "fs";
1916
1940
  import * as path46 from "path";
1941
+ import { execFileSync as execFileSync2 } from "child_process";
1942
+ import * as fs45 from "fs";
1943
+ import * as path45 from "path";
1944
+ import * as crypto4 from "crypto";
1917
1945
  import * as fs47 from "fs";
1918
- import * as fs48 from "fs";
1919
1946
  import * as path47 from "path";
1947
+ import * as fs48 from "fs";
1948
+ import * as fs49 from "fs";
1949
+ import * as path48 from "path";
1920
1950
  import { readFileSync as readFileSync392 } from "fs";
1921
1951
  import { fileURLToPath } from "url";
1922
- import { dirname as dirname16, join as join612 } from "path";
1952
+ import { dirname as dirname16, join as join622 } from "path";
1923
1953
  import { execFile } from "child_process";
1924
1954
  import { promisify as promisify2 } from "util";
1925
1955
  import { execFile as execFile2 } from "child_process";
1926
1956
  import { promisify as promisify3 } from "util";
1927
- import * as fs49 from "fs/promises";
1928
- import * as path48 from "path";
1929
- import * as fs50 from "fs";
1957
+ import * as fs50 from "fs/promises";
1930
1958
  import * as path49 from "path";
1959
+ import * as fs51 from "fs";
1960
+ import * as path50 from "path";
1961
+ import * as fs52 from "fs";
1962
+ import * as path51 from "path";
1931
1963
  import { randomUUID as randomUUID2 } from "crypto";
1932
1964
  function isBadPort(port) {
1933
1965
  return BAD_PORT_SET.has(port);
@@ -1974,15 +2006,15 @@ function validateConfig(data, schema) {
1974
2006
  let message = "Configuration validation failed";
1975
2007
  const suggestions = [];
1976
2008
  if (firstError) {
1977
- const path50 = firstError.path.join(".");
1978
- const pathDisplay = path50 ? ` at "${path50}"` : "";
2009
+ const path52 = firstError.path.join(".");
2010
+ const pathDisplay = path52 ? ` at "${path52}"` : "";
1979
2011
  if (firstError.code === "invalid_type") {
1980
2012
  const received = firstError.received;
1981
2013
  const expected = firstError.expected;
1982
2014
  if (received === "undefined") {
1983
2015
  code = "MISSING_FIELD";
1984
2016
  message = `Missing required field${pathDisplay}: ${firstError.message}`;
1985
- suggestions.push(`Field "${path50}" is required and must be of type "${expected}"`);
2017
+ suggestions.push(`Field "${path52}" is required and must be of type "${expected}"`);
1986
2018
  } else {
1987
2019
  code = "INVALID_TYPE";
1988
2020
  message = `Invalid type${pathDisplay}: ${firstError.message}`;
@@ -2118,23 +2150,127 @@ async function validatePulseConfig(cwd) {
2118
2150
  if (!result.ok) return Err(result.error);
2119
2151
  return Ok({ present: true, valid: true });
2120
2152
  }
2153
+ function coerceFrontmatter(raw) {
2154
+ if (raw === null || typeof raw !== "object") return raw;
2155
+ const out = { ...raw };
2156
+ const lu = out.last_updated;
2157
+ if (lu instanceof Date && !Number.isNaN(lu.getTime())) {
2158
+ const iso = lu.toISOString();
2159
+ out.last_updated = iso.slice(0, 10);
2160
+ }
2161
+ return out;
2162
+ }
2163
+ function findH2Matches(body) {
2164
+ const h2Re = /^##[ \t]+(.+?)[ \t]*$/gm;
2165
+ const matches = [];
2166
+ let m;
2167
+ while ((m = h2Re.exec(body)) !== null) {
2168
+ matches.push({
2169
+ name: (m[1] ?? "").trim(),
2170
+ headingStart: m.index,
2171
+ bodyStart: m.index + m[0].length
2172
+ });
2173
+ }
2174
+ return matches;
2175
+ }
2176
+ function accumulateSection(acc, name, body) {
2177
+ if (KNOWN_SECTION_NAMES.has(name)) {
2178
+ acc.sections.push({ name, body });
2179
+ } else {
2180
+ acc.unknownSectionNames.push(name);
2181
+ }
2182
+ }
2183
+ function buildSections(body, matches) {
2184
+ const acc = { sections: [], unknownSectionNames: [] };
2185
+ for (let i = 0; i < matches.length; i++) {
2186
+ const current = matches[i];
2187
+ if (current === void 0) continue;
2188
+ const sliceEnd = matches[i + 1]?.headingStart ?? body.length;
2189
+ const sectionBody = body.slice(current.bodyStart, sliceEnd).trim();
2190
+ accumulateSection(acc, current.name, sectionBody);
2191
+ }
2192
+ return acc;
2193
+ }
2194
+ function parseStrategyDoc(raw) {
2195
+ const parsed = matter(raw);
2196
+ const body = parsed.content;
2197
+ const frontmatter = coerceFrontmatter(parsed.data);
2198
+ const matches = findH2Matches(body);
2199
+ const { sections, unknownSectionNames } = buildSections(body, matches);
2200
+ return { frontmatter, sections, unknownSectionNames };
2201
+ }
2202
+ function asStrategyDoc(parsed) {
2203
+ const fm = parsed.frontmatter;
2204
+ if (fm === null || typeof fm !== "object" || typeof fm.name !== "string" || typeof fm.last_updated !== "string" || typeof fm.version !== "number") {
2205
+ return null;
2206
+ }
2207
+ return {
2208
+ frontmatter: {
2209
+ name: fm.name,
2210
+ last_updated: fm.last_updated,
2211
+ version: fm.version
2212
+ },
2213
+ sections: parsed.sections
2214
+ };
2215
+ }
2216
+ function nonBlankLines(body) {
2217
+ return body.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
2218
+ }
2219
+ function bodyContentIssue(body) {
2220
+ const lines = nonBlankLines(body);
2221
+ if (lines.length === 0) {
2222
+ return "section is empty";
2223
+ }
2224
+ for (const line of lines) {
2225
+ if (PLACEHOLDER_INLINE.test(line)) {
2226
+ return `unfilled template placeholder detected (${line})`;
2227
+ }
2228
+ }
2229
+ return null;
2230
+ }
2231
+ async function validateStrategy(cwd) {
2232
+ const strategyPath = path2.join(cwd, "STRATEGY.md");
2233
+ let raw;
2234
+ try {
2235
+ raw = await fs2.readFile(strategyPath, "utf-8");
2236
+ } catch {
2237
+ return Ok({ present: false, valid: true });
2238
+ }
2239
+ const parsed = parseStrategyDoc(raw);
2240
+ const doc = asStrategyDoc(parsed);
2241
+ if (doc === null) {
2242
+ return Err(
2243
+ createError(
2244
+ "VALIDATION_FAILED",
2245
+ "STRATEGY.md frontmatter is missing required fields (name, last_updated, version)",
2246
+ {},
2247
+ [
2248
+ "Add YAML frontmatter at the top of STRATEGY.md with name (string), last_updated (YYYY-MM-DD), and version (positive integer)"
2249
+ ]
2250
+ )
2251
+ );
2252
+ }
2253
+ const result = validateConfig(doc, StrategyDocSchema);
2254
+ if (!result.ok) return Err(result.error);
2255
+ return Ok({ present: true, valid: true });
2256
+ }
2121
2257
  async function* walk(dir) {
2122
2258
  let entries;
2123
2259
  try {
2124
- entries = await fs2.readdir(dir, { withFileTypes: true });
2260
+ entries = await fs3.readdir(dir, { withFileTypes: true });
2125
2261
  } catch {
2126
2262
  return;
2127
2263
  }
2128
2264
  for (const e of entries) {
2129
- const p = path2.join(dir, e.name);
2265
+ const p = path3.join(dir, e.name);
2130
2266
  if (e.isDirectory()) yield* walk(p);
2131
2267
  else if (e.isFile() && e.name.endsWith(".md")) yield p;
2132
2268
  }
2133
2269
  }
2134
2270
  async function validateSolutionsDir(cwd) {
2135
- const root = path2.join(cwd, "docs", "solutions");
2271
+ const root = path3.join(cwd, "docs", "solutions");
2136
2272
  try {
2137
- await fs2.stat(root);
2273
+ await fs3.stat(root);
2138
2274
  } catch {
2139
2275
  return Ok({ filesChecked: 0, issues: [] });
2140
2276
  }
@@ -2142,15 +2278,15 @@ async function validateSolutionsDir(cwd) {
2142
2278
  let count = 0;
2143
2279
  for (const [track, categories] of TRACK_CATEGORIES) {
2144
2280
  for (const category of categories) {
2145
- const categoryDir = path2.join(root, track, category);
2281
+ const categoryDir = path3.join(root, track, category);
2146
2282
  for await (const file of walk(categoryDir)) {
2147
2283
  count++;
2148
- const raw = await fs2.readFile(file, "utf-8");
2149
- const parsed = matter(raw);
2284
+ const raw = await fs3.readFile(file, "utf-8");
2285
+ const parsed = matter2(raw);
2150
2286
  const result = validateConfig(parsed.data, SolutionDocFrontmatterSchema);
2151
2287
  if (!result.ok)
2152
2288
  issues.push({
2153
- file: path2.relative(cwd, file).replaceAll("\\", "/"),
2289
+ file: path3.relative(cwd, file).replaceAll("\\", "/"),
2154
2290
  message: result.error.message
2155
2291
  });
2156
2292
  }
@@ -2188,7 +2324,7 @@ function lookupOnPath() {
2188
2324
  function firstExistingIn(dir, candidates) {
2189
2325
  if (!dir) return null;
2190
2326
  for (const name of candidates) {
2191
- const candidate = isAbsolute(dir) ? join32(dir, name) : join32(process.cwd(), dir, name);
2327
+ const candidate = isAbsolute(dir) ? join42(dir, name) : join42(process.cwd(), dir, name);
2192
2328
  if (existsSync2(candidate)) return candidate;
2193
2329
  }
2194
2330
  return null;
@@ -2265,7 +2401,7 @@ function extractDiagnostics(parsed) {
2265
2401
  }
2266
2402
  function normalizeDiagnostic(diag, cwd) {
2267
2403
  const finding = {
2268
- file: normalizeFilePath(readFile32(diag), cwd),
2404
+ file: normalizeFilePath(readFile42(diag), cwd),
2269
2405
  ruleId: readRuleId(diag),
2270
2406
  severity: normalizeSeverity(readSeverity(diag)),
2271
2407
  message: diag.message ?? "agnix diagnostic"
@@ -2273,7 +2409,7 @@ function normalizeDiagnostic(diag, cwd) {
2273
2409
  attachOptionalFields(finding, diag);
2274
2410
  return finding;
2275
2411
  }
2276
- function readFile32(diag) {
2412
+ function readFile42(diag) {
2277
2413
  return diag.file ?? diag.path ?? "";
2278
2414
  }
2279
2415
  function readRuleId(diag) {
@@ -2316,16 +2452,16 @@ function makeFinding(input) {
2316
2452
  if (input.suggestion) finding.suggestion = input.suggestion;
2317
2453
  return finding;
2318
2454
  }
2319
- function readTextSafe(path50) {
2455
+ function readTextSafe(path52) {
2320
2456
  try {
2321
- return readFileSync2(path50, "utf-8");
2457
+ return readFileSync2(path52, "utf-8");
2322
2458
  } catch {
2323
2459
  return null;
2324
2460
  }
2325
2461
  }
2326
- function safeFileSize(path50) {
2462
+ function safeFileSize(path52) {
2327
2463
  try {
2328
- return statSync(path50).size;
2464
+ return statSync(path52).size;
2329
2465
  } catch {
2330
2466
  return null;
2331
2467
  }
@@ -2362,7 +2498,7 @@ function stripQuotes(value) {
2362
2498
  function runClaudeMdRules(cwd) {
2363
2499
  const findings = [];
2364
2500
  for (const name of CONTEXT_FILES) {
2365
- const abs = join42(cwd, name);
2501
+ const abs = join52(cwd, name);
2366
2502
  if (!existsSync22(abs)) continue;
2367
2503
  findings.push(...inspectContextFile(abs, name));
2368
2504
  }
@@ -2509,7 +2645,7 @@ function appendDescriptionFindings(out, rel, description) {
2509
2645
  function runHookRules(cwd) {
2510
2646
  const findings = [];
2511
2647
  for (const rel of SETTINGS_PATHS) {
2512
- const abs = join52(cwd, rel);
2648
+ const abs = join62(cwd, rel);
2513
2649
  if (!existsSync3(abs)) continue;
2514
2650
  const content = readTextSafe(abs);
2515
2651
  if (content === null) continue;
@@ -2625,7 +2761,7 @@ async function runSkillRules(cwd) {
2625
2761
  for (const abs of skillFiles) {
2626
2762
  const rel = relPath(cwd, abs);
2627
2763
  const dirName = basename(dirname3(abs));
2628
- const siblingYaml = join6(dirname3(abs), "skill.yaml");
2764
+ const siblingYaml = join7(dirname3(abs), "skill.yaml");
2629
2765
  if (existsSync4(siblingYaml)) {
2630
2766
  const yamlFields = parseFrontmatterFields(readTextSafe(siblingYaml) ?? "");
2631
2767
  findings.push(...checkSkillMetadata(relPath(cwd, siblingYaml), dirName, yamlFields));
@@ -2731,7 +2867,7 @@ function runMcpRules(cwd) {
2731
2867
  return findings;
2732
2868
  }
2733
2869
  function loadMcpServers(cwd, rel) {
2734
- const abs = join7(cwd, rel);
2870
+ const abs = join8(cwd, rel);
2735
2871
  if (!existsSync5(abs)) return null;
2736
2872
  const content = readTextSafe(abs);
2737
2873
  if (content === null) return null;
@@ -2870,27 +3006,27 @@ function extractSections(content) {
2870
3006
  }
2871
3007
  return sections.map((section) => buildAgentMapSection(section, lines));
2872
3008
  }
2873
- function isExternalLink(path50) {
2874
- return path50.startsWith("http://") || path50.startsWith("https://") || path50.startsWith("#") || path50.startsWith("mailto:");
3009
+ function isExternalLink(path52) {
3010
+ return path52.startsWith("http://") || path52.startsWith("https://") || path52.startsWith("#") || path52.startsWith("mailto:");
2875
3011
  }
2876
3012
  function resolveLinkPath(linkPath, baseDir) {
2877
- return linkPath.startsWith(".") ? join8(baseDir, linkPath) : linkPath;
3013
+ return linkPath.startsWith(".") ? join9(baseDir, linkPath) : linkPath;
2878
3014
  }
2879
- async function validateAgentsMap(path50 = "./AGENTS.md") {
2880
- const contentResult = await readFileContent(path50);
3015
+ async function validateAgentsMap(path52 = "./AGENTS.md") {
3016
+ const contentResult = await readFileContent(path52);
2881
3017
  if (!contentResult.ok) {
2882
3018
  return Err(
2883
3019
  createError(
2884
3020
  "PARSE_ERROR",
2885
3021
  `Failed to read AGENTS.md: ${contentResult.error.message}`,
2886
- { path: path50 },
3022
+ { path: path52 },
2887
3023
  ["Ensure the file exists", "Check file permissions"]
2888
3024
  )
2889
3025
  );
2890
3026
  }
2891
3027
  const content = contentResult.value;
2892
3028
  const sections = extractSections(content);
2893
- const baseDir = dirname22(path50);
3029
+ const baseDir = dirname22(path52);
2894
3030
  const sectionTitles = sections.map((s) => s.title);
2895
3031
  const missingSections = REQUIRED_SECTIONS.filter(
2896
3032
  (required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
@@ -2929,7 +3065,7 @@ async function validateAgentsMap(path50 = "./AGENTS.md") {
2929
3065
  });
2930
3066
  }
2931
3067
  async function runAgentsMdRules(cwd) {
2932
- const abs = join9(cwd, "AGENTS.md");
3068
+ const abs = join10(cwd, "AGENTS.md");
2933
3069
  if (!existsSync6(abs)) return [];
2934
3070
  const result = await validateAgentsMap(abs);
2935
3071
  if (result.ok) return [];
@@ -2986,7 +3122,7 @@ function extractLinkTargets(content) {
2986
3122
  function runSettingsJsonRules(cwd) {
2987
3123
  const findings = [];
2988
3124
  for (const rel of SETTINGS_FILES) {
2989
- const abs = join10(cwd, rel);
3125
+ const abs = join11(cwd, rel);
2990
3126
  if (!existsSync8(abs)) continue;
2991
3127
  const content = readTextSafe(abs);
2992
3128
  if (content === null) continue;
@@ -3062,15 +3198,15 @@ function stripQuotes2(value) {
3062
3198
  return value;
3063
3199
  }
3064
3200
  function skillExists(cwd, slug) {
3065
- if (existsSync9(join11(cwd, "agents", "skills", slug, "SKILL.md"))) return true;
3201
+ if (existsSync9(join12(cwd, "agents", "skills", slug, "SKILL.md"))) return true;
3066
3202
  for (const platform of ["claude-code", "gemini-cli", "codex", "cursor", "shared"]) {
3067
- if (existsSync9(join11(cwd, "agents", "skills", platform, slug, "SKILL.md"))) return true;
3203
+ if (existsSync9(join12(cwd, "agents", "skills", platform, slug, "SKILL.md"))) return true;
3068
3204
  }
3069
3205
  return false;
3070
3206
  }
3071
3207
  function runAgnixTomlRules(cwd) {
3072
3208
  const rel = ".agnix.toml";
3073
- const abs = join12(cwd, rel);
3209
+ const abs = join13(cwd, rel);
3074
3210
  if (!existsSync10(abs)) return [];
3075
3211
  const content = readTextSafe(abs);
3076
3212
  if (content === null) return [];
@@ -3240,7 +3376,7 @@ function getRoadmapMode(config) {
3240
3376
  return mode === "file-less" ? "file-less" : "file-backed";
3241
3377
  }
3242
3378
  function validateRoadmapMode(config, projectRoot) {
3243
- const root = path3.resolve(projectRoot);
3379
+ const root = path4.resolve(projectRoot);
3244
3380
  const mode = getRoadmapMode(config);
3245
3381
  if (mode === "file-backed") return Ok(void 0);
3246
3382
  const tracker = config.roadmap?.tracker;
@@ -3257,8 +3393,8 @@ function validateRoadmapMode(config, projectRoot) {
3257
3393
  )
3258
3394
  );
3259
3395
  }
3260
- const roadmapPath = path3.join(root, "docs", "roadmap.md");
3261
- if (fs3.existsSync(roadmapPath)) {
3396
+ const roadmapPath = path4.join(root, "docs", "roadmap.md");
3397
+ if (fs4.existsSync(roadmapPath)) {
3262
3398
  return Err(
3263
3399
  createError(
3264
3400
  "ROADMAP_MODE_FILE_PRESENT",
@@ -3441,8 +3577,8 @@ async function checkDocCoverage(domain, options = {}) {
3441
3577
  );
3442
3578
  }
3443
3579
  }
3444
- function suggestFix(path50, existingFiles) {
3445
- const targetName = basename3(path50).toLowerCase();
3580
+ function suggestFix(path52, existingFiles) {
3581
+ const targetName = basename3(path52).toLowerCase();
3446
3582
  const similar = existingFiles.find((file) => {
3447
3583
  const fileName = basename3(file).toLowerCase();
3448
3584
  return fileName.includes(targetName) || targetName.includes(fileName);
@@ -3450,10 +3586,10 @@ function suggestFix(path50, existingFiles) {
3450
3586
  if (similar) {
3451
3587
  return `Did you mean "${similar}"?`;
3452
3588
  }
3453
- return `Create the file "${path50}" or remove the link`;
3589
+ return `Create the file "${path52}" or remove the link`;
3454
3590
  }
3455
3591
  async function validateKnowledgeMap(rootDir = process.cwd()) {
3456
- const agentsPath = join14(rootDir, "AGENTS.md");
3592
+ const agentsPath = join15(rootDir, "AGENTS.md");
3457
3593
  const agentsResult = await validateAgentsMap(agentsPath);
3458
3594
  if (!agentsResult.ok) {
3459
3595
  return Err(agentsResult.error);
@@ -3808,8 +3944,8 @@ function createBoundaryValidator(schema, name) {
3808
3944
  return Ok(result.data);
3809
3945
  }
3810
3946
  const suggestions = result.error.issues.map((issue) => {
3811
- const path50 = issue.path.join(".");
3812
- return path50 ? `${path50}: ${issue.message}` : issue.message;
3947
+ const path52 = issue.path.join(".");
3948
+ return path52 ? `${path52}: ${issue.message}` : issue.message;
3813
3949
  });
3814
3950
  return Err(
3815
3951
  createError(
@@ -3855,7 +3991,7 @@ function validateBoundaries(boundaries, data) {
3855
3991
  async function writeConfig(filePath, content) {
3856
3992
  try {
3857
3993
  const json = JSON.stringify(content, null, 2) + "\n";
3858
- await fs4.writeFile(filePath, json, "utf-8");
3994
+ await fs5.writeFile(filePath, json, "utf-8");
3859
3995
  return Ok(void 0);
3860
3996
  } catch (error) {
3861
3997
  return Err(error instanceof Error ? error : new Error(String(error)));
@@ -4129,7 +4265,7 @@ function deepMergeConstraints(localConfig, bundleConstraints, _existingContribut
4129
4265
  async function readLockfile(lockfilePath) {
4130
4266
  let raw;
4131
4267
  try {
4132
- raw = await fs5.readFile(lockfilePath, "utf-8");
4268
+ raw = await fs6.readFile(lockfilePath, "utf-8");
4133
4269
  } catch (err) {
4134
4270
  if (isNodeError(err) && err.code === "ENOENT") {
4135
4271
  return { ok: true, value: null };
@@ -4507,7 +4643,7 @@ function previewFix(fix) {
4507
4643
  }
4508
4644
  }
4509
4645
  async function createBackup(filePath, backupDir) {
4510
- const backupPath = join15(backupDir, `${Date.now()}-${basename5(filePath)}`);
4646
+ const backupPath = join16(backupDir, `${Date.now()}-${basename5(filePath)}`);
4511
4647
  try {
4512
4648
  await mkdir22(dirname5(backupPath), { recursive: true });
4513
4649
  await copyFile2(filePath, backupPath);
@@ -4533,7 +4669,7 @@ async function applyDeleteFile(fix, config) {
4533
4669
  }
4534
4670
  async function applyDeleteLines(fix) {
4535
4671
  if (fix.line !== void 0) {
4536
- const content = await readFile62(fix.file, "utf-8");
4672
+ const content = await readFile72(fix.file, "utf-8");
4537
4673
  const lines = content.split("\n");
4538
4674
  lines.splice(fix.line - 1, 1);
4539
4675
  await writeFile32(fix.file, lines.join("\n"));
@@ -4541,13 +4677,13 @@ async function applyDeleteLines(fix) {
4541
4677
  }
4542
4678
  async function applyReplace(fix) {
4543
4679
  if (fix.oldContent && fix.newContent !== void 0) {
4544
- const content = await readFile62(fix.file, "utf-8");
4680
+ const content = await readFile72(fix.file, "utf-8");
4545
4681
  await writeFile32(fix.file, content.replace(fix.oldContent, fix.newContent));
4546
4682
  }
4547
4683
  }
4548
4684
  async function applyInsert(fix) {
4549
4685
  if (fix.line !== void 0 && fix.newContent) {
4550
- const content = await readFile62(fix.file, "utf-8");
4686
+ const content = await readFile72(fix.file, "utf-8");
4551
4687
  const lines = content.split("\n");
4552
4688
  lines.splice(fix.line - 1, 0, fix.newContent);
4553
4689
  await writeFile32(fix.file, lines.join("\n"));
@@ -4814,7 +4950,7 @@ function arrayLen(arr) {
4814
4950
  return Array.isArray(arr) ? arr.length : 0;
4815
4951
  }
4816
4952
  async function gatherEntropyBlock(projectPath) {
4817
- const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./analyzer-H3AHBFSL-KPOPJYRB.js");
4953
+ const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./analyzer-V633GUHY-MBCQWKDU.js");
4818
4954
  const analyzer = new EntropyAnalyzer2({
4819
4955
  rootDir: projectPath,
4820
4956
  include: ["src/**/*.ts", "src/**/*.tsx", "packages/*/src/**/*.ts"],
@@ -4851,18 +4987,18 @@ function collectTopAffectedLabels(affected) {
4851
4987
  return out;
4852
4988
  }
4853
4989
  async function gatherAttentionBlock(projectPath) {
4854
- const sessionsDir = path4.join(projectPath, ".harness", "sessions");
4855
- if (!fs7.existsSync(sessionsDir)) return { activeThreadCount: 0, staleThreadCount: 0 };
4990
+ const sessionsDir = path5.join(projectPath, ".harness", "sessions");
4991
+ if (!fs8.existsSync(sessionsDir)) return { activeThreadCount: 0, staleThreadCount: 0 };
4856
4992
  const now = Date.now();
4857
4993
  const STALE_MS = 7 * 24 * 60 * 60 * 1e3;
4858
4994
  let active = 0;
4859
4995
  let stale = 0;
4860
- const entries = fs7.readdirSync(sessionsDir, { withFileTypes: true });
4996
+ const entries = fs8.readdirSync(sessionsDir, { withFileTypes: true });
4861
4997
  for (const entry of entries) {
4862
4998
  if (!entry.isDirectory()) continue;
4863
- const dirPath = path4.join(sessionsDir, entry.name);
4999
+ const dirPath = path5.join(sessionsDir, entry.name);
4864
5000
  try {
4865
- const stat2 = fs7.statSync(dirPath);
5001
+ const stat2 = fs8.statSync(dirPath);
4866
5002
  if (now - stat2.mtimeMs > STALE_MS) {
4867
5003
  stale++;
4868
5004
  } else {
@@ -4874,10 +5010,10 @@ async function gatherAttentionBlock(projectPath) {
4874
5010
  return { activeThreadCount: active, staleThreadCount: stale };
4875
5011
  }
4876
5012
  async function gatherImpactBlock(projectPath) {
4877
- const cachePath = path4.join(projectPath, ".harness", "cache", "impact.json");
4878
- if (!fs7.existsSync(cachePath)) return { recentBlastRadius: [] };
5013
+ const cachePath = path5.join(projectPath, ".harness", "cache", "impact.json");
5014
+ if (!fs8.existsSync(cachePath)) return { recentBlastRadius: [] };
4879
5015
  try {
4880
- const raw = JSON.parse(fs7.readFileSync(cachePath, "utf8"));
5016
+ const raw = JSON.parse(fs8.readFileSync(cachePath, "utf8"));
4881
5017
  const radii = raw.blastRadius ?? [];
4882
5018
  return {
4883
5019
  recentBlastRadius: radii.slice(0, 5).map((r) => ({
@@ -4927,9 +5063,9 @@ async function composeInsights(projectPath, opts = {}) {
4927
5063
  }
4928
5064
  function pickProjectName(projectPath) {
4929
5065
  try {
4930
- const pkgPath = path4.join(projectPath, "package.json");
4931
- if (!fs7.existsSync(pkgPath)) return void 0;
4932
- const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf8"));
5066
+ const pkgPath = path5.join(projectPath, "package.json");
5067
+ if (!fs8.existsSync(pkgPath)) return void 0;
5068
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf8"));
4933
5069
  return pkg.name;
4934
5070
  } catch {
4935
5071
  return void 0;
@@ -5015,12 +5151,12 @@ function parseDiffHeader(part) {
5015
5151
  return headerMatch[2];
5016
5152
  }
5017
5153
  function parseDiffPart(part) {
5018
- const path50 = parseDiffHeader(part);
5019
- if (!path50) return null;
5154
+ const path52 = parseDiffHeader(part);
5155
+ if (!path52) return null;
5020
5156
  const additionRegex = /^\+(?!\+\+)/gm;
5021
5157
  const deletionRegex = /^-(?!--)/gm;
5022
5158
  return {
5023
- path: path50,
5159
+ path: path52,
5024
5160
  status: detectFileStatus(part),
5025
5161
  additions: (part.match(additionRegex) || []).length,
5026
5162
  deletions: (part.match(deletionRegex) || []).length
@@ -5805,10 +5941,10 @@ function detectEmergentConstraints(history, options) {
5805
5941
  return { suggestions, totalViolationsAnalyzed, windowWeeks, minOccurrences };
5806
5942
  }
5807
5943
  function streamsDir(projectPath) {
5808
- return path9.join(projectPath, HARNESS_DIR, STREAMS_DIR);
5944
+ return path10.join(projectPath, HARNESS_DIR, STREAMS_DIR);
5809
5945
  }
5810
5946
  function indexPath(projectPath) {
5811
- return path9.join(streamsDir(projectPath), INDEX_FILE);
5947
+ return path10.join(streamsDir(projectPath), INDEX_FILE);
5812
5948
  }
5813
5949
  function validateStreamName(name) {
5814
5950
  if (!STREAM_NAME_REGEX.test(name)) {
@@ -5822,11 +5958,11 @@ function validateStreamName(name) {
5822
5958
  }
5823
5959
  async function loadStreamIndex(projectPath) {
5824
5960
  const idxPath = indexPath(projectPath);
5825
- if (!fs12.existsSync(idxPath)) {
5961
+ if (!fs13.existsSync(idxPath)) {
5826
5962
  return Ok({ ...DEFAULT_STREAM_INDEX, streams: {} });
5827
5963
  }
5828
5964
  try {
5829
- const raw = fs12.readFileSync(idxPath, "utf-8");
5965
+ const raw = fs13.readFileSync(idxPath, "utf-8");
5830
5966
  const parsed = JSON.parse(raw);
5831
5967
  const result = StreamIndexSchema.safeParse(parsed);
5832
5968
  if (!result.success) {
@@ -5844,8 +5980,8 @@ async function loadStreamIndex(projectPath) {
5844
5980
  async function saveStreamIndex(projectPath, index) {
5845
5981
  const dir = streamsDir(projectPath);
5846
5982
  try {
5847
- fs12.mkdirSync(dir, { recursive: true });
5848
- fs12.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
5983
+ fs13.mkdirSync(dir, { recursive: true });
5984
+ fs13.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
5849
5985
  return Ok(void 0);
5850
5986
  } catch (error) {
5851
5987
  return Err(
@@ -5884,18 +6020,18 @@ async function resolveStreamPath(projectPath, options) {
5884
6020
  )
5885
6021
  );
5886
6022
  }
5887
- return Ok(path9.join(streamsDir(projectPath), options.stream));
6023
+ return Ok(path10.join(streamsDir(projectPath), options.stream));
5888
6024
  }
5889
6025
  const branch = getCurrentBranch(projectPath);
5890
6026
  if (branch && branch !== "main" && branch !== "master") {
5891
6027
  for (const [name, info] of Object.entries(index.streams)) {
5892
6028
  if (info.branch === branch) {
5893
- return Ok(path9.join(streamsDir(projectPath), name));
6029
+ return Ok(path10.join(streamsDir(projectPath), name));
5894
6030
  }
5895
6031
  }
5896
6032
  }
5897
6033
  if (index.activeStream && index.streams[index.activeStream]) {
5898
- return Ok(path9.join(streamsDir(projectPath), index.activeStream));
6034
+ return Ok(path10.join(streamsDir(projectPath), index.activeStream));
5899
6035
  }
5900
6036
  return Err(
5901
6037
  new Error(
@@ -5923,9 +6059,9 @@ async function createStream(projectPath, name, branch) {
5923
6059
  if (index.streams[name]) {
5924
6060
  return Err(new Error(`Stream '${name}' already exists`));
5925
6061
  }
5926
- const streamPath = path9.join(streamsDir(projectPath), name);
6062
+ const streamPath = path10.join(streamsDir(projectPath), name);
5927
6063
  try {
5928
- fs12.mkdirSync(streamPath, { recursive: true });
6064
+ fs13.mkdirSync(streamPath, { recursive: true });
5929
6065
  } catch (error) {
5930
6066
  return Err(
5931
6067
  new Error(
@@ -5966,12 +6102,12 @@ async function archiveStream(projectPath, name) {
5966
6102
  if (!index.streams[name]) {
5967
6103
  return Err(new Error(`Stream '${name}' not found`));
5968
6104
  }
5969
- const streamPath = path9.join(streamsDir(projectPath), name);
5970
- const archiveDir = path9.join(projectPath, HARNESS_DIR, "archive", "streams");
6105
+ const streamPath = path10.join(streamsDir(projectPath), name);
6106
+ const archiveDir = path10.join(projectPath, HARNESS_DIR, "archive", "streams");
5971
6107
  try {
5972
- fs12.mkdirSync(archiveDir, { recursive: true });
6108
+ fs13.mkdirSync(archiveDir, { recursive: true });
5973
6109
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
5974
- fs12.renameSync(streamPath, path9.join(archiveDir, `${name}-${date}`));
6110
+ fs13.renameSync(streamPath, path10.join(archiveDir, `${name}-${date}`));
5975
6111
  } catch (error) {
5976
6112
  return Err(
5977
6113
  new Error(
@@ -5992,19 +6128,19 @@ function getStreamForBranch(index, branch) {
5992
6128
  return null;
5993
6129
  }
5994
6130
  async function migrateToStreams(projectPath) {
5995
- const harnessDir = path9.join(projectPath, HARNESS_DIR);
5996
- if (fs12.existsSync(indexPath(projectPath))) {
6131
+ const harnessDir = path10.join(projectPath, HARNESS_DIR);
6132
+ if (fs13.existsSync(indexPath(projectPath))) {
5997
6133
  return Ok(void 0);
5998
6134
  }
5999
- const filesToMove = STATE_FILES.filter((f) => fs12.existsSync(path9.join(harnessDir, f)));
6135
+ const filesToMove = STATE_FILES.filter((f) => fs13.existsSync(path10.join(harnessDir, f)));
6000
6136
  if (filesToMove.length === 0) {
6001
6137
  return Ok(void 0);
6002
6138
  }
6003
- const defaultDir = path9.join(streamsDir(projectPath), "default");
6139
+ const defaultDir = path10.join(streamsDir(projectPath), "default");
6004
6140
  try {
6005
- fs12.mkdirSync(defaultDir, { recursive: true });
6141
+ fs13.mkdirSync(defaultDir, { recursive: true });
6006
6142
  for (const file of filesToMove) {
6007
- fs12.renameSync(path9.join(harnessDir, file), path9.join(defaultDir, file));
6143
+ fs13.renameSync(path10.join(harnessDir, file), path10.join(defaultDir, file));
6008
6144
  }
6009
6145
  } catch (error) {
6010
6146
  return Err(
@@ -6034,26 +6170,26 @@ function resolveSessionDir(projectPath, sessionSlug, options) {
6034
6170
  new Error(`Invalid session slug '${sessionSlug}': must not contain path traversal characters`)
6035
6171
  );
6036
6172
  }
6037
- const sessionDir = path10.join(projectPath, HARNESS_DIR, SESSIONS_DIR, sessionSlug);
6173
+ const sessionDir = path11.join(projectPath, HARNESS_DIR, SESSIONS_DIR, sessionSlug);
6038
6174
  if (options?.create) {
6039
- fs13.mkdirSync(sessionDir, { recursive: true });
6175
+ fs14.mkdirSync(sessionDir, { recursive: true });
6040
6176
  }
6041
6177
  return Ok(sessionDir);
6042
6178
  }
6043
6179
  function updateSessionIndex(projectPath, sessionSlug, description) {
6044
- const sessionsDir = path10.join(projectPath, HARNESS_DIR, SESSIONS_DIR);
6045
- fs13.mkdirSync(sessionsDir, { recursive: true });
6046
- const indexPath2 = path10.join(sessionsDir, SESSION_INDEX_FILE);
6180
+ const sessionsDir = path11.join(projectPath, HARNESS_DIR, SESSIONS_DIR);
6181
+ fs14.mkdirSync(sessionsDir, { recursive: true });
6182
+ const indexPath2 = path11.join(sessionsDir, SESSION_INDEX_FILE);
6047
6183
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6048
6184
  const newLine = `- [${sessionSlug}](${sessionSlug}/summary.md) \u2014 ${description} (${date})`;
6049
- if (!fs13.existsSync(indexPath2)) {
6050
- fs13.writeFileSync(indexPath2, `## Active Sessions
6185
+ if (!fs14.existsSync(indexPath2)) {
6186
+ fs14.writeFileSync(indexPath2, `## Active Sessions
6051
6187
 
6052
6188
  ${newLine}
6053
6189
  `);
6054
6190
  return;
6055
6191
  }
6056
- const content = fs13.readFileSync(indexPath2, "utf-8");
6192
+ const content = fs14.readFileSync(indexPath2, "utf-8");
6057
6193
  const lines = content.split("\n");
6058
6194
  const slugPattern = `- [${sessionSlug}]`;
6059
6195
  const existingIdx = lines.findIndex((l) => l.startsWith(slugPattern));
@@ -6063,7 +6199,7 @@ ${newLine}
6063
6199
  const lastNonEmpty = lines.reduce((last, line, i) => line.trim() !== "" ? i : last, 0);
6064
6200
  lines.splice(lastNonEmpty + 1, 0, newLine);
6065
6201
  }
6066
- fs13.writeFileSync(indexPath2, lines.join("\n"));
6202
+ fs14.writeFileSync(indexPath2, lines.join("\n"));
6067
6203
  }
6068
6204
  function evictIfNeeded(map) {
6069
6205
  if (map.size > MAX_CACHE_ENTRIES) {
@@ -6076,8 +6212,8 @@ async function getStateDir(projectPath, stream, session) {
6076
6212
  const sessionResult = resolveSessionDir(projectPath, session, { create: true });
6077
6213
  return sessionResult;
6078
6214
  }
6079
- const streamsIndexPath = path11.join(projectPath, HARNESS_DIR, "streams", INDEX_FILE);
6080
- const hasStreams = fs14.existsSync(streamsIndexPath);
6215
+ const streamsIndexPath = path12.join(projectPath, HARNESS_DIR, "streams", INDEX_FILE);
6216
+ const hasStreams = fs15.existsSync(streamsIndexPath);
6081
6217
  if (stream || hasStreams) {
6082
6218
  const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
6083
6219
  if (result.ok) {
@@ -6087,18 +6223,18 @@ async function getStateDir(projectPath, stream, session) {
6087
6223
  return result;
6088
6224
  }
6089
6225
  }
6090
- return Ok(path11.join(projectPath, HARNESS_DIR));
6226
+ return Ok(path12.join(projectPath, HARNESS_DIR));
6091
6227
  }
6092
6228
  async function loadState(projectPath, stream, session) {
6093
6229
  try {
6094
6230
  const dirResult = await getStateDir(projectPath, stream, session);
6095
6231
  if (!dirResult.ok) return dirResult;
6096
6232
  const stateDir = dirResult.value;
6097
- const statePath = path12.join(stateDir, STATE_FILE);
6098
- if (!fs15.existsSync(statePath)) {
6233
+ const statePath = path13.join(stateDir, STATE_FILE);
6234
+ if (!fs16.existsSync(statePath)) {
6099
6235
  return Ok({ ...DEFAULT_STATE });
6100
6236
  }
6101
- const raw = fs15.readFileSync(statePath, "utf-8");
6237
+ const raw = fs16.readFileSync(statePath, "utf-8");
6102
6238
  const parsed = JSON.parse(raw);
6103
6239
  const result = HarnessStateSchema.safeParse(parsed);
6104
6240
  if (!result.success) {
@@ -6116,11 +6252,11 @@ async function saveState(projectPath, state, stream, session) {
6116
6252
  const dirResult = await getStateDir(projectPath, stream, session);
6117
6253
  if (!dirResult.ok) return dirResult;
6118
6254
  const stateDir = dirResult.value;
6119
- const statePath = path12.join(stateDir, STATE_FILE);
6120
- fs15.mkdirSync(stateDir, { recursive: true });
6255
+ const statePath = path13.join(stateDir, STATE_FILE);
6256
+ fs16.mkdirSync(stateDir, { recursive: true });
6121
6257
  const tmpPath = statePath + ".tmp";
6122
- fs15.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
6123
- fs15.renameSync(tmpPath, statePath);
6258
+ fs16.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
6259
+ fs16.renameSync(tmpPath, statePath);
6124
6260
  return Ok(void 0);
6125
6261
  } catch (error) {
6126
6262
  return Err(
@@ -6206,10 +6342,10 @@ function computeContentHash(text) {
6206
6342
  return crypto2.createHash("sha256").update(text).digest("hex").slice(0, 16);
6207
6343
  }
6208
6344
  function loadContentHashes(stateDir) {
6209
- const hashesPath = path13.join(stateDir, CONTENT_HASHES_FILE);
6210
- if (!fs16.existsSync(hashesPath)) return {};
6345
+ const hashesPath = path14.join(stateDir, CONTENT_HASHES_FILE);
6346
+ if (!fs17.existsSync(hashesPath)) return {};
6211
6347
  try {
6212
- const raw = fs16.readFileSync(hashesPath, "utf-8");
6348
+ const raw = fs17.readFileSync(hashesPath, "utf-8");
6213
6349
  const parsed = JSON.parse(raw);
6214
6350
  if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
6215
6351
  return parsed;
@@ -6218,15 +6354,15 @@ function loadContentHashes(stateDir) {
6218
6354
  }
6219
6355
  }
6220
6356
  function saveContentHashes(stateDir, index) {
6221
- const hashesPath = path13.join(stateDir, CONTENT_HASHES_FILE);
6357
+ const hashesPath = path14.join(stateDir, CONTENT_HASHES_FILE);
6222
6358
  const tmpPath = hashesPath + ".tmp";
6223
- fs16.writeFileSync(tmpPath, JSON.stringify(index, null, 2) + "\n");
6224
- fs16.renameSync(tmpPath, hashesPath);
6359
+ fs17.writeFileSync(tmpPath, JSON.stringify(index, null, 2) + "\n");
6360
+ fs17.renameSync(tmpPath, hashesPath);
6225
6361
  }
6226
6362
  function rebuildContentHashes(stateDir, learningsFile) {
6227
- const learningsPath = path13.join(stateDir, learningsFile);
6228
- if (!fs16.existsSync(learningsPath)) return {};
6229
- const content = fs16.readFileSync(learningsPath, "utf-8");
6363
+ const learningsPath = path14.join(stateDir, learningsFile);
6364
+ if (!fs17.existsSync(learningsPath)) return {};
6365
+ const content = fs17.readFileSync(learningsPath, "utf-8");
6230
6366
  const lines = content.split("\n");
6231
6367
  const index = {};
6232
6368
  for (let i = 0; i < lines.length; i++) {
@@ -6285,18 +6421,18 @@ async function loadRelevantLearnings(projectPath, skillName, stream, session) {
6285
6421
  const dirResult = await getStateDir(projectPath, stream, session);
6286
6422
  if (!dirResult.ok) return dirResult;
6287
6423
  const stateDir = dirResult.value;
6288
- const learningsPath = path14.join(stateDir, LEARNINGS_FILE);
6289
- if (!fs17.existsSync(learningsPath)) {
6424
+ const learningsPath = path15.join(stateDir, LEARNINGS_FILE);
6425
+ if (!fs18.existsSync(learningsPath)) {
6290
6426
  return Ok([]);
6291
6427
  }
6292
- const stats = fs17.statSync(learningsPath);
6428
+ const stats = fs18.statSync(learningsPath);
6293
6429
  const cacheKey = learningsPath;
6294
6430
  const cached = learningsCacheMap.get(cacheKey);
6295
6431
  let entries;
6296
6432
  if (cached && cached.mtimeMs === stats.mtimeMs) {
6297
6433
  entries = cached.entries;
6298
6434
  } else {
6299
- const content = fs17.readFileSync(learningsPath, "utf-8");
6435
+ const content = fs18.readFileSync(learningsPath, "utf-8");
6300
6436
  const lines = content.split("\n");
6301
6437
  entries = [];
6302
6438
  let currentBlock = [];
@@ -6437,11 +6573,11 @@ function sleep(ms) {
6437
6573
  async function acquireFileLock(lockPath) {
6438
6574
  for (let attempt = 0; attempt < LOCK_RETRIES; attempt++) {
6439
6575
  try {
6440
- const fd = fs18.openSync(
6576
+ const fd = fs19.openSync(
6441
6577
  lockPath,
6442
- fs18.constants.O_CREAT | fs18.constants.O_EXCL | fs18.constants.O_WRONLY
6578
+ fs19.constants.O_CREAT | fs19.constants.O_EXCL | fs19.constants.O_WRONLY
6443
6579
  );
6444
- fs18.closeSync(fd);
6580
+ fs19.closeSync(fd);
6445
6581
  return;
6446
6582
  } catch (err) {
6447
6583
  const isExist = err instanceof Error && "code" in err && err.code === "EEXIST";
@@ -6455,20 +6591,20 @@ async function acquireFileLock(lockPath) {
6455
6591
  }
6456
6592
  function releaseFileLock(lockPath) {
6457
6593
  try {
6458
- fs18.unlinkSync(lockPath);
6594
+ fs19.unlinkSync(lockPath);
6459
6595
  } catch {
6460
6596
  }
6461
6597
  }
6462
6598
  function loadOrRebuildHashes(stateDir, learningsPath) {
6463
- const hashesPath = path15.join(stateDir, CONTENT_HASHES_FILE);
6464
- if (fs18.existsSync(hashesPath)) {
6599
+ const hashesPath = path16.join(stateDir, CONTENT_HASHES_FILE);
6600
+ if (fs19.existsSync(hashesPath)) {
6465
6601
  const hashes = loadContentHashes(stateDir);
6466
- if (Object.keys(hashes).length === 0 && fs18.existsSync(learningsPath)) {
6602
+ if (Object.keys(hashes).length === 0 && fs19.existsSync(learningsPath)) {
6467
6603
  return rebuildContentHashes(stateDir, LEARNINGS_FILE);
6468
6604
  }
6469
6605
  return hashes;
6470
6606
  }
6471
- if (fs18.existsSync(learningsPath)) {
6607
+ if (fs19.existsSync(learningsPath)) {
6472
6608
  return rebuildContentHashes(stateDir, LEARNINGS_FILE);
6473
6609
  }
6474
6610
  return {};
@@ -6492,8 +6628,8 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream,
6492
6628
  const dirResult = await getStateDir(projectPath, stream, session);
6493
6629
  if (!dirResult.ok) return dirResult;
6494
6630
  const stateDir = dirResult.value;
6495
- const learningsPath = path15.join(stateDir, LEARNINGS_FILE);
6496
- fs18.mkdirSync(stateDir, { recursive: true });
6631
+ const learningsPath = path16.join(stateDir, LEARNINGS_FILE);
6632
+ fs19.mkdirSync(stateDir, { recursive: true });
6497
6633
  const lockPath = learningsPath + ".lock";
6498
6634
  await acquireFileLock(lockPath);
6499
6635
  try {
@@ -6523,17 +6659,17 @@ ${bulletLine}
6523
6659
  `;
6524
6660
  let overlapResult;
6525
6661
  let existingLineCount;
6526
- if (!fs18.existsSync(learningsPath)) {
6527
- fs18.writeFileSync(learningsPath, `# Learnings
6662
+ if (!fs19.existsSync(learningsPath)) {
6663
+ fs19.writeFileSync(learningsPath, `# Learnings
6528
6664
  ${entry}`);
6529
6665
  existingLineCount = 1;
6530
6666
  } else {
6531
- const existingContent = fs18.readFileSync(learningsPath, "utf-8");
6667
+ const existingContent = fs19.readFileSync(learningsPath, "utf-8");
6532
6668
  const existingEntries = existingContent.split("\n").filter((line) => /^- \*\*\d{4}-\d{2}-\d{2}/.test(line));
6533
6669
  const overlap = checkOverlap(bulletLine, existingEntries);
6534
6670
  if (overlap.score >= 0.7) overlapResult = overlap;
6535
6671
  existingLineCount = existingContent.split("\n").length;
6536
- fs18.appendFileSync(learningsPath, entry);
6672
+ fs19.appendFileSync(learningsPath, entry);
6537
6673
  }
6538
6674
  const bulletLineNum = existingLineCount + 2;
6539
6675
  contentHashes[contentHash] = { date: timestamp ?? "", line: bulletLineNum };
@@ -6638,11 +6774,11 @@ async function loadIndexEntries(projectPath, skillName, stream, session) {
6638
6774
  const dirResult = await getStateDir(projectPath, stream, session);
6639
6775
  if (!dirResult.ok) return dirResult;
6640
6776
  const stateDir = dirResult.value;
6641
- const learningsPath = path15.join(stateDir, LEARNINGS_FILE);
6642
- if (!fs18.existsSync(learningsPath)) {
6777
+ const learningsPath = path16.join(stateDir, LEARNINGS_FILE);
6778
+ if (!fs19.existsSync(learningsPath)) {
6643
6779
  return Ok([]);
6644
6780
  }
6645
- const content = fs18.readFileSync(learningsPath, "utf-8");
6781
+ const content = fs19.readFileSync(learningsPath, "utf-8");
6646
6782
  const lines = content.split("\n");
6647
6783
  const indexEntries = [];
6648
6784
  let pendingFrontmatter = null;
@@ -6684,16 +6820,16 @@ async function archiveLearnings(projectPath, entries, stream) {
6684
6820
  const dirResult = await getStateDir(projectPath, stream);
6685
6821
  if (!dirResult.ok) return dirResult;
6686
6822
  const stateDir = dirResult.value;
6687
- const archiveDir = path16.join(stateDir, "learnings-archive");
6688
- fs19.mkdirSync(archiveDir, { recursive: true });
6823
+ const archiveDir = path17.join(stateDir, "learnings-archive");
6824
+ fs20.mkdirSync(archiveDir, { recursive: true });
6689
6825
  const now = /* @__PURE__ */ new Date();
6690
6826
  const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
6691
- const archivePath = path16.join(archiveDir, `${yearMonth}.md`);
6827
+ const archivePath = path17.join(archiveDir, `${yearMonth}.md`);
6692
6828
  const archiveContent = entries.join("\n\n") + "\n";
6693
- if (fs19.existsSync(archivePath)) {
6694
- fs19.appendFileSync(archivePath, "\n" + archiveContent);
6829
+ if (fs20.existsSync(archivePath)) {
6830
+ fs20.appendFileSync(archivePath, "\n" + archiveContent);
6695
6831
  } else {
6696
- fs19.writeFileSync(archivePath, `# Learnings Archive
6832
+ fs20.writeFileSync(archivePath, `# Learnings Archive
6697
6833
 
6698
6834
  ${archiveContent}`);
6699
6835
  }
@@ -6711,8 +6847,8 @@ async function pruneLearnings(projectPath, stream) {
6711
6847
  const dirResult = await getStateDir(projectPath, stream);
6712
6848
  if (!dirResult.ok) return dirResult;
6713
6849
  const stateDir = dirResult.value;
6714
- const learningsPath = path16.join(stateDir, LEARNINGS_FILE);
6715
- if (!fs19.existsSync(learningsPath)) {
6850
+ const learningsPath = path17.join(stateDir, LEARNINGS_FILE);
6851
+ if (!fs20.existsSync(learningsPath)) {
6716
6852
  return Ok({ kept: 0, archived: 0, patterns: [] });
6717
6853
  }
6718
6854
  const loadResult = await loadRelevantLearnings(projectPath, void 0, stream);
@@ -6743,7 +6879,7 @@ async function pruneLearnings(projectPath, stream) {
6743
6879
  if (!archiveResult.ok) return archiveResult;
6744
6880
  }
6745
6881
  const newContent = "# Learnings\n\n" + toKeep.join("\n\n") + "\n";
6746
- fs19.writeFileSync(learningsPath, newContent);
6882
+ fs20.writeFileSync(learningsPath, newContent);
6747
6883
  invalidateLearningsCacheEntry(learningsPath);
6748
6884
  return Ok({
6749
6885
  kept: toKeep.length,
@@ -6787,19 +6923,19 @@ async function promoteSessionLearnings(projectPath, sessionSlug, stream) {
6787
6923
  const dirResult = await getStateDir(projectPath, stream);
6788
6924
  if (!dirResult.ok) return dirResult;
6789
6925
  const stateDir = dirResult.value;
6790
- const globalPath = path16.join(stateDir, LEARNINGS_FILE);
6791
- const existingGlobal = fs19.existsSync(globalPath) ? fs19.readFileSync(globalPath, "utf-8") : "";
6926
+ const globalPath = path17.join(stateDir, LEARNINGS_FILE);
6927
+ const existingGlobal = fs20.existsSync(globalPath) ? fs20.readFileSync(globalPath, "utf-8") : "";
6792
6928
  const newEntries = toPromote.filter((entry) => !existingGlobal.includes(entry.trim()));
6793
6929
  if (newEntries.length === 0) {
6794
6930
  return Ok({ promoted: 0, skipped: skipped + toPromote.length });
6795
6931
  }
6796
6932
  const promotedContent = newEntries.join("\n\n") + "\n";
6797
6933
  if (!existingGlobal) {
6798
- fs19.writeFileSync(globalPath, `# Learnings
6934
+ fs20.writeFileSync(globalPath, `# Learnings
6799
6935
 
6800
6936
  ${promotedContent}`);
6801
6937
  } else {
6802
- fs19.appendFileSync(globalPath, "\n\n" + promotedContent);
6938
+ fs20.appendFileSync(globalPath, "\n\n" + promotedContent);
6803
6939
  }
6804
6940
  invalidateLearningsCacheEntry(globalPath);
6805
6941
  return Ok({
@@ -6835,13 +6971,13 @@ async function detectStaleLearnings(projectPath, stream, session) {
6835
6971
  freshCount++;
6836
6972
  continue;
6837
6973
  }
6838
- const resolvedRoot = path17.resolve(projectPath);
6974
+ const resolvedRoot = path18.resolve(projectPath);
6839
6975
  const missing = refs.filter((ref) => {
6840
- const absPath = path17.resolve(projectPath, ref);
6841
- if (!absPath.startsWith(resolvedRoot + path17.sep) && absPath !== resolvedRoot) {
6976
+ const absPath = path18.resolve(projectPath, ref);
6977
+ if (!absPath.startsWith(resolvedRoot + path18.sep) && absPath !== resolvedRoot) {
6842
6978
  return false;
6843
6979
  }
6844
- return !fs20.existsSync(absPath);
6980
+ return !fs21.existsSync(absPath);
6845
6981
  });
6846
6982
  if (missing.length > 0) {
6847
6983
  const idx = extractIndexEntry(entry);
@@ -6876,17 +7012,17 @@ async function appendFailure(projectPath, description, skillName, type, stream,
6876
7012
  const dirResult = await getStateDir(projectPath, stream, session);
6877
7013
  if (!dirResult.ok) return dirResult;
6878
7014
  const stateDir = dirResult.value;
6879
- const failuresPath = path18.join(stateDir, FAILURES_FILE);
6880
- fs21.mkdirSync(stateDir, { recursive: true });
7015
+ const failuresPath = path19.join(stateDir, FAILURES_FILE);
7016
+ fs22.mkdirSync(stateDir, { recursive: true });
6881
7017
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6882
7018
  const entry = `
6883
7019
  - **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
6884
7020
  `;
6885
- if (!fs21.existsSync(failuresPath)) {
6886
- fs21.writeFileSync(failuresPath, `# Failures
7021
+ if (!fs22.existsSync(failuresPath)) {
7022
+ fs22.writeFileSync(failuresPath, `# Failures
6887
7023
  ${entry}`);
6888
7024
  } else {
6889
- fs21.appendFileSync(failuresPath, entry);
7025
+ fs22.appendFileSync(failuresPath, entry);
6890
7026
  }
6891
7027
  failuresCacheMap.delete(failuresPath);
6892
7028
  return Ok(void 0);
@@ -6903,17 +7039,17 @@ async function loadFailures(projectPath, stream, session) {
6903
7039
  const dirResult = await getStateDir(projectPath, stream, session);
6904
7040
  if (!dirResult.ok) return dirResult;
6905
7041
  const stateDir = dirResult.value;
6906
- const failuresPath = path18.join(stateDir, FAILURES_FILE);
6907
- if (!fs21.existsSync(failuresPath)) {
7042
+ const failuresPath = path19.join(stateDir, FAILURES_FILE);
7043
+ if (!fs22.existsSync(failuresPath)) {
6908
7044
  return Ok([]);
6909
7045
  }
6910
- const stats = fs21.statSync(failuresPath);
7046
+ const stats = fs22.statSync(failuresPath);
6911
7047
  const cacheKey = failuresPath;
6912
7048
  const cached = failuresCacheMap.get(cacheKey);
6913
7049
  if (cached && cached.mtimeMs === stats.mtimeMs) {
6914
7050
  return Ok(cached.entries);
6915
7051
  }
6916
- const content = fs21.readFileSync(failuresPath, "utf-8");
7052
+ const content = fs22.readFileSync(failuresPath, "utf-8");
6917
7053
  const entries = [];
6918
7054
  for (const line of content.split("\n")) {
6919
7055
  const match = line.match(FAILURE_LINE_REGEX);
@@ -6942,20 +7078,20 @@ async function archiveFailures(projectPath, stream, session) {
6942
7078
  const dirResult = await getStateDir(projectPath, stream, session);
6943
7079
  if (!dirResult.ok) return dirResult;
6944
7080
  const stateDir = dirResult.value;
6945
- const failuresPath = path18.join(stateDir, FAILURES_FILE);
6946
- if (!fs21.existsSync(failuresPath)) {
7081
+ const failuresPath = path19.join(stateDir, FAILURES_FILE);
7082
+ if (!fs22.existsSync(failuresPath)) {
6947
7083
  return Ok(void 0);
6948
7084
  }
6949
- const archiveDir = path18.join(stateDir, "archive");
6950
- fs21.mkdirSync(archiveDir, { recursive: true });
7085
+ const archiveDir = path19.join(stateDir, "archive");
7086
+ fs22.mkdirSync(archiveDir, { recursive: true });
6951
7087
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
6952
7088
  let archiveName = `failures-${date}.md`;
6953
7089
  let counter = 2;
6954
- while (fs21.existsSync(path18.join(archiveDir, archiveName))) {
7090
+ while (fs22.existsSync(path19.join(archiveDir, archiveName))) {
6955
7091
  archiveName = `failures-${date}-${counter}.md`;
6956
7092
  counter++;
6957
7093
  }
6958
- fs21.renameSync(failuresPath, path18.join(archiveDir, archiveName));
7094
+ fs22.renameSync(failuresPath, path19.join(archiveDir, archiveName));
6959
7095
  failuresCacheMap.delete(failuresPath);
6960
7096
  return Ok(void 0);
6961
7097
  } catch (error) {
@@ -6971,11 +7107,11 @@ async function saveHandoff(projectPath, handoff, stream, session) {
6971
7107
  const dirResult = await getStateDir(projectPath, stream, session);
6972
7108
  if (!dirResult.ok) return dirResult;
6973
7109
  const stateDir = dirResult.value;
6974
- const handoffPath = path19.join(stateDir, HANDOFF_FILE);
6975
- fs22.mkdirSync(stateDir, { recursive: true });
7110
+ const handoffPath = path20.join(stateDir, HANDOFF_FILE);
7111
+ fs23.mkdirSync(stateDir, { recursive: true });
6976
7112
  const tmpPath = handoffPath + ".tmp";
6977
- fs22.writeFileSync(tmpPath, JSON.stringify(handoff, null, 2));
6978
- fs22.renameSync(tmpPath, handoffPath);
7113
+ fs23.writeFileSync(tmpPath, JSON.stringify(handoff, null, 2));
7114
+ fs23.renameSync(tmpPath, handoffPath);
6979
7115
  return Ok(void 0);
6980
7116
  } catch (error) {
6981
7117
  return Err(
@@ -6988,11 +7124,11 @@ async function loadHandoff(projectPath, stream, session) {
6988
7124
  const dirResult = await getStateDir(projectPath, stream, session);
6989
7125
  if (!dirResult.ok) return dirResult;
6990
7126
  const stateDir = dirResult.value;
6991
- const handoffPath = path19.join(stateDir, HANDOFF_FILE);
6992
- if (!fs22.existsSync(handoffPath)) {
7127
+ const handoffPath = path20.join(stateDir, HANDOFF_FILE);
7128
+ if (!fs23.existsSync(handoffPath)) {
6993
7129
  return Ok(null);
6994
7130
  }
6995
- const raw = fs22.readFileSync(handoffPath, "utf-8");
7131
+ const raw = fs23.readFileSync(handoffPath, "utf-8");
6996
7132
  const parsed = JSON.parse(raw);
6997
7133
  const result = HandoffSchema.safeParse(parsed);
6998
7134
  if (!result.success) {
@@ -7006,28 +7142,28 @@ async function loadHandoff(projectPath, stream, session) {
7006
7142
  }
7007
7143
  }
7008
7144
  function loadChecksFromConfig(gateConfigPath) {
7009
- if (!fs23.existsSync(gateConfigPath)) return [];
7010
- const raw = JSON.parse(fs23.readFileSync(gateConfigPath, "utf-8"));
7145
+ if (!fs24.existsSync(gateConfigPath)) return [];
7146
+ const raw = JSON.parse(fs24.readFileSync(gateConfigPath, "utf-8"));
7011
7147
  const config = GateConfigSchema.safeParse(raw);
7012
7148
  if (config.success && config.data.checks) return config.data.checks;
7013
7149
  return [];
7014
7150
  }
7015
7151
  function discoverChecksFromProject(projectPath) {
7016
7152
  const checks = [];
7017
- const packageJsonPath = path20.join(projectPath, "package.json");
7018
- if (fs23.existsSync(packageJsonPath)) {
7019
- const pkg = JSON.parse(fs23.readFileSync(packageJsonPath, "utf-8"));
7153
+ const packageJsonPath = path21.join(projectPath, "package.json");
7154
+ if (fs24.existsSync(packageJsonPath)) {
7155
+ const pkg = JSON.parse(fs24.readFileSync(packageJsonPath, "utf-8"));
7020
7156
  const scripts = pkg.scripts || {};
7021
7157
  if (scripts.test) checks.push({ name: "test", command: "npm test" });
7022
7158
  if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
7023
7159
  if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
7024
7160
  if (scripts.build) checks.push({ name: "build", command: "npm run build" });
7025
7161
  }
7026
- if (fs23.existsSync(path20.join(projectPath, "go.mod"))) {
7162
+ if (fs24.existsSync(path21.join(projectPath, "go.mod"))) {
7027
7163
  checks.push({ name: "test", command: "go test ./..." });
7028
7164
  checks.push({ name: "build", command: "go build ./..." });
7029
7165
  }
7030
- if (fs23.existsSync(path20.join(projectPath, "pyproject.toml")) || fs23.existsSync(path20.join(projectPath, "setup.py"))) {
7166
+ if (fs24.existsSync(path21.join(projectPath, "pyproject.toml")) || fs24.existsSync(path21.join(projectPath, "setup.py"))) {
7031
7167
  checks.push({ name: "test", command: "python -m pytest" });
7032
7168
  }
7033
7169
  return checks;
@@ -7067,8 +7203,8 @@ function executeCheck(check, projectPath) {
7067
7203
  }
7068
7204
  }
7069
7205
  async function runMechanicalGate(projectPath) {
7070
- const harnessDir = path20.join(projectPath, HARNESS_DIR);
7071
- const gateConfigPath = path20.join(harnessDir, GATE_CONFIG_FILE);
7206
+ const harnessDir = path21.join(projectPath, HARNESS_DIR);
7207
+ const gateConfigPath = path21.join(harnessDir, GATE_CONFIG_FILE);
7072
7208
  try {
7073
7209
  let checks = loadChecksFromConfig(gateConfigPath);
7074
7210
  if (checks.length === 0) {
@@ -7124,9 +7260,9 @@ function writeSessionSummary(projectPath, sessionSlug, data) {
7124
7260
  const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
7125
7261
  if (!dirResult.ok) return dirResult;
7126
7262
  const sessionDir = dirResult.value;
7127
- const summaryPath = path21.join(sessionDir, SUMMARY_FILE);
7263
+ const summaryPath = path22.join(sessionDir, SUMMARY_FILE);
7128
7264
  const content = formatSummary(data);
7129
- fs24.writeFileSync(summaryPath, content);
7265
+ fs25.writeFileSync(summaryPath, content);
7130
7266
  const description = deriveIndexDescription(data);
7131
7267
  updateSessionIndex(projectPath, sessionSlug, description);
7132
7268
  return Ok(void 0);
@@ -7143,11 +7279,11 @@ function loadSessionSummary(projectPath, sessionSlug) {
7143
7279
  const dirResult = resolveSessionDir(projectPath, sessionSlug);
7144
7280
  if (!dirResult.ok) return dirResult;
7145
7281
  const sessionDir = dirResult.value;
7146
- const summaryPath = path21.join(sessionDir, SUMMARY_FILE);
7147
- if (!fs24.existsSync(summaryPath)) {
7282
+ const summaryPath = path22.join(sessionDir, SUMMARY_FILE);
7283
+ if (!fs25.existsSync(summaryPath)) {
7148
7284
  return Ok(null);
7149
7285
  }
7150
- const content = fs24.readFileSync(summaryPath, "utf-8");
7286
+ const content = fs25.readFileSync(summaryPath, "utf-8");
7151
7287
  return Ok(content);
7152
7288
  } catch (error) {
7153
7289
  return Err(
@@ -7159,11 +7295,11 @@ function loadSessionSummary(projectPath, sessionSlug) {
7159
7295
  }
7160
7296
  function listActiveSessions(projectPath) {
7161
7297
  try {
7162
- const indexPath2 = path21.join(projectPath, HARNESS_DIR, SESSIONS_DIR, SESSION_INDEX_FILE);
7163
- if (!fs24.existsSync(indexPath2)) {
7298
+ const indexPath2 = path22.join(projectPath, HARNESS_DIR, SESSIONS_DIR, SESSION_INDEX_FILE);
7299
+ if (!fs25.existsSync(indexPath2)) {
7164
7300
  return Ok(null);
7165
7301
  }
7166
- const content = fs24.readFileSync(indexPath2, "utf-8");
7302
+ const content = fs25.readFileSync(indexPath2, "utf-8");
7167
7303
  return Ok(content);
7168
7304
  } catch (error) {
7169
7305
  return Err(
@@ -7184,12 +7320,12 @@ async function loadSessionState(projectPath, sessionSlug) {
7184
7320
  const dirResult = resolveSessionDir(projectPath, sessionSlug);
7185
7321
  if (!dirResult.ok) return dirResult;
7186
7322
  const sessionDir = dirResult.value;
7187
- const filePath = path22.join(sessionDir, SESSION_STATE_FILE);
7188
- if (!fs25.existsSync(filePath)) {
7323
+ const filePath = path23.join(sessionDir, SESSION_STATE_FILE);
7324
+ if (!fs26.existsSync(filePath)) {
7189
7325
  return Ok(emptySections());
7190
7326
  }
7191
7327
  try {
7192
- const raw = fs25.readFileSync(filePath, "utf-8");
7328
+ const raw = fs26.readFileSync(filePath, "utf-8");
7193
7329
  const parsed = JSON.parse(raw);
7194
7330
  const sections = emptySections();
7195
7331
  for (const name of SESSION_SECTION_NAMES) {
@@ -7210,9 +7346,9 @@ async function saveSessionState(projectPath, sessionSlug, sections) {
7210
7346
  const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
7211
7347
  if (!dirResult.ok) return dirResult;
7212
7348
  const sessionDir = dirResult.value;
7213
- const filePath = path22.join(sessionDir, SESSION_STATE_FILE);
7349
+ const filePath = path23.join(sessionDir, SESSION_STATE_FILE);
7214
7350
  try {
7215
- fs25.writeFileSync(filePath, JSON.stringify(sections, null, 2));
7351
+ fs26.writeFileSync(filePath, JSON.stringify(sections, null, 2));
7216
7352
  return Ok(void 0);
7217
7353
  } catch (error) {
7218
7354
  return Err(
@@ -7268,26 +7404,26 @@ async function archiveSession(projectPath, sessionSlug, options = {}) {
7268
7404
  const dirResult = resolveSessionDir(projectPath, sessionSlug);
7269
7405
  if (!dirResult.ok) return dirResult;
7270
7406
  const sessionDir = dirResult.value;
7271
- if (!fs26.existsSync(sessionDir)) {
7407
+ if (!fs27.existsSync(sessionDir)) {
7272
7408
  return Err(new Error(`Session '${sessionSlug}' not found at ${sessionDir}`));
7273
7409
  }
7274
- const archiveBase = path23.join(projectPath, HARNESS_DIR, ARCHIVE_DIR, "sessions");
7410
+ const archiveBase = path24.join(projectPath, HARNESS_DIR, ARCHIVE_DIR, "sessions");
7275
7411
  try {
7276
- fs26.mkdirSync(archiveBase, { recursive: true });
7412
+ fs27.mkdirSync(archiveBase, { recursive: true });
7277
7413
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
7278
7414
  let archiveName = `${sessionSlug}-${date}`;
7279
7415
  let counter = 1;
7280
- while (fs26.existsSync(path23.join(archiveBase, archiveName))) {
7416
+ while (fs27.existsSync(path24.join(archiveBase, archiveName))) {
7281
7417
  archiveName = `${sessionSlug}-${date}-${counter}`;
7282
7418
  counter++;
7283
7419
  }
7284
- const dest = path23.join(archiveBase, archiveName);
7420
+ const dest = path24.join(archiveBase, archiveName);
7285
7421
  try {
7286
- fs26.renameSync(sessionDir, dest);
7422
+ fs27.renameSync(sessionDir, dest);
7287
7423
  } catch (renameErr) {
7288
7424
  if (renameErr instanceof Error && "code" in renameErr && renameErr.code === "EXDEV") {
7289
- fs26.cpSync(sessionDir, dest, { recursive: true });
7290
- fs26.rmSync(sessionDir, { recursive: true });
7425
+ fs27.cpSync(sessionDir, dest, { recursive: true });
7426
+ fs27.rmSync(sessionDir, { recursive: true });
7291
7427
  } else {
7292
7428
  throw renameErr;
7293
7429
  }
@@ -7322,8 +7458,8 @@ function loadKnownHashes(eventsPath) {
7322
7458
  const cached = knownHashesCache.get(eventsPath);
7323
7459
  if (cached) return cached;
7324
7460
  const hashes = /* @__PURE__ */ new Set();
7325
- if (fs27.existsSync(eventsPath)) {
7326
- const content = fs27.readFileSync(eventsPath, "utf-8");
7461
+ if (fs28.existsSync(eventsPath)) {
7462
+ const content = fs28.readFileSync(eventsPath, "utf-8");
7327
7463
  const lines = content.split("\n").filter((line) => line.trim() !== "");
7328
7464
  for (const line of lines) {
7329
7465
  try {
@@ -7346,8 +7482,8 @@ async function emitEvent(projectPath, event, options) {
7346
7482
  const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
7347
7483
  if (!dirResult.ok) return dirResult;
7348
7484
  const stateDir = dirResult.value;
7349
- const eventsPath = path24.join(stateDir, EVENTS_FILE);
7350
- fs27.mkdirSync(stateDir, { recursive: true });
7485
+ const eventsPath = path25.join(stateDir, EVENTS_FILE);
7486
+ fs28.mkdirSync(stateDir, { recursive: true });
7351
7487
  const contentHash = computeEventHash(event, options?.session);
7352
7488
  const knownHashes = loadKnownHashes(eventsPath);
7353
7489
  if (knownHashes.has(contentHash)) {
@@ -7361,7 +7497,7 @@ async function emitEvent(projectPath, event, options) {
7361
7497
  if (options?.session) {
7362
7498
  fullEvent.session = options.session;
7363
7499
  }
7364
- fs27.appendFileSync(eventsPath, JSON.stringify(fullEvent) + "\n");
7500
+ fs28.appendFileSync(eventsPath, JSON.stringify(fullEvent) + "\n");
7365
7501
  knownHashes.add(contentHash);
7366
7502
  return Ok({ written: true });
7367
7503
  } catch (error) {
@@ -7375,11 +7511,11 @@ async function loadEvents(projectPath, options) {
7375
7511
  const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
7376
7512
  if (!dirResult.ok) return dirResult;
7377
7513
  const stateDir = dirResult.value;
7378
- const eventsPath = path24.join(stateDir, EVENTS_FILE);
7379
- if (!fs27.existsSync(eventsPath)) {
7514
+ const eventsPath = path25.join(stateDir, EVENTS_FILE);
7515
+ if (!fs28.existsSync(eventsPath)) {
7380
7516
  return Ok([]);
7381
7517
  }
7382
- const content = fs27.readFileSync(eventsPath, "utf-8");
7518
+ const content = fs28.readFileSync(eventsPath, "utf-8");
7383
7519
  const lines = content.split("\n").filter((line) => line.trim() !== "");
7384
7520
  const events = [];
7385
7521
  for (const line of lines) {
@@ -7628,11 +7764,11 @@ function nodeSubStacks(allDeps) {
7628
7764
  return found;
7629
7765
  }
7630
7766
  function detectNodeStacks(projectRoot) {
7631
- const pkgJsonPath = path25.join(projectRoot, "package.json");
7632
- if (!fs28.existsSync(pkgJsonPath)) return [];
7767
+ const pkgJsonPath = path26.join(projectRoot, "package.json");
7768
+ if (!fs29.existsSync(pkgJsonPath)) return [];
7633
7769
  const stacks = ["node"];
7634
7770
  try {
7635
- const pkgJson = JSON.parse(fs28.readFileSync(pkgJsonPath, "utf-8"));
7771
+ const pkgJson = JSON.parse(fs29.readFileSync(pkgJsonPath, "utf-8"));
7636
7772
  const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
7637
7773
  stacks.push(...nodeSubStacks(allDeps));
7638
7774
  } catch {
@@ -7641,10 +7777,10 @@ function detectNodeStacks(projectRoot) {
7641
7777
  }
7642
7778
  function detectStack(projectRoot) {
7643
7779
  const stacks = [...detectNodeStacks(projectRoot)];
7644
- if (fs28.existsSync(path25.join(projectRoot, "go.mod"))) {
7780
+ if (fs29.existsSync(path26.join(projectRoot, "go.mod"))) {
7645
7781
  stacks.push("go");
7646
7782
  }
7647
- const hasPython = fs28.existsSync(path25.join(projectRoot, "requirements.txt")) || fs28.existsSync(path25.join(projectRoot, "pyproject.toml"));
7783
+ const hasPython = fs29.existsSync(path26.join(projectRoot, "requirements.txt")) || fs29.existsSync(path26.join(projectRoot, "pyproject.toml"));
7648
7784
  if (hasPython) {
7649
7785
  stacks.push("python");
7650
7786
  }
@@ -7667,7 +7803,7 @@ function createOsvClient(options = {}) {
7667
7803
  return {
7668
7804
  async check(pkg) {
7669
7805
  const cacheKey = `${pkg.ecosystem}-${sanitizePkgName(pkg.name)}@${pkg.version ?? "latest"}.json`;
7670
- const cachePath = path26.join(cacheDir, cacheKey);
7806
+ const cachePath = path27.join(cacheDir, cacheKey);
7671
7807
  const cached = await readCache(cachePath, ttlMs);
7672
7808
  if (cached) {
7673
7809
  return { ...cached, source: "cache" };
@@ -7688,7 +7824,7 @@ function createOsvClient(options = {}) {
7688
7824
  },
7689
7825
  async clearCache() {
7690
7826
  try {
7691
- await fs30.promises.rm(cacheDir, { recursive: true, force: true });
7827
+ await fs31.promises.rm(cacheDir, { recursive: true, force: true });
7692
7828
  } catch {
7693
7829
  }
7694
7830
  }
@@ -7724,9 +7860,9 @@ function classify(advisories) {
7724
7860
  }
7725
7861
  async function readCache(cachePath, ttlMs) {
7726
7862
  try {
7727
- const stat2 = await fs30.promises.stat(cachePath);
7863
+ const stat2 = await fs31.promises.stat(cachePath);
7728
7864
  if (Date.now() - stat2.mtimeMs > ttlMs) return null;
7729
- const buf = await fs30.promises.readFile(cachePath, "utf-8");
7865
+ const buf = await fs31.promises.readFile(cachePath, "utf-8");
7730
7866
  const parsed = JSON.parse(buf);
7731
7867
  return { malicious: parsed.malicious ?? [], other: parsed.other ?? [] };
7732
7868
  } catch {
@@ -7735,8 +7871,8 @@ async function readCache(cachePath, ttlMs) {
7735
7871
  }
7736
7872
  async function writeCache(cachePath, payload) {
7737
7873
  try {
7738
- await fs30.promises.mkdir(path26.dirname(cachePath), { recursive: true });
7739
- await fs30.promises.writeFile(cachePath, JSON.stringify(payload, null, 2), "utf-8");
7874
+ await fs31.promises.mkdir(path27.dirname(cachePath), { recursive: true });
7875
+ await fs31.promises.writeFile(cachePath, JSON.stringify(payload, null, 2), "utf-8");
7740
7876
  } catch {
7741
7877
  }
7742
7878
  }
@@ -7772,7 +7908,7 @@ function getInjectionPatterns() {
7772
7908
  }
7773
7909
  function getTaintFilePath(projectRoot, sessionId) {
7774
7910
  const id = sessionId || DEFAULT_SESSION_ID;
7775
- return join382(projectRoot, ".harness", `session-taint-${id}.json`);
7911
+ return join39(projectRoot, ".harness", `session-taint-${id}.json`);
7776
7912
  }
7777
7913
  function readTaint(projectRoot, sessionId) {
7778
7914
  const filePath = getTaintFilePath(projectRoot, sessionId);
@@ -7854,14 +7990,14 @@ function clearTaint(projectRoot, sessionId) {
7854
7990
  return 0;
7855
7991
  }
7856
7992
  }
7857
- const harnessDir = join382(projectRoot, ".harness");
7993
+ const harnessDir = join39(projectRoot, ".harness");
7858
7994
  let count = 0;
7859
7995
  try {
7860
7996
  const files = readdirSync3(harnessDir);
7861
7997
  for (const file of files) {
7862
7998
  if (file.startsWith("session-taint-") && file.endsWith(".json")) {
7863
7999
  try {
7864
- unlinkSync2(join382(harnessDir, file));
8000
+ unlinkSync2(join39(harnessDir, file));
7865
8001
  count++;
7866
8002
  } catch {
7867
8003
  }
@@ -7872,7 +8008,7 @@ function clearTaint(projectRoot, sessionId) {
7872
8008
  return count;
7873
8009
  }
7874
8010
  function listTaintedSessions(projectRoot) {
7875
- const harnessDir = join382(projectRoot, ".harness");
8011
+ const harnessDir = join39(projectRoot, ".harness");
7876
8012
  const sessions = [];
7877
8013
  try {
7878
8014
  const files = readdirSync3(harnessDir);
@@ -7944,7 +8080,7 @@ function securityFindingId(finding) {
7944
8080
  }
7945
8081
  async function runValidateCheck(projectRoot, config) {
7946
8082
  const issues = [];
7947
- const agentsPath = path27.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
8083
+ const agentsPath = path28.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
7948
8084
  const result = await validateAgentsMap(agentsPath);
7949
8085
  if (!result.ok) {
7950
8086
  issues.push({ severity: "error", message: result.error.message });
@@ -8001,7 +8137,7 @@ async function runDepsCheck(projectRoot, config) {
8001
8137
  }
8002
8138
  async function runDocsCheck(projectRoot, config) {
8003
8139
  const issues = [];
8004
- const docsDir = path27.join(projectRoot, config.docsDir ?? "docs");
8140
+ const docsDir = path28.join(projectRoot, config.docsDir ?? "docs");
8005
8141
  const entropyConfig = config.entropy || {};
8006
8142
  const result = await checkDocCoverage("project", {
8007
8143
  docsDir,
@@ -8180,7 +8316,7 @@ async function runTraceabilityCheck(projectRoot, config) {
8180
8316
  const issues = [];
8181
8317
  const traceConfig = config.traceability || {};
8182
8318
  if (traceConfig.enabled === false) return issues;
8183
- const graphDir = path27.join(projectRoot, ".harness", "graph");
8319
+ const graphDir = path28.join(projectRoot, ".harness", "graph");
8184
8320
  const store = new GraphStore();
8185
8321
  const loaded = await store.load(graphDir);
8186
8322
  if (!loaded) {
@@ -8358,7 +8494,7 @@ async function runMechanicalChecks(options) {
8358
8494
  };
8359
8495
  if (!skip.includes("validate")) {
8360
8496
  try {
8361
- const agentsPath = path28.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
8497
+ const agentsPath = path29.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
8362
8498
  const result = await validateAgentsMap(agentsPath);
8363
8499
  if (!result.ok) {
8364
8500
  statuses.validate = "fail";
@@ -8395,7 +8531,7 @@ async function runMechanicalChecks(options) {
8395
8531
  statuses.validate = "fail";
8396
8532
  findings.push({
8397
8533
  tool: "validate",
8398
- file: path28.join(projectRoot, "AGENTS.md"),
8534
+ file: path29.join(projectRoot, "AGENTS.md"),
8399
8535
  message: err instanceof Error ? err.message : String(err),
8400
8536
  severity: "error"
8401
8537
  });
@@ -8459,7 +8595,7 @@ async function runMechanicalChecks(options) {
8459
8595
  (async () => {
8460
8596
  const localFindings = [];
8461
8597
  try {
8462
- const docsDir = path28.join(projectRoot, config.docsDir ?? "docs");
8598
+ const docsDir = path29.join(projectRoot, config.docsDir ?? "docs");
8463
8599
  const result = await checkDocCoverage("project", { docsDir });
8464
8600
  if (!result.ok) {
8465
8601
  statuses["check-docs"] = "warn";
@@ -8486,7 +8622,7 @@ async function runMechanicalChecks(options) {
8486
8622
  statuses["check-docs"] = "warn";
8487
8623
  localFindings.push({
8488
8624
  tool: "check-docs",
8489
- file: path28.join(projectRoot, "docs"),
8625
+ file: path29.join(projectRoot, "docs"),
8490
8626
  message: err instanceof Error ? err.message : String(err),
8491
8627
  severity: "warning"
8492
8628
  });
@@ -8583,18 +8719,18 @@ function computeContextBudget(diffLines) {
8583
8719
  return diffLines;
8584
8720
  }
8585
8721
  function isWithinProject(absPath, projectRoot) {
8586
- const resolvedRoot = path29.resolve(projectRoot) + path29.sep;
8587
- const resolvedPath = path29.resolve(absPath);
8588
- return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path29.resolve(projectRoot);
8722
+ const resolvedRoot = path30.resolve(projectRoot) + path30.sep;
8723
+ const resolvedPath = path30.resolve(absPath);
8724
+ return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path30.resolve(projectRoot);
8589
8725
  }
8590
8726
  async function readContextFile(projectRoot, filePath, reason) {
8591
- const absPath = path29.isAbsolute(filePath) ? filePath : path29.join(projectRoot, filePath);
8727
+ const absPath = path30.isAbsolute(filePath) ? filePath : path30.join(projectRoot, filePath);
8592
8728
  if (!isWithinProject(absPath, projectRoot)) return null;
8593
8729
  const result = await readFileContent(absPath);
8594
8730
  if (!result.ok) return null;
8595
8731
  const content = result.value;
8596
8732
  const lines = content.split("\n").length;
8597
- const relPath2 = path29.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
8733
+ const relPath2 = path30.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
8598
8734
  return { path: relPath2, content, reason, lines };
8599
8735
  }
8600
8736
  function extractImportSources2(content) {
@@ -8609,24 +8745,24 @@ function extractImportSources2(content) {
8609
8745
  }
8610
8746
  async function resolveImportPath(projectRoot, fromFile, importSource) {
8611
8747
  if (!importSource.startsWith(".")) return null;
8612
- const fromDir = path29.dirname(path29.join(projectRoot, fromFile));
8613
- const basePath = path29.resolve(fromDir, importSource);
8748
+ const fromDir = path30.dirname(path30.join(projectRoot, fromFile));
8749
+ const basePath = path30.resolve(fromDir, importSource);
8614
8750
  if (!isWithinProject(basePath, projectRoot)) return null;
8615
8751
  const relBase = relativePosix(projectRoot, basePath);
8616
- const sourceExt = path29.extname(relBase);
8752
+ const sourceExt = path30.extname(relBase);
8617
8753
  const candidates = [];
8618
8754
  const fallbacks = JS_EXT_FALLBACKS[sourceExt];
8619
8755
  if (fallbacks) {
8620
8756
  const stripped = relBase.slice(0, -sourceExt.length);
8621
8757
  for (const ext of fallbacks) candidates.push(stripped + ext);
8622
- candidates.push(path29.join(stripped, "index.ts"));
8623
- candidates.push(path29.join(stripped, "index.tsx"));
8624
- candidates.push(path29.join(stripped, "index.jsx"));
8758
+ candidates.push(path30.join(stripped, "index.ts"));
8759
+ candidates.push(path30.join(stripped, "index.tsx"));
8760
+ candidates.push(path30.join(stripped, "index.jsx"));
8625
8761
  }
8626
8762
  candidates.push(relBase + ".ts", relBase + ".tsx", relBase + ".mts");
8627
- candidates.push(path29.join(relBase, "index.ts"));
8763
+ candidates.push(path30.join(relBase, "index.ts"));
8628
8764
  for (const candidate of candidates) {
8629
- const absCandidate = path29.join(projectRoot, candidate);
8765
+ const absCandidate = path30.join(projectRoot, candidate);
8630
8766
  if (await fileExists(absCandidate)) {
8631
8767
  return candidate;
8632
8768
  }
@@ -8634,7 +8770,7 @@ async function resolveImportPath(projectRoot, fromFile, importSource) {
8634
8770
  return null;
8635
8771
  }
8636
8772
  async function findTestFiles(projectRoot, sourceFile) {
8637
- const baseName = path29.basename(sourceFile, path29.extname(sourceFile));
8773
+ const baseName = path30.basename(sourceFile, path30.extname(sourceFile));
8638
8774
  const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
8639
8775
  const results = await findFiles(pattern, projectRoot);
8640
8776
  return results.map((f) => relativePosix(projectRoot, f));
@@ -9370,6 +9506,383 @@ function runLearningsAgent(bundle) {
9370
9506
  const allEntries = learningsContextFiles.flatMap((lcf) => parseLearningEntries(lcf.content));
9371
9507
  return allEntries.map((entry) => matchEntryToFiles(entry, changedPaths)).filter((f) => f !== null);
9372
9508
  }
9509
+ function scan(bundle, predicate) {
9510
+ const matches = [];
9511
+ for (const cf of bundle.changedFiles) {
9512
+ const lines = cf.content.split("\n");
9513
+ for (let i = 0; i < lines.length; i++) {
9514
+ const line = lines[i];
9515
+ if (predicate(line)) matches.push({ file: cf.path, line: i + 1, snippet: line.trim() });
9516
+ }
9517
+ }
9518
+ return matches;
9519
+ }
9520
+ function makeFinding2(category, match, title, rationale, suggestion, confidence) {
9521
+ return {
9522
+ id: makeFindingId("adversarial", match.file, match.line, `${category} ${title}`),
9523
+ file: match.file,
9524
+ lineRange: [match.line, match.line],
9525
+ domain: "bug",
9526
+ severity: confidence >= 75 ? "important" : "suggestion",
9527
+ title,
9528
+ rationale,
9529
+ suggestion,
9530
+ evidence: [`Line ${match.line}: ${match.snippet}`],
9531
+ validatedBy: "heuristic",
9532
+ subagent: "adversarial",
9533
+ confidence
9534
+ };
9535
+ }
9536
+ function detectAssumptionViolations(bundle) {
9537
+ return scan(bundle, (line) => UNVERIFIED_INPUT_PATTERN.test(line)).map(
9538
+ (m) => makeFinding2(
9539
+ "assumption",
9540
+ m,
9541
+ "JSON.parse on untrusted input without shape validation",
9542
+ "The diff assumes the parsed value matches an expected schema. Malformed JSON throws synchronously; structurally-valid-but-semantically-wrong JSON propagates downstream as a malformed object.",
9543
+ "Validate the parsed value against a schema (zod, valibot) or guard each access with a narrow type predicate before use.",
9544
+ 75
9545
+ )
9546
+ );
9547
+ }
9548
+ function detectCompositionFailures(bundle) {
9549
+ const findings = [];
9550
+ findings.push(
9551
+ ...scan(bundle, (line) => COMPOSE_NO_AWAIT.test(line) && !line.includes("await ")).map(
9552
+ (m) => makeFinding2(
9553
+ "composition",
9554
+ m,
9555
+ "Floating Promise chain without await or .catch",
9556
+ "A `.then(...)` chain without `await` or `.catch(...)` produces an unhandled rejection if any link rejects. Composing two such chains lets a failure in one silently delay or stall the other.",
9557
+ "Await the chain or attach `.catch(handler)` and verify the handler is observable.",
9558
+ 75
9559
+ )
9560
+ )
9561
+ );
9562
+ findings.push(
9563
+ ...scan(bundle, (line) => UNGUARDED_OPTIONAL_CALL.test(line)).map(
9564
+ (m) => makeFinding2(
9565
+ "composition",
9566
+ m,
9567
+ "Optional chain over a callable masks composition failure",
9568
+ "Calling `foo?.foo(...)` silently becomes `undefined` when the receiver is nil. When composed with downstream code that expects a value, this propagates as a confusing `undefined` rather than a clear error.",
9569
+ "Decide explicitly: either guard up front and throw a meaningful error, or rename the variable so the optionality is visible at the call site.",
9570
+ 50
9571
+ )
9572
+ )
9573
+ );
9574
+ return findings;
9575
+ }
9576
+ function detectAbuseCases(bundle) {
9577
+ return scan(
9578
+ bundle,
9579
+ (line) => FETCH_NO_TIMEOUT.test(line) && !line.includes("AbortController")
9580
+ ).map(
9581
+ (m) => makeFinding2(
9582
+ "abuse",
9583
+ m,
9584
+ "fetch() without timeout or AbortController",
9585
+ "A `fetch` with no timeout allows a slow or malicious upstream to hold a request open indefinitely. Multiple such calls can exhaust the calling pool.",
9586
+ "Pass `signal: AbortSignal.timeout(ms)` (Node 18+) or wire an `AbortController` with an explicit timeout.",
9587
+ 100
9588
+ )
9589
+ );
9590
+ }
9591
+ function detectCascades(bundle) {
9592
+ return scan(bundle, (line) => PROMISE_NO_REJECT_PATTERN.test(line)).map(
9593
+ (m) => makeFinding2(
9594
+ "cascade",
9595
+ m,
9596
+ "new Promise constructor without a reject parameter",
9597
+ "A Promise that only captures `resolve` cannot signal failure. Errors thrown inside the executor become unhandled, and downstream chains await indefinitely if no `resolve` path is reached.",
9598
+ "Add a `reject` parameter and forward thrown errors, or refactor to an `async` function that returns the value or throws.",
9599
+ 100
9600
+ )
9601
+ );
9602
+ }
9603
+ function runAdversarialAgent(bundle, options = {}) {
9604
+ const { runCascades = true } = options;
9605
+ const findings = [];
9606
+ findings.push(...detectAssumptionViolations(bundle));
9607
+ findings.push(...detectCompositionFailures(bundle));
9608
+ findings.push(...detectAbuseCases(bundle));
9609
+ if (runCascades) findings.push(...detectCascades(bundle));
9610
+ return findings;
9611
+ }
9612
+ function isProductionTsFile(path52) {
9613
+ if (!/\.(ts|tsx)$/.test(path52)) return false;
9614
+ if (/\.d\.ts$/.test(path52)) return false;
9615
+ if (/\.(test|spec)\.(ts|tsx)$/.test(path52)) return false;
9616
+ if (/__tests__\//.test(path52)) return false;
9617
+ return true;
9618
+ }
9619
+ function isProductionFile(path52) {
9620
+ return isProductionTsFile(path52);
9621
+ }
9622
+ function scan2(bundle, predicate) {
9623
+ const matches = [];
9624
+ for (const cf of bundle.changedFiles) {
9625
+ if (!isProductionFile(cf.path)) continue;
9626
+ const lines = cf.content.split("\n");
9627
+ for (let i = 0; i < lines.length; i++) {
9628
+ const line = lines[i];
9629
+ const codePart = stripLineComment(line);
9630
+ if (predicate(codePart) || predicate(line)) {
9631
+ matches.push({ file: cf.path, line: i + 1, snippet: line.trim() });
9632
+ }
9633
+ }
9634
+ }
9635
+ return matches;
9636
+ }
9637
+ function stripLineComment(line) {
9638
+ const idx = line.indexOf("//");
9639
+ if (idx === -1) return line;
9640
+ if (TS_IGNORE_PATTERN.test(line)) return line;
9641
+ return line.slice(0, idx);
9642
+ }
9643
+ function detectExplicitAny(bundle) {
9644
+ return scan2(bundle, (codeLine) => {
9645
+ const cleaned = codeLine.replace(/(['"`])[^'"`]*\1/g, '""');
9646
+ return /:\s*any(?![A-Za-z0-9_$])/.test(cleaned) || /\bas\s+any(?![A-Za-z0-9_$])/.test(cleaned);
9647
+ }).map((m) => ({
9648
+ id: makeFindingId("ts-strict", m.file, m.line, "explicit any"),
9649
+ file: m.file,
9650
+ lineRange: [m.line, m.line],
9651
+ domain: "bug",
9652
+ severity: "important",
9653
+ title: "Explicit `any` disables the type checker",
9654
+ rationale: "An `any`-typed value bypasses every downstream check. Future callers receive no help from the compiler when they pass the wrong shape.",
9655
+ suggestion: "Replace `any` with the narrowest type that compiles \u2014 a union, `unknown` with a type predicate, or a generic. If the shape is truly external, parse it with a schema (zod, valibot) at the boundary.",
9656
+ evidence: [`Line ${m.line}: ${m.snippet}`],
9657
+ validatedBy: "heuristic",
9658
+ subagent: "typescript-strict",
9659
+ confidence: 100
9660
+ })).filter((f) => EXPLICIT_ANY_PATTERN.test(f.evidence[0]));
9661
+ }
9662
+ function detectTsIgnore(bundle) {
9663
+ return scan2(bundle, (line) => TS_IGNORE_PATTERN.test(line)).map((m) => ({
9664
+ id: makeFindingId("ts-strict", m.file, m.line, "ts ignore"),
9665
+ file: m.file,
9666
+ lineRange: [m.line, m.line],
9667
+ domain: "bug",
9668
+ severity: "important",
9669
+ title: "ts-ignore / ts-expect-error suppresses a type error",
9670
+ rationale: "Suppressing a type error hides a real signal from the compiler. The reason for the suppression is rarely captured, and the original error is invisible at the call site.",
9671
+ suggestion: "Either remove the directive and fix the underlying type, or replace it with a typed assertion accompanied by a comment that names the invariant being asserted.",
9672
+ evidence: [`Line ${m.line}: ${m.snippet}`],
9673
+ validatedBy: "heuristic",
9674
+ subagent: "typescript-strict",
9675
+ confidence: 100
9676
+ }));
9677
+ }
9678
+ function detectUnsafeAsUnknown(bundle) {
9679
+ return scan2(bundle, (line) => UNSAFE_AS_UNKNOWN.test(line)).map((m) => ({
9680
+ id: makeFindingId("ts-strict", m.file, m.line, "as unknown as"),
9681
+ file: m.file,
9682
+ lineRange: [m.line, m.line],
9683
+ domain: "bug",
9684
+ severity: "important",
9685
+ title: "`as unknown as X` is a double-cast escape hatch",
9686
+ rationale: "The pattern bypasses the type system entirely. If the runtime shape diverges from X, downstream code crashes with a type-confusion error that points at the wrong line.",
9687
+ suggestion: "Parse the value at the boundary with a schema, or refine via a narrowing predicate so the cast becomes unnecessary.",
9688
+ evidence: [`Line ${m.line}: ${m.snippet}`],
9689
+ validatedBy: "heuristic",
9690
+ subagent: "typescript-strict",
9691
+ confidence: 100
9692
+ }));
9693
+ }
9694
+ function detectNonNullAssertion(bundle) {
9695
+ return scan2(bundle, (line) => NON_NULL_ASSERTION.test(line)).filter((m) => !/process\.env\./.test(m.snippet)).map((m) => ({
9696
+ id: makeFindingId("ts-strict", m.file, m.line, "non null assertion"),
9697
+ file: m.file,
9698
+ lineRange: [m.line, m.line],
9699
+ domain: "bug",
9700
+ severity: "suggestion",
9701
+ title: "Non-null assertion (`!`) bypasses the null check",
9702
+ rationale: "The `!` operator promises the compiler that the value is not nullish. If that promise is wrong, the failure shows up as a `Cannot read properties of undefined` at runtime far from the assertion.",
9703
+ suggestion: "Guard with an explicit `if (value == null) throw new Error(...)` so the assertion is verified at runtime, or refactor the producer to return a non-nullable type.",
9704
+ evidence: [`Line ${m.line}: ${m.snippet}`],
9705
+ validatedBy: "heuristic",
9706
+ subagent: "typescript-strict",
9707
+ confidence: 75
9708
+ }));
9709
+ }
9710
+ function detectLargeFileGrowth(bundle) {
9711
+ return bundle.changedFiles.filter((cf) => isProductionFile(cf.path) && cf.lines > 400).map((cf) => ({
9712
+ id: makeFindingId("ts-strict", cf.path, 1, "file growth"),
9713
+ file: cf.path,
9714
+ lineRange: [1, cf.lines],
9715
+ domain: "architecture",
9716
+ severity: "suggestion",
9717
+ title: `File grew to ${cf.lines} lines \u2014 Single Responsibility risk`,
9718
+ rationale: "Past 400 lines a single TypeScript file typically encodes more than one responsibility. Adding to it compounds the burden on future readers and reviewers.",
9719
+ suggestion: "Identify two responsibilities and split the larger one into a new module. A new file with a clear name reads better than a longer file with vague boundaries.",
9720
+ evidence: [`File length: ${cf.lines} lines`],
9721
+ validatedBy: "heuristic",
9722
+ subagent: "typescript-strict",
9723
+ confidence: 50
9724
+ }));
9725
+ }
9726
+ function detectVagueNames(bundle) {
9727
+ return scan2(bundle, (line) => VAGUE_HELPER_NAME.test(line)).map((m) => ({
9728
+ id: makeFindingId("ts-strict", m.file, m.line, "vague name"),
9729
+ file: m.file,
9730
+ lineRange: [m.line, m.line],
9731
+ domain: "bug",
9732
+ severity: "suggestion",
9733
+ title: "Vague function name \u2014 fails the five-second rule",
9734
+ rationale: "A name like `handle*` or `process*` or `doStuff` does not let a reader predict what the function does from a call site. Future maintainers must read the body to know \u2014 every time.",
9735
+ suggestion: "Rename to a verb-object phrase that says what changes (e.g., `applyDiscount`, `serializeOrder`). If you cannot name it, the function probably does more than one thing.",
9736
+ evidence: [`Line ${m.line}: ${m.snippet}`],
9737
+ validatedBy: "heuristic",
9738
+ subagent: "typescript-strict",
9739
+ confidence: 50
9740
+ }));
9741
+ }
9742
+ function runTypescriptStrictAgent(bundle) {
9743
+ const findings = [];
9744
+ findings.push(...detectExplicitAny(bundle));
9745
+ findings.push(...detectTsIgnore(bundle));
9746
+ findings.push(...detectUnsafeAsUnknown(bundle));
9747
+ findings.push(...detectNonNullAssertion(bundle));
9748
+ findings.push(...detectLargeFileGrowth(bundle));
9749
+ findings.push(...detectVagueNames(bundle));
9750
+ return findings;
9751
+ }
9752
+ function frontendFiles(bundle) {
9753
+ return bundle.changedFiles.filter((cf) => /\.(ts|tsx|jsx|js)$/.test(cf.path)).filter((cf) => !/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(cf.path)).filter((cf) => !/__tests__\//.test(cf.path)).filter((cf) => !/\.d\.ts$/.test(cf.path)).map((cf) => ({ path: cf.path, content: cf.content, lines: cf.content.split("\n") }));
9754
+ }
9755
+ function detectUnclearedTimers(bundle) {
9756
+ const findings = [];
9757
+ for (const file of frontendFiles(bundle)) {
9758
+ const hasClearInterval = /\bclearInterval\s*\(/.test(file.content);
9759
+ const hasClearTimeout = /\bclearTimeout\s*\(/.test(file.content);
9760
+ for (let i = 0; i < file.lines.length; i++) {
9761
+ const line = file.lines[i];
9762
+ if (/\bsetInterval\s*\(/.test(line) && !hasClearInterval) {
9763
+ findings.push({
9764
+ id: makeFindingId("frontend-races", file.path, i + 1, "setInterval no clear"),
9765
+ file: file.path,
9766
+ lineRange: [i + 1, i + 1],
9767
+ domain: "bug",
9768
+ severity: "important",
9769
+ title: "setInterval without matching clearInterval in the same module",
9770
+ rationale: "An interval that is never cleared keeps firing after the owning component, controller, or scope is gone. Each render creates a new timer; old timers continue mutating state that no longer exists.",
9771
+ suggestion: "Capture the handle and clear it in the matching teardown path (useEffect return, controller `disconnect`, or explicit `dispose`). For one-shot work, prefer setTimeout with a clearTimeout in the cleanup path.",
9772
+ evidence: [`Line ${i + 1}: ${line.trim()}`],
9773
+ validatedBy: "heuristic",
9774
+ subagent: "frontend-races",
9775
+ confidence: 100
9776
+ });
9777
+ }
9778
+ if (/\bsetTimeout\s*\(/.test(line) && !hasClearTimeout && // ignore intentional fire-and-forget where the callback is a single bound function with no closure access
9779
+ !/setTimeout\s*\(\s*[A-Za-z_$][\w$]*\s*(?:,|\))/.test(line)) {
9780
+ findings.push({
9781
+ id: makeFindingId("frontend-races", file.path, i + 1, "setTimeout no clear"),
9782
+ file: file.path,
9783
+ lineRange: [i + 1, i + 1],
9784
+ domain: "bug",
9785
+ severity: "suggestion",
9786
+ title: "setTimeout with closure access but no clearTimeout in the same module",
9787
+ rationale: 'A timeout that closes over mutable state can fire after the owner is gone, updating stale references and triggering "set state on unmounted component" warnings.',
9788
+ suggestion: "Capture the timeout id and clear it in the teardown path, or extract a pure function and bind explicit arguments.",
9789
+ evidence: [`Line ${i + 1}: ${line.trim()}`],
9790
+ validatedBy: "heuristic",
9791
+ subagent: "frontend-races",
9792
+ confidence: 75
9793
+ });
9794
+ }
9795
+ }
9796
+ }
9797
+ return findings;
9798
+ }
9799
+ function detectDanglingListeners(bundle) {
9800
+ const findings = [];
9801
+ for (const file of frontendFiles(bundle)) {
9802
+ const hasRemove = /\bremoveEventListener\s*\(/.test(file.content);
9803
+ for (let i = 0; i < file.lines.length; i++) {
9804
+ const line = file.lines[i];
9805
+ if (/\baddEventListener\s*\(/.test(line) && !hasRemove) {
9806
+ findings.push({
9807
+ id: makeFindingId("frontend-races", file.path, i + 1, "listener no remove"),
9808
+ file: file.path,
9809
+ lineRange: [i + 1, i + 1],
9810
+ domain: "bug",
9811
+ severity: "important",
9812
+ title: "addEventListener without removeEventListener in the same module",
9813
+ rationale: "Event listeners that are not removed accumulate across mounts. Each mount adds another callback, and old callbacks fire against stale state.",
9814
+ suggestion: "Pair every `addEventListener` with a `removeEventListener` in the teardown path. In React, return the cleanup from `useEffect`; in Stimulus, remove in `disconnect()`.",
9815
+ evidence: [`Line ${i + 1}: ${line.trim()}`],
9816
+ validatedBy: "heuristic",
9817
+ subagent: "frontend-races",
9818
+ confidence: 100
9819
+ });
9820
+ }
9821
+ }
9822
+ }
9823
+ return findings;
9824
+ }
9825
+ function detectMisplacedStateUpdate(bundle) {
9826
+ const findings = [];
9827
+ const useEffectStateRe = /useEffect\s*\(\s*\(\)\s*=>\s*\{\s*set[A-Z]\w*\s*\(/;
9828
+ for (const file of frontendFiles(bundle)) {
9829
+ for (let i = 0; i < file.lines.length; i++) {
9830
+ const window = file.lines.slice(i, Math.min(i + 3, file.lines.length)).join("\n");
9831
+ if (useEffectStateRe.test(window)) {
9832
+ findings.push({
9833
+ id: makeFindingId("frontend-races", file.path, i + 1, "state in useEffect"),
9834
+ file: file.path,
9835
+ lineRange: [i + 1, Math.min(i + 3, file.lines.length)],
9836
+ domain: "bug",
9837
+ severity: "suggestion",
9838
+ title: "useEffect immediately calling setState may indicate derived state",
9839
+ rationale: "When an effect runs only to set state from props or render-time values, the same value can be derived inline. The effect adds a render, a stale-closure risk, and a window where the state and its source disagree.",
9840
+ suggestion: "Compute the value inline from props. Reserve `useEffect` for true side effects (subscriptions, IO).",
9841
+ evidence: [`Line ${i + 1}: ${file.lines[i].trim()}`],
9842
+ validatedBy: "heuristic",
9843
+ subagent: "frontend-races",
9844
+ confidence: 50
9845
+ });
9846
+ }
9847
+ }
9848
+ }
9849
+ return findings;
9850
+ }
9851
+ function detectStaleResponseRace(bundle) {
9852
+ const findings = [];
9853
+ for (const file of frontendFiles(bundle)) {
9854
+ for (let i = 0; i < file.lines.length - 4; i++) {
9855
+ const window = file.lines.slice(i, i + 5).join("\n");
9856
+ if (!/\bawait\s+(?:fetch|axios|api)/.test(window)) continue;
9857
+ if (!/\bset[A-Z]\w*\s*\(/.test(window)) continue;
9858
+ if (/AbortController|AbortSignal\.timeout|signal:/.test(window)) continue;
9859
+ findings.push({
9860
+ id: makeFindingId("frontend-races", file.path, i + 1, "stale response race"),
9861
+ file: file.path,
9862
+ lineRange: [i + 1, i + 5],
9863
+ domain: "bug",
9864
+ severity: "important",
9865
+ title: "await fetch followed by setState without an abort signal",
9866
+ rationale: "When the component unmounts (or re-renders with new inputs) before the response resolves, the late-arriving setState writes stale data onto a now-replaced component, causing flashes of wrong content or warnings about unmounted components.",
9867
+ suggestion: "Pass `signal: AbortSignal.timeout(ms)` or an `AbortController` and abort it in the cleanup path. Alternatively, ignore the response when a request-generation counter has advanced.",
9868
+ evidence: [`Lines ${i + 1}-${i + 5}`],
9869
+ validatedBy: "heuristic",
9870
+ subagent: "frontend-races",
9871
+ confidence: 75
9872
+ });
9873
+ i += 4;
9874
+ }
9875
+ }
9876
+ return findings;
9877
+ }
9878
+ function runFrontendRacesAgent(bundle) {
9879
+ const findings = [];
9880
+ findings.push(...detectUnclearedTimers(bundle));
9881
+ findings.push(...detectDanglingListeners(bundle));
9882
+ findings.push(...detectMisplacedStateUpdate(bundle));
9883
+ findings.push(...detectStaleResponseRace(bundle));
9884
+ return findings;
9885
+ }
9373
9886
  async function runAgent(bundle) {
9374
9887
  const start = Date.now();
9375
9888
  const runner = AGENT_RUNNERS[bundle.domain];
@@ -9387,6 +9900,160 @@ async function fanOutReview(options) {
9387
9900
  const results = await Promise.all(bundles.map((bundle) => runAgent(bundle)));
9388
9901
  return results;
9389
9902
  }
9903
+ async function fanOutConditionalSubagents(options) {
9904
+ const { bundles, activations, depth } = options;
9905
+ if (activations.size === 0 || bundles.length === 0) return [];
9906
+ const bugBundle = bundles.find((b) => b.domain === "bug") ?? bundles[0];
9907
+ const tasks = [];
9908
+ if (activations.has("adversarial")) {
9909
+ tasks.push(
9910
+ Promise.resolve().then(() => {
9911
+ const start = Date.now();
9912
+ const findings = runAdversarialAgent(bugBundle, { runCascades: depth === "deep" });
9913
+ return {
9914
+ subagent: "adversarial",
9915
+ findings,
9916
+ durationMs: Date.now() - start
9917
+ };
9918
+ })
9919
+ );
9920
+ }
9921
+ if (activations.has("typescript-strict")) {
9922
+ tasks.push(
9923
+ Promise.resolve().then(() => {
9924
+ const start = Date.now();
9925
+ const findings = runTypescriptStrictAgent(bugBundle);
9926
+ return {
9927
+ subagent: "typescript-strict",
9928
+ findings,
9929
+ durationMs: Date.now() - start
9930
+ };
9931
+ })
9932
+ );
9933
+ }
9934
+ if (activations.has("frontend-races")) {
9935
+ tasks.push(
9936
+ Promise.resolve().then(() => {
9937
+ const start = Date.now();
9938
+ const findings = runFrontendRacesAgent(bugBundle);
9939
+ return {
9940
+ subagent: "frontend-races",
9941
+ findings,
9942
+ durationMs: Date.now() - start
9943
+ };
9944
+ })
9945
+ );
9946
+ }
9947
+ return Promise.all(tasks);
9948
+ }
9949
+ function isExcludedPath(path52) {
9950
+ return EXCLUDED_PATH_PATTERNS.some((re) => re.test(path52));
9951
+ }
9952
+ function isProductionTsFile2(path52) {
9953
+ if (!/\.(ts|tsx)$/.test(path52)) return false;
9954
+ if (/\.d\.ts$/.test(path52)) return false;
9955
+ return !isExcludedPath(path52);
9956
+ }
9957
+ function countDiffLines(diffContent) {
9958
+ let count = 0;
9959
+ for (const line of diffContent.split("\n")) {
9960
+ if (line.length === 0) continue;
9961
+ const first = line[0];
9962
+ if (first === "+" && !line.startsWith("+++")) count++;
9963
+ else if (first === "-" && !line.startsWith("---")) count++;
9964
+ }
9965
+ return count;
9966
+ }
9967
+ function countChangedLines(diff2) {
9968
+ let total = 0;
9969
+ let counted = false;
9970
+ for (const [path52, content] of diff2.fileDiffs) {
9971
+ if (isExcludedPath(path52)) continue;
9972
+ total += countDiffLines(content);
9973
+ counted = true;
9974
+ }
9975
+ if (counted) return total;
9976
+ if (diff2.changedFiles.length === 0) return 0;
9977
+ const includedFiles = diff2.changedFiles.filter((p) => !isExcludedPath(p)).length;
9978
+ if (includedFiles === 0) return 0;
9979
+ const ratio = includedFiles / diff2.changedFiles.length;
9980
+ return Math.round(diff2.totalDiffLines * ratio);
9981
+ }
9982
+ function buildHaystack(diff2, commitMessage) {
9983
+ const parts = [commitMessage];
9984
+ for (const [path52, content] of diff2.fileDiffs) {
9985
+ parts.push(path52);
9986
+ parts.push(content);
9987
+ }
9988
+ if (diff2.fileDiffs.size === 0) {
9989
+ parts.push(...diff2.changedFiles);
9990
+ }
9991
+ return parts.join("\n");
9992
+ }
9993
+ function keywordMatches(keyword, haystack) {
9994
+ const lowerKey = keyword.toLowerCase();
9995
+ const lowerHay = haystack.toLowerCase();
9996
+ if (keyword.includes(" ")) {
9997
+ return lowerHay.includes(lowerKey);
9998
+ }
9999
+ let idx = lowerHay.indexOf(lowerKey);
10000
+ while (idx !== -1) {
10001
+ const before = idx === 0 ? "" : lowerHay[idx - 1];
10002
+ const after = lowerHay[idx + lowerKey.length] ?? "";
10003
+ const leftOk = before === "" || !WORD_BOUNDARY.test(before);
10004
+ const rightOk = after === "" || !WORD_BOUNDARY.test(after);
10005
+ if (leftOk && rightOk) return true;
10006
+ idx = lowerHay.indexOf(lowerKey, idx + 1);
10007
+ }
10008
+ return false;
10009
+ }
10010
+ function detectRiskKeywords(diff2, commitMessage) {
10011
+ const haystack = buildHaystack(diff2, commitMessage);
10012
+ const matched = [];
10013
+ for (const keyword of RISK_KEYWORDS) {
10014
+ if (keywordMatches(keyword, haystack)) matched.push(keyword);
10015
+ }
10016
+ return matched;
10017
+ }
10018
+ function computeDepth(changedLines, riskKeywordCount) {
10019
+ if (changedLines >= 200 || riskKeywordCount >= 2) return "deep";
10020
+ if (changedLines < 50 && riskKeywordCount === 0) return "quick";
10021
+ return "standard";
10022
+ }
10023
+ function computeActivations(depth, diff2, overridden) {
10024
+ if (overridden && depth === "deep") {
10025
+ return /* @__PURE__ */ new Set(["adversarial", "typescript-strict", "frontend-races"]);
10026
+ }
10027
+ const activations = /* @__PURE__ */ new Set();
10028
+ if (depth !== "quick") activations.add("adversarial");
10029
+ const hasTsFile = diff2.changedFiles.some(isProductionTsFile2);
10030
+ if (hasTsFile) activations.add("typescript-strict");
10031
+ if (activations.has("typescript-strict") && hasAsyncUiSignal(diff2)) {
10032
+ activations.add("frontend-races");
10033
+ }
10034
+ return activations;
10035
+ }
10036
+ function hasAsyncUiSignal(diff2) {
10037
+ const tsxInDiff = diff2.changedFiles.some((p) => /\.tsx$/.test(p) && !isExcludedPath(p));
10038
+ if (tsxInDiff) return true;
10039
+ for (const [, content] of diff2.fileDiffs) {
10040
+ for (const signal of ASYNC_UI_SIGNALS) {
10041
+ if (signal === ".tsx") continue;
10042
+ if (content.includes(signal)) return true;
10043
+ }
10044
+ }
10045
+ return false;
10046
+ }
10047
+ function calibrateDepth(options) {
10048
+ const { diff: diff2, commitMessage, override } = options;
10049
+ const changedLines = countChangedLines(diff2);
10050
+ const riskSignals = detectRiskKeywords(diff2, commitMessage);
10051
+ const computed = computeDepth(changedLines, riskSignals.length);
10052
+ const depth = override ?? computed;
10053
+ const overridden = override !== void 0;
10054
+ const activations = computeActivations(depth, diff2, overridden);
10055
+ return { depth, changedLines, riskSignals, activations, overridden };
10056
+ }
9390
10057
  function extractCrossFileRefs(finding) {
9391
10058
  const refs = [];
9392
10059
  const crossFilePattern = /([^\s]+\.(?:ts|tsx|js|jsx))\s+affects\s+([^\s]+\.(?:ts|tsx|js|jsx))/i;
@@ -9402,7 +10069,7 @@ function normalizePath(filePath, projectRoot) {
9402
10069
  let normalized = filePath;
9403
10070
  normalized = normalized.replace(/\\/g, "/");
9404
10071
  const normalizedRoot = projectRoot.replace(/\\/g, "/");
9405
- if (path30.isAbsolute(normalized)) {
10072
+ if (path31.isAbsolute(normalized)) {
9406
10073
  const root = normalizedRoot.endsWith("/") ? normalizedRoot : normalizedRoot + "/";
9407
10074
  if (normalized.startsWith(root)) {
9408
10075
  normalized = normalized.slice(root.length);
@@ -9414,12 +10081,12 @@ function normalizePath(filePath, projectRoot) {
9414
10081
  return normalized;
9415
10082
  }
9416
10083
  function resolveImportPath2(currentFile, importPath) {
9417
- const dir = path30.dirname(currentFile);
9418
- let resolved = path30.join(dir, importPath).replace(/\\/g, "/");
10084
+ const dir = path31.dirname(currentFile);
10085
+ let resolved = path31.join(dir, importPath).replace(/\\/g, "/");
9419
10086
  if (!resolved.match(/\.(ts|tsx|js|jsx)$/)) {
9420
10087
  resolved += ".ts";
9421
10088
  }
9422
- return path30.normalize(resolved).replace(/\\/g, "/");
10089
+ return path31.normalize(resolved).replace(/\\/g, "/");
9423
10090
  }
9424
10091
  function enqueueImports(content, current, visited, queue, maxDepth) {
9425
10092
  const importRegex = /import\s+.*?from\s+['"]([^'"]+)['"]/g;
@@ -9451,7 +10118,7 @@ function isMechanicallyExcluded(finding, exclusionSet, projectRoot) {
9451
10118
  const normalizedFile = normalizePath(finding.file, projectRoot);
9452
10119
  if (exclusionSet.isExcluded(normalizedFile, finding.lineRange)) return true;
9453
10120
  if (exclusionSet.isExcluded(finding.file, finding.lineRange)) return true;
9454
- const absoluteFile = path30.isAbsolute(finding.file) ? finding.file : path30.join(projectRoot, finding.file).replace(/\\/g, "/");
10121
+ const absoluteFile = path31.isAbsolute(finding.file) ? finding.file : path31.join(projectRoot, finding.file).replace(/\\/g, "/");
9455
10122
  return exclusionSet.isExcluded(absoluteFile, finding.lineRange);
9456
10123
  }
9457
10124
  async function validateWithGraph(crossFileRefs, graph) {
@@ -9509,8 +10176,22 @@ function pickLongest(a, b) {
9509
10176
  if (a && b) return a.length >= b.length ? a : b;
9510
10177
  return a ?? b;
9511
10178
  }
10179
+ function confidenceStrength(f) {
10180
+ if (typeof f.confidence === "number") return f.confidence;
10181
+ if (f.confidence === "high") return 75;
10182
+ if (f.confidence === "medium") return 50;
10183
+ if (f.confidence === "low") return 25;
10184
+ return 0;
10185
+ }
10186
+ function pickPrimary(a, b) {
10187
+ const sevDiff = SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
10188
+ if (sevDiff !== 0) return sevDiff > 0 ? a : b;
10189
+ const confDiff = confidenceStrength(a) - confidenceStrength(b);
10190
+ if (confDiff !== 0) return confDiff > 0 ? a : b;
10191
+ return a;
10192
+ }
9512
10193
  function buildMergedTitle(a, b, domains) {
9513
- const primaryFinding = SEVERITY_RANK[a.severity] >= SEVERITY_RANK[b.severity] ? a : b;
10194
+ const primaryFinding = pickPrimary(a, b);
9514
10195
  const domainList = [...domains].sort().join(", ");
9515
10196
  const cleanTitle = primaryFinding.title.replace(/^\[.*?\]\s*/, "");
9516
10197
  return { title: `[${domainList}] ${cleanTitle}`, primaryFinding };
@@ -9556,6 +10237,7 @@ function mergeFindings(a, b) {
9556
10237
  };
9557
10238
  setIfDefined(merged, "suggestion", pickLongest(a.suggestion, b.suggestion));
9558
10239
  setIfDefined(merged, "trustScore", mergeTrustScore(a, b));
10240
+ setIfDefined(merged, "subagent", primaryFinding.subagent ?? a.subagent ?? b.subagent);
9559
10241
  mergeSecurityFields(merged, primaryFinding, a, b);
9560
10242
  return merged;
9561
10243
  }
@@ -9646,11 +10328,19 @@ function determineAssessment(findings) {
9646
10328
  function getExitCode(assessment) {
9647
10329
  return assessment === "request-changes" ? 1 : 0;
9648
10330
  }
10331
+ function formatConfidence(c) {
10332
+ if (c === void 0) return "";
10333
+ if (typeof c === "number") return ` (conf ${c})`;
10334
+ return ` (conf ${c})`;
10335
+ }
9649
10336
  function formatFindingBlock(finding) {
9650
10337
  const lines = [];
9651
10338
  const location = `${finding.file}:L${finding.lineRange[0]}-${finding.lineRange[1]}`;
9652
10339
  const trustBadge = finding.trustScore != null ? ` [${finding.trustScore}%]` : "";
9653
- lines.push(` [${finding.domain}] ${finding.title}${trustBadge}`);
10340
+ const subagentBadge = finding.subagent && finding.subagent !== finding.domain ? `:${finding.subagent}` : "";
10341
+ lines.push(
10342
+ ` [${finding.domain}${subagentBadge}] ${finding.title}${formatConfidence(finding.confidence)}${trustBadge}`
10343
+ );
9654
10344
  lines.push(` Location: ${location}`);
9655
10345
  lines.push(` Rationale: ${finding.rationale}`);
9656
10346
  if (finding.suggestion) {
@@ -9658,9 +10348,28 @@ function formatFindingBlock(finding) {
9658
10348
  }
9659
10349
  return lines.join("\n");
9660
10350
  }
10351
+ function titleCase(s) {
10352
+ if (s.length === 0) return s;
10353
+ return s[0].toUpperCase() + s.slice(1);
10354
+ }
10355
+ function formatDepthHeader(calibration) {
10356
+ const overrideTag = calibration.overridden ? " (override)" : "";
10357
+ const signals = calibration.riskSignals.length === 0 ? "none" : calibration.riskSignals.join(", ");
10358
+ const activations = calibration.activations.size === 0 ? "none" : [...calibration.activations].join(", ");
10359
+ return [
10360
+ `## Review Depth: ${titleCase(calibration.depth)}${overrideTag}`,
10361
+ ` Changed lines: ${calibration.changedLines}`,
10362
+ ` Risk signals: ${signals}`,
10363
+ ` Conditional subagents: ${activations}`,
10364
+ ""
10365
+ ].join("\n");
10366
+ }
9661
10367
  function formatTerminalOutput(options) {
9662
10368
  const { findings, strengths } = options;
9663
10369
  const sections = [];
10370
+ if (options.depthCalibration) {
10371
+ sections.push(formatDepthHeader(options.depthCalibration));
10372
+ }
9664
10373
  sections.push("## Strengths\n");
9665
10374
  if (strengths.length === 0) {
9666
10375
  sections.push(" No specific strengths noted.\n");
@@ -10252,6 +10961,22 @@ async function runReviewPipeline(options) {
10252
10961
  if (rubric) {
10253
10962
  contextBundles = contextBundles.map((b) => ({ ...b, rubric }));
10254
10963
  }
10964
+ let depthCalibration;
10965
+ try {
10966
+ depthCalibration = calibrateDepth({
10967
+ diff: diff2,
10968
+ commitMessage,
10969
+ ...flags.depth != null ? { override: flags.depth } : {}
10970
+ });
10971
+ } catch {
10972
+ depthCalibration = {
10973
+ depth: "standard",
10974
+ changedLines: diff2.totalDiffLines,
10975
+ riskSignals: [],
10976
+ activations: /* @__PURE__ */ new Set(),
10977
+ overridden: false
10978
+ };
10979
+ }
10255
10980
  let agentResults;
10256
10981
  if (flags.isolated) {
10257
10982
  const specBundles = splitBundlesByStage(contextBundles, "spec-compliance");
@@ -10264,7 +10989,15 @@ async function runReviewPipeline(options) {
10264
10989
  } else {
10265
10990
  agentResults = await fanOutReview({ bundles: contextBundles });
10266
10991
  }
10267
- const rawFindings = agentResults.flatMap((r) => r.findings);
10992
+ const conditionalResults = await fanOutConditionalSubagents({
10993
+ bundles: contextBundles,
10994
+ activations: depthCalibration.activations,
10995
+ depth: depthCalibration.depth
10996
+ });
10997
+ const rawFindings = [
10998
+ ...agentResults.flatMap((r) => r.findings),
10999
+ ...conditionalResults.flatMap((r) => r.findings)
11000
+ ];
10268
11001
  const fileContents = /* @__PURE__ */ new Map();
10269
11002
  for (const [file, content] of diff2.fileDiffs) {
10270
11003
  fileContents.set(file, content);
@@ -10298,7 +11031,8 @@ async function runReviewPipeline(options) {
10298
11031
  const terminalOutput = formatTerminalOutput({
10299
11032
  findings: dedupedFindings,
10300
11033
  strengths,
10301
- ...evidenceCoverage != null ? { evidenceCoverage } : {}
11034
+ ...evidenceCoverage != null ? { evidenceCoverage } : {},
11035
+ depthCalibration
10302
11036
  });
10303
11037
  let githubComments = [];
10304
11038
  if (flags.comment) {
@@ -10313,6 +11047,7 @@ async function runReviewPipeline(options) {
10313
11047
  terminalOutput,
10314
11048
  githubComments,
10315
11049
  exitCode,
11050
+ depthCalibration,
10316
11051
  ...mechanicalResult != null ? { mechanicalResult } : {},
10317
11052
  ...evidenceCoverage != null ? { evidenceCoverage } : {}
10318
11053
  };
@@ -10459,7 +11194,7 @@ function isRegression(from, to) {
10459
11194
  }
10460
11195
  function collectAutopilotStatuses(autopilotPath, featurePlans, allTaskStatuses) {
10461
11196
  try {
10462
- const raw = fs31.readFileSync(autopilotPath, "utf-8");
11197
+ const raw = fs32.readFileSync(autopilotPath, "utf-8");
10463
11198
  const autopilot = JSON.parse(raw);
10464
11199
  if (!autopilot.phases) return;
10465
11200
  const linkedPhases = autopilot.phases.filter(
@@ -10491,10 +11226,10 @@ function inferStatus(feature, projectPath, allFeatures) {
10491
11226
  const featuresWithPlans = allFeatures.filter((f) => f.plans.length > 0);
10492
11227
  const useRootState = featuresWithPlans.length <= 1;
10493
11228
  if (useRootState) {
10494
- const rootStatePath = path31.join(projectPath, ".harness", "state.json");
10495
- if (fs31.existsSync(rootStatePath)) {
11229
+ const rootStatePath = path32.join(projectPath, ".harness", "state.json");
11230
+ if (fs32.existsSync(rootStatePath)) {
10496
11231
  try {
10497
- const raw = fs31.readFileSync(rootStatePath, "utf-8");
11232
+ const raw = fs32.readFileSync(rootStatePath, "utf-8");
10498
11233
  const state = JSON.parse(raw);
10499
11234
  if (state.progress) {
10500
11235
  for (const status of Object.values(state.progress)) {
@@ -10505,14 +11240,14 @@ function inferStatus(feature, projectPath, allFeatures) {
10505
11240
  }
10506
11241
  }
10507
11242
  }
10508
- const sessionsDir = path31.join(projectPath, ".harness", "sessions");
10509
- if (fs31.existsSync(sessionsDir)) {
11243
+ const sessionsDir = path32.join(projectPath, ".harness", "sessions");
11244
+ if (fs32.existsSync(sessionsDir)) {
10510
11245
  try {
10511
- const sessionDirs = fs31.readdirSync(sessionsDir, { withFileTypes: true });
11246
+ const sessionDirs = fs32.readdirSync(sessionsDir, { withFileTypes: true });
10512
11247
  for (const entry of sessionDirs) {
10513
11248
  if (!entry.isDirectory()) continue;
10514
- const autopilotPath = path31.join(sessionsDir, entry.name, "autopilot-state.json");
10515
- if (!fs31.existsSync(autopilotPath)) continue;
11249
+ const autopilotPath = path32.join(sessionsDir, entry.name, "autopilot-state.json");
11250
+ if (!fs32.existsSync(autopilotPath)) continue;
10516
11251
  collectAutopilotStatuses(autopilotPath, feature.plans, allTaskStatuses);
10517
11252
  }
10518
11253
  } catch {
@@ -10627,9 +11362,9 @@ function isValidTrackerShape(tracker) {
10627
11362
  }
10628
11363
  function loadTrackerSyncConfig(projectRoot) {
10629
11364
  try {
10630
- const configPath = path32.join(projectRoot, "harness.config.json");
10631
- if (!fs32.existsSync(configPath)) return null;
10632
- const raw = fs32.readFileSync(configPath, "utf-8");
11365
+ const configPath = path33.join(projectRoot, "harness.config.json");
11366
+ if (!fs33.existsSync(configPath)) return null;
11367
+ const raw = fs33.readFileSync(configPath, "utf-8");
10633
11368
  const config = JSON.parse(raw);
10634
11369
  const tracker = config.roadmap?.tracker;
10635
11370
  if (!isValidTrackerShape(tracker)) return null;
@@ -10749,7 +11484,7 @@ async function fullSync(roadmapPath, adapter2, config, options) {
10749
11484
  });
10750
11485
  await previousSync;
10751
11486
  try {
10752
- const raw = fs33.readFileSync(roadmapPath, "utf-8");
11487
+ const raw = fs34.readFileSync(roadmapPath, "utf-8");
10753
11488
  const parseResult = parseRoadmap(raw);
10754
11489
  if (!parseResult.ok) {
10755
11490
  return {
@@ -10762,7 +11497,7 @@ async function fullSync(roadmapPath, adapter2, config, options) {
10762
11497
  const tickets = fetchResult.ok ? fetchResult.value : void 0;
10763
11498
  const pushResult = await syncToExternal(roadmap, adapter2, config, tickets);
10764
11499
  const pullResult = await syncFromExternal(roadmap, adapter2, config, options);
10765
- fs33.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
11500
+ fs34.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
10766
11501
  return {
10767
11502
  created: pushResult.created,
10768
11503
  updated: pushResult.updated,
@@ -11122,9 +11857,9 @@ function makeTrackerConflictBody(err, opts = {}) {
11122
11857
  }
11123
11858
  function loadProjectRoadmapMode(projectRoot) {
11124
11859
  try {
11125
- const configPath = path33.join(projectRoot, "harness.config.json");
11126
- if (!fs34.existsSync(configPath)) return getRoadmapMode(null);
11127
- const raw = fs34.readFileSync(configPath, "utf-8");
11860
+ const configPath = path34.join(projectRoot, "harness.config.json");
11861
+ if (!fs35.existsSync(configPath)) return getRoadmapMode(null);
11862
+ const raw = fs35.readFileSync(configPath, "utf-8");
11128
11863
  const parsed = JSON.parse(raw);
11129
11864
  return getRoadmapMode(parsed);
11130
11865
  } catch {
@@ -11133,11 +11868,11 @@ function loadProjectRoadmapMode(projectRoot) {
11133
11868
  }
11134
11869
  function loadTrackerClientConfigFromProject(projectRoot) {
11135
11870
  try {
11136
- const configPath = path34.join(projectRoot, "harness.config.json");
11137
- if (!fs35.existsSync(configPath)) {
11871
+ const configPath = path35.join(projectRoot, "harness.config.json");
11872
+ if (!fs36.existsSync(configPath)) {
11138
11873
  return Err(new Error("harness.config.json not found"));
11139
11874
  }
11140
- const cfg = JSON.parse(fs35.readFileSync(configPath, "utf-8"));
11875
+ const cfg = JSON.parse(fs36.readFileSync(configPath, "utf-8"));
11141
11876
  const tracker = cfg.roadmap?.tracker;
11142
11877
  if (!tracker) {
11143
11878
  return Err(
@@ -11352,10 +12087,10 @@ function mapAction(action) {
11352
12087
  }
11353
12088
  async function runMigrationPlan(plan, deps, opts) {
11354
12089
  const projectRoot = opts.projectRoot;
11355
- const roadmapPath = path35.join(projectRoot, "docs", "roadmap.md");
11356
- const archivedPath = path35.join(projectRoot, "docs", "roadmap.md.archived");
11357
- const configPath = path35.join(projectRoot, "harness.config.json");
11358
- const configBackupPath = path35.join(projectRoot, "harness.config.json.pre-migration");
12090
+ const roadmapPath = path36.join(projectRoot, "docs", "roadmap.md");
12091
+ const archivedPath = path36.join(projectRoot, "docs", "roadmap.md.archived");
12092
+ const configPath = path36.join(projectRoot, "harness.config.json");
12093
+ const configBackupPath = path36.join(projectRoot, "harness.config.json.pre-migration");
11359
12094
  const createdSoFar = [];
11360
12095
  const report = {
11361
12096
  created: 0,
@@ -11467,7 +12202,7 @@ async function runMigrationPlan(plan, deps, opts) {
11467
12202
  }
11468
12203
  function getStatePath() {
11469
12204
  const home = process.env["HOME"] || os.homedir();
11470
- return path38.join(home, ".harness", "update-check.json");
12205
+ return path39.join(home, ".harness", "update-check.json");
11471
12206
  }
11472
12207
  function isUpdateCheckEnabled(configInterval) {
11473
12208
  if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
@@ -11480,13 +12215,13 @@ function shouldRunCheck(state, intervalMs) {
11480
12215
  }
11481
12216
  function invalidateCheckState() {
11482
12217
  try {
11483
- fs38.unlinkSync(getStatePath());
12218
+ fs39.unlinkSync(getStatePath());
11484
12219
  } catch {
11485
12220
  }
11486
12221
  }
11487
12222
  function readCheckState() {
11488
12223
  try {
11489
- const raw = fs38.readFileSync(getStatePath(), "utf-8");
12224
+ const raw = fs39.readFileSync(getStatePath(), "utf-8");
11490
12225
  const parsed = JSON.parse(raw);
11491
12226
  if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
11492
12227
  const state = parsed;
@@ -11503,7 +12238,7 @@ function readCheckState() {
11503
12238
  }
11504
12239
  function spawnBackgroundCheck(currentVersion) {
11505
12240
  const statePath = getStatePath();
11506
- const stateDir = path38.dirname(statePath);
12241
+ const stateDir = path39.dirname(statePath);
11507
12242
  const script = `
11508
12243
  const { execSync } = require('child_process');
11509
12244
  const fs = require('fs');
@@ -11746,14 +12481,14 @@ function getModelPrice(model, dataset) {
11746
12481
  return pricing;
11747
12482
  }
11748
12483
  function getCachePath(projectRoot) {
11749
- return path39.join(projectRoot, ".harness", "cache", "pricing.json");
12484
+ return path40.join(projectRoot, ".harness", "cache", "pricing.json");
11750
12485
  }
11751
12486
  function getStalenessMarkerPath(projectRoot) {
11752
- return path39.join(projectRoot, ".harness", "cache", "staleness-marker.json");
12487
+ return path40.join(projectRoot, ".harness", "cache", "staleness-marker.json");
11753
12488
  }
11754
12489
  async function readDiskCache(projectRoot) {
11755
12490
  try {
11756
- const raw = await fs39.readFile(getCachePath(projectRoot), "utf-8");
12491
+ const raw = await fs40.readFile(getCachePath(projectRoot), "utf-8");
11757
12492
  return JSON.parse(raw);
11758
12493
  } catch {
11759
12494
  return null;
@@ -11761,8 +12496,8 @@ async function readDiskCache(projectRoot) {
11761
12496
  }
11762
12497
  async function writeDiskCache(projectRoot, data) {
11763
12498
  const cachePath = getCachePath(projectRoot);
11764
- await fs39.mkdir(path39.dirname(cachePath), { recursive: true });
11765
- await fs39.writeFile(cachePath, JSON.stringify(data, null, 2));
12499
+ await fs40.mkdir(path40.dirname(cachePath), { recursive: true });
12500
+ await fs40.writeFile(cachePath, JSON.stringify(data, null, 2));
11766
12501
  }
11767
12502
  async function fetchFromNetwork() {
11768
12503
  try {
@@ -11789,7 +12524,7 @@ function loadFallbackDataset() {
11789
12524
  async function checkAndWarnStaleness(projectRoot) {
11790
12525
  const markerPath = getStalenessMarkerPath(projectRoot);
11791
12526
  try {
11792
- const raw = await fs39.readFile(markerPath, "utf-8");
12527
+ const raw = await fs40.readFile(markerPath, "utf-8");
11793
12528
  const marker = JSON.parse(raw);
11794
12529
  const firstUse = new Date(marker.firstFallbackUse).getTime();
11795
12530
  const now = Date.now();
@@ -11801,8 +12536,8 @@ async function checkAndWarnStaleness(projectRoot) {
11801
12536
  }
11802
12537
  } catch {
11803
12538
  try {
11804
- await fs39.mkdir(path39.dirname(markerPath), { recursive: true });
11805
- await fs39.writeFile(
12539
+ await fs40.mkdir(path40.dirname(markerPath), { recursive: true });
12540
+ await fs40.writeFile(
11806
12541
  markerPath,
11807
12542
  JSON.stringify({ firstFallbackUse: (/* @__PURE__ */ new Date()).toISOString() })
11808
12543
  );
@@ -11812,7 +12547,7 @@ async function checkAndWarnStaleness(projectRoot) {
11812
12547
  }
11813
12548
  async function clearStalenessMarker(projectRoot) {
11814
12549
  try {
11815
- await fs39.unlink(getStalenessMarkerPath(projectRoot));
12550
+ await fs40.unlink(getStalenessMarkerPath(projectRoot));
11816
12551
  } catch {
11817
12552
  }
11818
12553
  }
@@ -12049,10 +12784,10 @@ function parseLine(line, lineNumber) {
12049
12784
  return record;
12050
12785
  }
12051
12786
  function readCostRecords(projectRoot) {
12052
- const costsFile = path40.join(projectRoot, ".harness", "metrics", "costs.jsonl");
12787
+ const costsFile = path41.join(projectRoot, ".harness", "metrics", "costs.jsonl");
12053
12788
  let raw;
12054
12789
  try {
12055
- raw = fs40.readFileSync(costsFile, "utf-8");
12790
+ raw = fs41.readFileSync(costsFile, "utf-8");
12056
12791
  } catch {
12057
12792
  return [];
12058
12793
  }
@@ -12102,7 +12837,7 @@ function parseCCLine(line, filePath, lineNumber) {
12102
12837
  entry = JSON.parse(line);
12103
12838
  } catch {
12104
12839
  console.warn(
12105
- `[harness usage] Skipping malformed CC JSONL line ${lineNumber} in ${path41.basename(filePath)}`
12840
+ `[harness usage] Skipping malformed CC JSONL line ${lineNumber} in ${path42.basename(filePath)}`
12106
12841
  );
12107
12842
  return null;
12108
12843
  }
@@ -12116,7 +12851,7 @@ function parseCCLine(line, filePath, lineNumber) {
12116
12851
  function readCCFile(filePath) {
12117
12852
  let raw;
12118
12853
  try {
12119
- raw = fs41.readFileSync(filePath, "utf-8");
12854
+ raw = fs42.readFileSync(filePath, "utf-8");
12120
12855
  } catch {
12121
12856
  return [];
12122
12857
  }
@@ -12138,10 +12873,10 @@ function readCCFile(filePath) {
12138
12873
  }
12139
12874
  function parseCCRecords() {
12140
12875
  const homeDir = process.env.HOME ?? os2.homedir();
12141
- const projectsDir = path41.join(homeDir, ".claude", "projects");
12876
+ const projectsDir = path42.join(homeDir, ".claude", "projects");
12142
12877
  let projectDirs;
12143
12878
  try {
12144
- projectDirs = fs41.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path41.join(projectsDir, d.name));
12879
+ projectDirs = fs42.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path42.join(projectsDir, d.name));
12145
12880
  } catch {
12146
12881
  return [];
12147
12882
  }
@@ -12149,7 +12884,7 @@ function parseCCRecords() {
12149
12884
  for (const dir of projectDirs) {
12150
12885
  let files;
12151
12886
  try {
12152
- files = fs41.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path41.join(dir, f));
12887
+ files = fs42.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path42.join(dir, f));
12153
12888
  } catch {
12154
12889
  continue;
12155
12890
  }
@@ -12177,10 +12912,10 @@ function parseLine2(line, lineNumber) {
12177
12912
  }
12178
12913
  }
12179
12914
  function readAdoptionRecords(projectRoot) {
12180
- const adoptionFile = path42.join(projectRoot, ".harness", "metrics", "adoption.jsonl");
12915
+ const adoptionFile = path43.join(projectRoot, ".harness", "metrics", "adoption.jsonl");
12181
12916
  let raw;
12182
12917
  try {
12183
- raw = fs42.readFileSync(adoptionFile, "utf-8");
12918
+ raw = fs43.readFileSync(adoptionFile, "utf-8");
12184
12919
  } catch {
12185
12920
  return [];
12186
12921
  }
@@ -12253,13 +12988,13 @@ function topSkills(records, n) {
12253
12988
  return aggregateBySkill(records).slice(0, n);
12254
12989
  }
12255
12990
  function loadNotificationsConfig(projectRoot) {
12256
- const configPath = path43.join(projectRoot, "harness.config.json");
12257
- if (!fs43.existsSync(configPath)) {
12991
+ const configPath = path44.join(projectRoot, "harness.config.json");
12992
+ if (!fs44.existsSync(configPath)) {
12258
12993
  return Ok({ sinks: [] });
12259
12994
  }
12260
12995
  let raw;
12261
12996
  try {
12262
- raw = fs43.readFileSync(configPath, "utf-8");
12997
+ raw = fs44.readFileSync(configPath, "utf-8");
12263
12998
  } catch (err) {
12264
12999
  return Err(err instanceof Error ? err : new Error(String(err)));
12265
13000
  }
@@ -12360,25 +13095,25 @@ function resolveStability(contentType) {
12360
13095
  return STABILITY_LOOKUP[contentType] ?? "ephemeral";
12361
13096
  }
12362
13097
  function getOrCreateInstallId(projectRoot) {
12363
- const harnessDir = path44.join(projectRoot, ".harness");
12364
- const installIdFile = path44.join(harnessDir, ".install-id");
13098
+ const harnessDir = path45.join(projectRoot, ".harness");
13099
+ const installIdFile = path45.join(harnessDir, ".install-id");
12365
13100
  const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
12366
13101
  try {
12367
- const existing = fs44.readFileSync(installIdFile, "utf-8").trim();
13102
+ const existing = fs45.readFileSync(installIdFile, "utf-8").trim();
12368
13103
  if (UUID_V4_RE.test(existing)) {
12369
13104
  return existing;
12370
13105
  }
12371
13106
  } catch {
12372
13107
  }
12373
13108
  const id = crypto4.randomUUID();
12374
- fs44.mkdirSync(harnessDir, { recursive: true });
12375
- fs44.writeFileSync(installIdFile, id, { encoding: "utf-8", mode: 384 });
13109
+ fs45.mkdirSync(harnessDir, { recursive: true });
13110
+ fs45.writeFileSync(installIdFile, id, { encoding: "utf-8", mode: 384 });
12376
13111
  return id;
12377
13112
  }
12378
13113
  function parseIdentityFromTelemetryFile(filePath) {
12379
13114
  const identity = {};
12380
13115
  try {
12381
- const raw = fs45.readFileSync(filePath, "utf-8");
13116
+ const raw = fs46.readFileSync(filePath, "utf-8");
12382
13117
  const parsed = JSON.parse(raw);
12383
13118
  const src = parsed?.identity;
12384
13119
  if (!src || typeof src !== "object") return identity;
@@ -12391,7 +13126,7 @@ function parseIdentityFromTelemetryFile(filePath) {
12391
13126
  }
12392
13127
  function readProjectNameFallback(configPath) {
12393
13128
  try {
12394
- const raw = fs45.readFileSync(configPath, "utf-8");
13129
+ const raw = fs46.readFileSync(configPath, "utf-8");
12395
13130
  const config = JSON.parse(raw);
12396
13131
  return typeof config?.name === "string" ? config.name : void 0;
12397
13132
  } catch {
@@ -12412,10 +13147,10 @@ function readGitAliasFallback(cwd) {
12412
13147
  }
12413
13148
  }
12414
13149
  function readIdentity(projectRoot) {
12415
- const filePath = path45.join(projectRoot, ".harness", "telemetry.json");
13150
+ const filePath = path46.join(projectRoot, ".harness", "telemetry.json");
12416
13151
  const identity = parseIdentityFromTelemetryFile(filePath);
12417
13152
  if (!identity.project) {
12418
- const fallbackProject = readProjectNameFallback(path45.join(projectRoot, "harness.config.json"));
13153
+ const fallbackProject = readProjectNameFallback(path46.join(projectRoot, "harness.config.json"));
12419
13154
  if (fallbackProject) identity.project = fallbackProject;
12420
13155
  }
12421
13156
  if (!identity.alias) {
@@ -12545,12 +13280,12 @@ function acquireCompoundLock(category, opts = {}) {
12545
13280
  );
12546
13281
  }
12547
13282
  const cwd = opts.cwd ?? process.cwd();
12548
- const lockDir = path46.join(cwd, ".harness", "locks");
12549
- fs46.mkdirSync(lockDir, { recursive: true });
12550
- const lockPath = path46.join(lockDir, `compound-${category}.lock`);
13283
+ const lockDir = path47.join(cwd, ".harness", "locks");
13284
+ fs47.mkdirSync(lockDir, { recursive: true });
13285
+ const lockPath = path47.join(lockDir, `compound-${category}.lock`);
12551
13286
  let fd;
12552
13287
  try {
12553
- fd = fs46.openSync(lockPath, "wx");
13288
+ fd = fs47.openSync(lockPath, "wx");
12554
13289
  } catch (e) {
12555
13290
  const err = e;
12556
13291
  if (err.code === "EEXIST") {
@@ -12559,14 +13294,14 @@ function acquireCompoundLock(category, opts = {}) {
12559
13294
  }
12560
13295
  throw err;
12561
13296
  }
12562
- fs46.writeSync(fd, String(process.pid));
12563
- fs46.closeSync(fd);
13297
+ fs47.writeSync(fd, String(process.pid));
13298
+ fs47.closeSync(fd);
12564
13299
  let released = false;
12565
13300
  const release = () => {
12566
13301
  if (released) return;
12567
13302
  released = true;
12568
13303
  try {
12569
- fs46.unlinkSync(lockPath);
13304
+ fs47.unlinkSync(lockPath);
12570
13305
  } catch {
12571
13306
  }
12572
13307
  process.removeListener("exit", onExit);
@@ -12592,7 +13327,7 @@ function acquireCompoundLock(category, opts = {}) {
12592
13327
  }
12593
13328
  function readHolderPid(lockPath) {
12594
13329
  try {
12595
- const raw = fs46.readFileSync(lockPath, "utf-8").trim();
13330
+ const raw = fs47.readFileSync(lockPath, "utf-8").trim();
12596
13331
  const n = Number.parseInt(raw, 10);
12597
13332
  return Number.isFinite(n) ? n : -1;
12598
13333
  } catch {
@@ -12624,10 +13359,10 @@ function assertSanitized(value) {
12624
13359
  }
12625
13360
  function writePulseConfig(config, opts) {
12626
13361
  PulseConfigSchema.parse(config);
12627
- if (!fs47.existsSync(opts.configPath)) {
13362
+ if (!fs48.existsSync(opts.configPath)) {
12628
13363
  throw new Error(`harness.config.json not found at ${opts.configPath}`);
12629
13364
  }
12630
- const raw = fs47.readFileSync(opts.configPath, "utf-8");
13365
+ const raw = fs48.readFileSync(opts.configPath, "utf-8");
12631
13366
  let parsed;
12632
13367
  try {
12633
13368
  parsed = JSON.parse(raw);
@@ -12635,18 +13370,18 @@ function writePulseConfig(config, opts) {
12635
13370
  throw new Error(`Invalid JSON in ${opts.configPath}: ${e.message}`, { cause: e });
12636
13371
  }
12637
13372
  const bakPath = `${opts.configPath}.bak`;
12638
- if (!opts.skipBackup && !fs47.existsSync(bakPath)) {
12639
- fs47.writeFileSync(bakPath, raw, "utf-8");
13373
+ if (!opts.skipBackup && !fs48.existsSync(bakPath)) {
13374
+ fs48.writeFileSync(bakPath, raw, "utf-8");
12640
13375
  }
12641
13376
  parsed.pulse = config;
12642
13377
  const serialized = JSON.stringify(parsed, null, 2) + "\n";
12643
13378
  const tmpPath = `${opts.configPath}.tmp-${process.pid}`;
12644
- fs47.writeFileSync(tmpPath, serialized, "utf-8");
13379
+ fs48.writeFileSync(tmpPath, serialized, "utf-8");
12645
13380
  try {
12646
- fs47.renameSync(tmpPath, opts.configPath);
13381
+ fs48.renameSync(tmpPath, opts.configPath);
12647
13382
  } catch (e) {
12648
13383
  try {
12649
- fs47.unlinkSync(tmpPath);
13384
+ fs48.unlinkSync(tmpPath);
12650
13385
  } catch {
12651
13386
  }
12652
13387
  throw e;
@@ -12654,12 +13389,12 @@ function writePulseConfig(config, opts) {
12654
13389
  }
12655
13390
  function seedFromStrategy(opts = {}) {
12656
13391
  const cwd = opts.cwd ?? process.cwd();
12657
- const strategyPath = path47.join(cwd, "STRATEGY.md");
13392
+ const strategyPath = path48.join(cwd, "STRATEGY.md");
12658
13393
  const warnings = [];
12659
- if (!fs48.existsSync(strategyPath)) {
13394
+ if (!fs49.existsSync(strategyPath)) {
12660
13395
  return { name: null, keyMetrics: [], warnings: ["STRATEGY.md not found"] };
12661
13396
  }
12662
- const raw = fs48.readFileSync(strategyPath, "utf-8");
13397
+ const raw = fs49.readFileSync(strategyPath, "utf-8");
12663
13398
  let name = null;
12664
13399
  const fmMatch = /^---\s*\n([\s\S]*?)\n---\s*\n/.exec(raw);
12665
13400
  const fm = fmMatch?.[1];
@@ -12811,7 +13546,7 @@ function loadTemplate() {
12811
13546
  const url = import.meta?.url;
12812
13547
  if (!url) return INLINE_TEMPLATE;
12813
13548
  const here = dirname16(fileURLToPath(url));
12814
- return readFileSync392(join612(here, "template.md"), "utf-8");
13549
+ return readFileSync392(join622(here, "template.md"), "utf-8");
12815
13550
  } catch {
12816
13551
  return INLINE_TEMPLATE;
12817
13552
  }
@@ -13000,11 +13735,11 @@ async function computeHotspots(opts) {
13000
13735
  }
13001
13736
  const counts = /* @__PURE__ */ new Map();
13002
13737
  for (const line of stdout.split("\n")) {
13003
- const path50 = line.trim();
13004
- if (path50.length === 0) continue;
13005
- counts.set(path50, (counts.get(path50) ?? 0) + 1);
13738
+ const path52 = line.trim();
13739
+ if (path52.length === 0) continue;
13740
+ counts.set(path52, (counts.get(path52) ?? 0) + 1);
13006
13741
  }
13007
- return [...counts.entries()].filter(([, c]) => c > opts.threshold).map(([path50, churn]) => ({ path: path50, churn })).sort((a, b) => b.churn - a.churn);
13742
+ return [...counts.entries()].filter(([, c]) => c > opts.threshold).map(([path52, churn]) => ({ path: path52, churn })).sort((a, b) => b.churn - a.churn);
13008
13743
  }
13009
13744
  function tokenize(text) {
13010
13745
  return new Set(
@@ -13014,12 +13749,12 @@ function tokenize(text) {
13014
13749
  async function* walk2(dir) {
13015
13750
  let entries;
13016
13751
  try {
13017
- entries = await fs49.readdir(dir, { withFileTypes: true });
13752
+ entries = await fs50.readdir(dir, { withFileTypes: true });
13018
13753
  } catch {
13019
13754
  return;
13020
13755
  }
13021
13756
  for (const e of entries) {
13022
- const p = path48.join(dir, e.name);
13757
+ const p = path49.join(dir, e.name);
13023
13758
  if (e.isDirectory()) yield* walk2(p);
13024
13759
  else if (e.isFile() && e.name.endsWith(".md")) yield p;
13025
13760
  }
@@ -13028,11 +13763,11 @@ async function readDocumentedTokens(solutionsDir) {
13028
13763
  const docs = [];
13029
13764
  for (const [track, categories] of TRACK_CATEGORIES2) {
13030
13765
  for (const category of categories) {
13031
- const dir = path48.join(solutionsDir, track, category);
13766
+ const dir = path49.join(solutionsDir, track, category);
13032
13767
  for await (const file of walk2(dir)) {
13033
- const raw = await fs49.readFile(file, "utf-8");
13768
+ const raw = await fs50.readFile(file, "utf-8");
13034
13769
  const m = /^#\s+(.+)$/m.exec(raw);
13035
- const title = m?.[1] ?? path48.basename(file, ".md");
13770
+ const title = m?.[1] ?? path49.basename(file, ".md");
13036
13771
  docs.push(tokenize(title));
13037
13772
  }
13038
13773
  }
@@ -13095,19 +13830,89 @@ function assembleCandidateReport(input) {
13095
13830
  }
13096
13831
  return lines.join("\n");
13097
13832
  }
13833
+ function emitFrontmatter(doc) {
13834
+ const lines = [
13835
+ "---",
13836
+ `name: ${doc.frontmatter.name}`,
13837
+ `last_updated: "${doc.frontmatter.last_updated}"`,
13838
+ `version: ${doc.frontmatter.version}`,
13839
+ "---"
13840
+ ];
13841
+ return lines.join("\n");
13842
+ }
13843
+ function emitH1(doc, opts) {
13844
+ if (typeof opts.h1 === "string" && opts.h1.trim().length > 0) {
13845
+ return opts.h1.trimEnd();
13846
+ }
13847
+ return `# ${doc.frontmatter.name} Strategy`;
13848
+ }
13849
+ function emitSection(name, body) {
13850
+ const trimmed = body.trim();
13851
+ return trimmed.length === 0 ? `## ${name}
13852
+ ` : `## ${name}
13853
+
13854
+ ${trimmed}
13855
+ `;
13856
+ }
13857
+ function serializeStrategyDoc(doc, opts = {}) {
13858
+ const sectionByName = new Map(doc.sections.map((s) => [s.name, s.body]));
13859
+ const sectionBlocks = [];
13860
+ for (const name of SECTION_ORDER) {
13861
+ const body = sectionByName.get(name);
13862
+ if (body === void 0) continue;
13863
+ sectionBlocks.push(emitSection(name, body));
13864
+ }
13865
+ const blocks = [emitFrontmatter(doc), "", emitH1(doc, opts), "", sectionBlocks.join("\n")];
13866
+ const out = blocks.join("\n");
13867
+ return out.endsWith("\n") ? out : out + "\n";
13868
+ }
13869
+ function readExistingH1(filePath) {
13870
+ if (!fs51.existsSync(filePath)) return void 0;
13871
+ const raw = fs51.readFileSync(filePath, "utf-8");
13872
+ const lines = raw.split("\n").slice(0, 50);
13873
+ for (const line of lines) {
13874
+ if (line.startsWith("# ")) return line.trimEnd();
13875
+ }
13876
+ return void 0;
13877
+ }
13878
+ function writeStrategyDoc(doc, opts) {
13879
+ StrategyDocSchema.parse(doc);
13880
+ const targetPath = path50.join(opts.cwd, FILENAME);
13881
+ const bakPath = `${targetPath}.bak`;
13882
+ const tmpPath = `${targetPath}.tmp-${process.pid}`;
13883
+ const preservedH1 = readExistingH1(targetPath);
13884
+ if (fs51.existsSync(targetPath) && !opts.skipBackup && !fs51.existsSync(bakPath)) {
13885
+ const raw = fs51.readFileSync(targetPath, "utf-8");
13886
+ fs51.writeFileSync(bakPath, raw, "utf-8");
13887
+ }
13888
+ const serialized = serializeStrategyDoc(
13889
+ doc,
13890
+ preservedH1 !== void 0 ? { h1: preservedH1 } : {}
13891
+ );
13892
+ fs51.writeFileSync(tmpPath, serialized, "utf-8");
13893
+ try {
13894
+ fs51.renameSync(tmpPath, targetPath);
13895
+ } catch (e) {
13896
+ try {
13897
+ fs51.unlinkSync(tmpPath);
13898
+ } catch {
13899
+ }
13900
+ throw e;
13901
+ }
13902
+ }
13098
13903
  function proposalsDir(projectPath) {
13099
- return path49.join(projectPath, ".harness", "proposals");
13904
+ return path51.join(projectPath, ".harness", "proposals");
13100
13905
  }
13101
13906
  function proposalPath(projectPath, id) {
13102
- return path49.join(proposalsDir(projectPath), `${id}.json`);
13907
+ return path51.join(proposalsDir(projectPath), `${id}.json`);
13103
13908
  }
13104
13909
  function ensureDir(dir) {
13105
- fs50.mkdirSync(dir, { recursive: true });
13910
+ fs52.mkdirSync(dir, { recursive: true });
13106
13911
  }
13107
13912
  function writeAtomic(filePath, content) {
13108
13913
  const tmpPath = `${filePath}.tmp`;
13109
- fs50.writeFileSync(tmpPath, content);
13110
- fs50.renameSync(tmpPath, filePath);
13914
+ fs52.writeFileSync(tmpPath, content);
13915
+ fs52.renameSync(tmpPath, filePath);
13111
13916
  }
13112
13917
  async function createProposal(projectPath, input) {
13113
13918
  const validated = EmitSkillProposalInputSchema.parse(input);
@@ -13145,7 +13950,7 @@ async function createProposal(projectPath, input) {
13145
13950
  async function getProposal(projectPath, id) {
13146
13951
  const file = proposalPath(projectPath, id);
13147
13952
  try {
13148
- const raw = fs50.readFileSync(file, "utf-8");
13953
+ const raw = fs52.readFileSync(file, "utf-8");
13149
13954
  const parsed = SkillProposalSchema.safeParse(JSON.parse(raw));
13150
13955
  return parsed.success ? parsed.data : null;
13151
13956
  } catch {
@@ -13156,7 +13961,7 @@ async function listProposals(projectPath, opts = {}) {
13156
13961
  const dir = proposalsDir(projectPath);
13157
13962
  let files;
13158
13963
  try {
13159
- files = fs50.readdirSync(dir);
13964
+ files = fs52.readdirSync(dir);
13160
13965
  } catch {
13161
13966
  return [];
13162
13967
  }
@@ -13201,17 +14006,19 @@ function deriveSkillUsage(projectRoot, skillName, windowDays = 30) {
13201
14006
  if (lastUsed) stats.lastUsed = lastUsed;
13202
14007
  return stats;
13203
14008
  }
13204
- var WHATWG_BAD_PORTS, BAD_PORT_SET, VALID_TYPES, CONVENTIONAL_PATTERN, LOOKBACK_PATTERN, PulseDbSourceSchema, PulseSourcesSchema, PulseConfigSchema, BUG_TRACK_CATEGORIES, KNOWLEDGE_TRACK_CATEGORIES, ALL_SOLUTION_CATEGORIES, ISO_DATE, BugTrackSchema, KnowledgeTrackSchema, BaseFrontmatter, SolutionDocFrontmatterSchema, TRACK_CATEGORIES, DEFAULT_AGNIX_TIMEOUT_MS, HARNESS_AGNIX_DISABLE, HARNESS_AGNIX_BIN, CLAUDE_MD_WARN_BYTES, CLAUDE_MD_ERROR_BYTES, CONTEXT_FILES, MIN_DESCRIPTION_LENGTH, SETTINGS_PATHS, KNOWN_HOOK_EVENTS, MCP_SETTINGS_FILES, REQUIRED_SECTIONS, SETTINGS_FILES, KNOWN_AGNIX_TOOLS, KEBAB_CASE, TICKET_ID, DEFAULT_SECTIONS, DEFAULT_RATIOS, NODE_TYPE_TO_CATEGORY, SECTION_LEVEL_MAP, DEFAULT_LEVEL, DEFAULT_BUDGET, DEFAULT_LOADER_CONFIG, LEVEL_FRACTIONS, PHASE_PRIORITIES, ManifestSchema, BundleConstraintsSchema, BundleSchema, ContributionsSchema, LockfilePackageSchema, LockfileSchema, SharableLayerSchema, SharableForbiddenImportSchema, SharableBoundaryConfigSchema, SharableSecurityRulesSchema, VALID_SCOPES, LINE_PATTERN, START_PATTERN, END_PATTERN, readFile62, writeFile32, unlink22, mkdir22, copyFile2, DEFAULT_FIX_CONFIG, EXPORT_TYPE_KEYWORD, ALWAYS_UNSAFE_TYPES, idCounter, DEAD_CODE_FIX_ACTIONS, MustExportRuleSchema, MustExportDefaultRuleSchema, NoExportRuleSchema, MustImportRuleSchema, NoImportRuleSchema, NamingRuleSchema, MaxExportsRuleSchema, MaxLinesRuleSchema, RequireJSDocRuleSchema, RuleSchema, ConfigPatternSchema, PatternConfigSchema, DriftConfigSchema, DeadCodeConfigSchema, EntropyConfigSchema, MAX_TOP_AFFECTED, BaselineManager, BenchmarkRunner, RegressionDetector, SOURCE_EXTENSIONS, FUNCTION_DECL_RE, CONST_DECL_RE, CriticalPathResolver, NoOpTelemetryAdapter, NoOpExecutor, ConsoleSink, feedbackConfig, ChecklistBuilder, AgentActionEmitter, globalEmitter, FileSink, NoOpSink, ConfidenceTierSchema, RegressionResultSchema, DirectionSchema, CategoryForecastSchema, SpecImpactSignalsSchema, SpecImpactEstimateSchema, ContributingFeatureSchema, AdjustedForecastSchema, PredictionWarningSchema, StabilityForecastSchema, PredictionResultSchema, PredictionOptionsSchema, VALID_STATUSES, EM_DASH, VALID_PRIORITIES, ALL_CATEGORIES, DIRECTION_THRESHOLD, PredictionEngine, DEFAULT_COEFFICIENTS, SpecImpactEstimator, EMPTY_HISTORY, ViolationHistoryManager, NORMALIZERS, MAX_SAMPLES, DEFAULT_WINDOW_WEEKS, DEFAULT_MIN_OCCURRENCES, FailureEntrySchema, HandoffSchema, GateCheckSchema, GateResultSchema, GateConfigSchema, HarnessStateSchema, DEFAULT_STATE, StreamInfoSchema, StreamIndexSchema, DEFAULT_STREAM_INDEX, HARNESS_DIR, STATE_FILE, LEARNINGS_FILE, FAILURES_FILE, HANDOFF_FILE, GATE_CONFIG_FILE, INDEX_FILE, SESSIONS_DIR, SESSION_INDEX_FILE, SUMMARY_FILE, SESSION_STATE_FILE, ARCHIVE_DIR, CONTENT_HASHES_FILE, EVENTS_FILE, STREAMS_DIR, STREAM_NAME_REGEX, branchCache, BRANCH_CACHE_TTL_MS, STATE_FILES, MAX_CACHE_ENTRIES, learningsCacheMap, WEIGHTS, LOCK_RETRIES, LOCK_BACKOFFS, PROMOTABLE_OUTCOMES, failuresCacheMap, FAILURE_LINE_REGEX, SAFE_GATE_COMMAND, SkillEventSchema, knownHashesCache, EVENT_FORMATTERS, RuleRegistry, DEFAULT_SECURITY_CONFIG, RuleOverrideSchema, SecurityConfigSchema, secretRules, injectionRules, xssRules, cryptoRules, pathTraversalRules, networkRules, deserializationRules, agentConfigRules, mcpRules, insecureDefaultsRules, sharpEdgesRules, nodeRules, expressRules, reactRules, goRules, SecurityScanner, OSV_ENDPOINT, DEFAULT_CACHE_DIR, DEFAULT_TTL_HOURS, hiddenUnicodePatterns, reRolingPatterns, permissionEscalationPatterns, encodedPayloadPatterns, indirectInjectionPatterns, contextManipulationPatterns, socialEngineeringPatterns, suspiciousPatterns, ALL_PATTERNS, DESTRUCTIVE_BASH, TAINT_DURATION_MS, DEFAULT_SESSION_ID, SecurityCategorySnapshotSchema, SupplyChainSnapshotSchema, SecurityTimelineSnapshotSchema, FindingLifecycleSchema, SecurityTimelineFileSchema, DirectionSchema2, SecurityTrendLineSchema, TrendAttributionSchema, SecurityTrendResultSchema, TimeToFixStatsSchema, TimeToFixResultSchema, EMPTY_SUPPLY_CHAIN, SecurityTimelineManager, ALL_CHECKS, STATUS_ICONS, CINotifier, ExclusionSet, PREFIX_PATTERNS, TEST_FILE_PATTERN, MD_FILE_PATTERN, ALL_DOMAINS, SECURITY_PATTERNS, JS_EXT_FALLBACKS, SEVERITY_RANK, SEVERITY_ORDER, SEVERITY_LABELS, VALIDATED_BY_RANK, VALIDATION_SCORES, DOMAIN_BASELINES, FACTOR_WEIGHTS, EVIDENCE_SATURATION, CORROBORATED_AGREEMENT, STANDALONE_AGREEMENT, AGREEMENT_LINE_GAP, COMPLIANCE_DESCRIPTOR, EXPORT_RE, BUG_DETECTION_DESCRIPTOR, SECURITY_DESCRIPTOR, EVAL_PATTERN, SECRET_PATTERNS, SQL_CONCAT_PATTERN, SHELL_EXEC_PATTERN, ARCHITECTURE_DESCRIPTOR, LARGE_FILE_THRESHOLD, VIOLATION_FILE_RE, LEARNINGS_DESCRIPTOR, RELEVANCE_THRESHOLD, AGENT_DESCRIPTORS, AGENT_RUNNERS, DOWNGRADE_MAP, DEFAULT_PROVIDER_TIERS, SMALL_SUGGESTION_LINE_LIMIT, FILE_LINE_RANGE_PATTERN, FILE_LINE_PATTERN, FILE_ONLY_PATTERN, SECURITY_SENSITIVE_PATH, MIGRATION_PATH, TEST_PATH, CONFIG_PATH, STAGE_DOMAINS, EM_DASH2, STATUS_RANK, RETRY_DEFAULTS, GitHubIssuesSyncAdapter, syncMutex, PRIORITY_RANK, PRIORITY_RANK2, POSITION_WEIGHT, DEPENDENTS_WEIGHT, AFFINITY_WEIGHT, ConflictError, DEFAULTS, GitHubHttp, ETagStore, START_MARKER, END_MARKER, SERIALIZER_FIELDS, GitHubIssuesTrackerAdapter, migrate_exports, HASH_RE, InteractionTypeSchema, QuestionSchema, ConfirmationSchema, TransitionSchema, EmitInteractionInputSchema, ProjectScanner, SHELL_TEMPLATE, STYLES, SCRIPTS, MockLLMService, llmService, ContentPipeline, BlueprintGenerator, TOKENS_PER_MILLION, fallback_default, LITELLM_PRICING_URL, CACHE_TTL_MS, STALENESS_WARNING_DAYS, MICRODOLLARS_PER_DOLLAR, TOKENS_PER_MILLION2, StructuralStrategy, DEFAULT_TOKEN_BUDGET, CHARS_PER_TOKEN, TRUNCATION_MARKER, TruncationStrategy, CompactionPipeline, STABILITY_LOOKUP, TIER_ORDER, AnthropicCacheAdapter, TIER_ORDER2, OpenAICacheAdapter, TIER_ORDER3, CACHED_CONTENT_MARKER, GeminiCacheAdapter, VERSION, POSTHOG_BATCH_URL, MAX_ATTEMPTS, TIMEOUT_MS, DEFAULT_CAPACITY, CacheMetricsRecorder, RETRY_BACKOFFS_MS, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_BATCH_SIZE, defaultFetch, defaultWarn, OTLPExporter, SERVICE_NAME_ATTR, SpanKind, CompoundLockHeldError, KNOWN_CATEGORIES, ALLOWED_FIELD_KEYS, PII_TOKENS, PII_FIELD_DENYLIST, PII_LINE_RE, ALLOWED_SET, PulseAdapterAlreadyRegisteredError, REGISTRY, MOCK_ADAPTER_NAME, ALLOWED_FIELD_SET, adapter, LOOKBACK_RE, MAX_LINES, INLINE_TEMPLATE, TRUNCATION_MARKER2, execFileAsync, FIX_RE, EMPTY_REPO_RE, execFileAsync2, TRACK_CATEGORIES2, STOPWORDS, KEYWORD_TO_CATEGORY, ProposalNotFoundError, ProposalConflictError;
14009
+ var WHATWG_BAD_PORTS, BAD_PORT_SET, VALID_TYPES, CONVENTIONAL_PATTERN, LOOKBACK_PATTERN, PulseDbSourceSchema, PulseSourcesSchema, PulseConfigSchema, KNOWN_SECTION_NAMES, ISO_DATE, PLACEHOLDER_INLINE, REQUIRED, ALLOWED, StrategyFrontmatterSchema, StrategySectionSchema, StrategyDocSchema, BUG_TRACK_CATEGORIES, KNOWLEDGE_TRACK_CATEGORIES, ALL_SOLUTION_CATEGORIES, ISO_DATE2, BugTrackSchema, KnowledgeTrackSchema, BaseFrontmatter, SolutionDocFrontmatterSchema, TRACK_CATEGORIES, DEFAULT_AGNIX_TIMEOUT_MS, HARNESS_AGNIX_DISABLE, HARNESS_AGNIX_BIN, CLAUDE_MD_WARN_BYTES, CLAUDE_MD_ERROR_BYTES, CONTEXT_FILES, MIN_DESCRIPTION_LENGTH, SETTINGS_PATHS, KNOWN_HOOK_EVENTS, MCP_SETTINGS_FILES, REQUIRED_SECTIONS, SETTINGS_FILES, KNOWN_AGNIX_TOOLS, KEBAB_CASE, TICKET_ID, DEFAULT_SECTIONS, DEFAULT_RATIOS, NODE_TYPE_TO_CATEGORY, SECTION_LEVEL_MAP, DEFAULT_LEVEL, DEFAULT_BUDGET, DEFAULT_LOADER_CONFIG, LEVEL_FRACTIONS, PHASE_PRIORITIES, ManifestSchema, BundleConstraintsSchema, BundleSchema, ContributionsSchema, LockfilePackageSchema, LockfileSchema, SharableLayerSchema, SharableForbiddenImportSchema, SharableBoundaryConfigSchema, SharableSecurityRulesSchema, VALID_SCOPES, LINE_PATTERN, START_PATTERN, END_PATTERN, readFile72, writeFile32, unlink22, mkdir22, copyFile2, DEFAULT_FIX_CONFIG, EXPORT_TYPE_KEYWORD, ALWAYS_UNSAFE_TYPES, idCounter, DEAD_CODE_FIX_ACTIONS, MustExportRuleSchema, MustExportDefaultRuleSchema, NoExportRuleSchema, MustImportRuleSchema, NoImportRuleSchema, NamingRuleSchema, MaxExportsRuleSchema, MaxLinesRuleSchema, RequireJSDocRuleSchema, RuleSchema, ConfigPatternSchema, PatternConfigSchema, DriftConfigSchema, DeadCodeConfigSchema, EntropyConfigSchema, MAX_TOP_AFFECTED, BaselineManager, BenchmarkRunner, RegressionDetector, SOURCE_EXTENSIONS, FUNCTION_DECL_RE, CONST_DECL_RE, CriticalPathResolver, NoOpTelemetryAdapter, NoOpExecutor, ConsoleSink, feedbackConfig, ChecklistBuilder, AgentActionEmitter, globalEmitter, FileSink, NoOpSink, ConfidenceTierSchema, RegressionResultSchema, DirectionSchema, CategoryForecastSchema, SpecImpactSignalsSchema, SpecImpactEstimateSchema, ContributingFeatureSchema, AdjustedForecastSchema, PredictionWarningSchema, StabilityForecastSchema, PredictionResultSchema, PredictionOptionsSchema, VALID_STATUSES, EM_DASH, VALID_PRIORITIES, ALL_CATEGORIES, DIRECTION_THRESHOLD, PredictionEngine, DEFAULT_COEFFICIENTS, SpecImpactEstimator, EMPTY_HISTORY, ViolationHistoryManager, NORMALIZERS, MAX_SAMPLES, DEFAULT_WINDOW_WEEKS, DEFAULT_MIN_OCCURRENCES, FailureEntrySchema, HandoffSchema, GateCheckSchema, GateResultSchema, GateConfigSchema, HarnessStateSchema, DEFAULT_STATE, StreamInfoSchema, StreamIndexSchema, DEFAULT_STREAM_INDEX, HARNESS_DIR, STATE_FILE, LEARNINGS_FILE, FAILURES_FILE, HANDOFF_FILE, GATE_CONFIG_FILE, INDEX_FILE, SESSIONS_DIR, SESSION_INDEX_FILE, SUMMARY_FILE, SESSION_STATE_FILE, ARCHIVE_DIR, CONTENT_HASHES_FILE, EVENTS_FILE, STREAMS_DIR, STREAM_NAME_REGEX, branchCache, BRANCH_CACHE_TTL_MS, STATE_FILES, MAX_CACHE_ENTRIES, learningsCacheMap, WEIGHTS, LOCK_RETRIES, LOCK_BACKOFFS, PROMOTABLE_OUTCOMES, failuresCacheMap, FAILURE_LINE_REGEX, SAFE_GATE_COMMAND, SkillEventSchema, knownHashesCache, EVENT_FORMATTERS, RuleRegistry, DEFAULT_SECURITY_CONFIG, RuleOverrideSchema, SecurityConfigSchema, secretRules, injectionRules, xssRules, cryptoRules, pathTraversalRules, networkRules, deserializationRules, agentConfigRules, mcpRules, insecureDefaultsRules, sharpEdgesRules, nodeRules, expressRules, reactRules, goRules, SecurityScanner, OSV_ENDPOINT, DEFAULT_CACHE_DIR, DEFAULT_TTL_HOURS, hiddenUnicodePatterns, reRolingPatterns, permissionEscalationPatterns, encodedPayloadPatterns, indirectInjectionPatterns, contextManipulationPatterns, socialEngineeringPatterns, suspiciousPatterns, ALL_PATTERNS, DESTRUCTIVE_BASH, TAINT_DURATION_MS, DEFAULT_SESSION_ID, SecurityCategorySnapshotSchema, SupplyChainSnapshotSchema, SecurityTimelineSnapshotSchema, FindingLifecycleSchema, SecurityTimelineFileSchema, DirectionSchema2, SecurityTrendLineSchema, TrendAttributionSchema, SecurityTrendResultSchema, TimeToFixStatsSchema, TimeToFixResultSchema, EMPTY_SUPPLY_CHAIN, SecurityTimelineManager, ALL_CHECKS, STATUS_ICONS, CINotifier, ExclusionSet, PREFIX_PATTERNS, TEST_FILE_PATTERN, MD_FILE_PATTERN, ALL_DOMAINS, SECURITY_PATTERNS, JS_EXT_FALLBACKS, SEVERITY_RANK, SEVERITY_ORDER, SEVERITY_LABELS, VALIDATED_BY_RANK, VALIDATION_SCORES, DOMAIN_BASELINES, FACTOR_WEIGHTS, EVIDENCE_SATURATION, CORROBORATED_AGREEMENT, STANDALONE_AGREEMENT, AGREEMENT_LINE_GAP, COMPLIANCE_DESCRIPTOR, EXPORT_RE, BUG_DETECTION_DESCRIPTOR, SECURITY_DESCRIPTOR, EVAL_PATTERN, SECRET_PATTERNS, SQL_CONCAT_PATTERN, SHELL_EXEC_PATTERN, ARCHITECTURE_DESCRIPTOR, LARGE_FILE_THRESHOLD, VIOLATION_FILE_RE, LEARNINGS_DESCRIPTOR, RELEVANCE_THRESHOLD, ADVERSARIAL_DESCRIPTOR, UNVERIFIED_INPUT_PATTERN, PROMISE_NO_REJECT_PATTERN, UNGUARDED_OPTIONAL_CALL, COMPOSE_NO_AWAIT, FETCH_NO_TIMEOUT, TYPESCRIPT_STRICT_DESCRIPTOR, EXPLICIT_ANY_PATTERN, TS_IGNORE_PATTERN, UNSAFE_AS_UNKNOWN, NON_NULL_ASSERTION, VAGUE_HELPER_NAME, FRONTEND_RACES_DESCRIPTOR, AGENT_DESCRIPTORS, CONDITIONAL_SUBAGENT_DESCRIPTORS, AGENT_RUNNERS, SUBAGENT_ORDER, RISK_KEYWORDS, EXCLUDED_PATH_PATTERNS, ASYNC_UI_SIGNALS, WORD_BOUNDARY, DOWNGRADE_MAP, DEFAULT_PROVIDER_TIERS, SMALL_SUGGESTION_LINE_LIMIT, FILE_LINE_RANGE_PATTERN, FILE_LINE_PATTERN, FILE_ONLY_PATTERN, SECURITY_SENSITIVE_PATH, MIGRATION_PATH, TEST_PATH, CONFIG_PATH, STAGE_DOMAINS, EM_DASH2, STATUS_RANK, RETRY_DEFAULTS, GitHubIssuesSyncAdapter, syncMutex, PRIORITY_RANK, PRIORITY_RANK2, POSITION_WEIGHT, DEPENDENTS_WEIGHT, AFFINITY_WEIGHT, ConflictError, DEFAULTS, GitHubHttp, ETagStore, START_MARKER, END_MARKER, SERIALIZER_FIELDS, GitHubIssuesTrackerAdapter, migrate_exports, HASH_RE, InteractionTypeSchema, QuestionSchema, ConfirmationSchema, TransitionSchema, EmitInteractionInputSchema, ProjectScanner, SHELL_TEMPLATE, STYLES, SCRIPTS, MockLLMService, llmService, ContentPipeline, BlueprintGenerator, TOKENS_PER_MILLION, fallback_default, LITELLM_PRICING_URL, CACHE_TTL_MS, STALENESS_WARNING_DAYS, MICRODOLLARS_PER_DOLLAR, TOKENS_PER_MILLION2, StructuralStrategy, DEFAULT_TOKEN_BUDGET, CHARS_PER_TOKEN, TRUNCATION_MARKER, TruncationStrategy, CompactionPipeline, STABILITY_LOOKUP, TIER_ORDER, AnthropicCacheAdapter, TIER_ORDER2, OpenAICacheAdapter, TIER_ORDER3, CACHED_CONTENT_MARKER, GeminiCacheAdapter, VERSION, POSTHOG_BATCH_URL, MAX_ATTEMPTS, TIMEOUT_MS, DEFAULT_CAPACITY, CacheMetricsRecorder, RETRY_BACKOFFS_MS, DEFAULT_FLUSH_INTERVAL_MS, DEFAULT_BATCH_SIZE, defaultFetch, defaultWarn, OTLPExporter, SERVICE_NAME_ATTR, SpanKind, CompoundLockHeldError, KNOWN_CATEGORIES, ALLOWED_FIELD_KEYS, PII_TOKENS, PII_FIELD_DENYLIST, PII_LINE_RE, ALLOWED_SET, PulseAdapterAlreadyRegisteredError, REGISTRY, MOCK_ADAPTER_NAME, ALLOWED_FIELD_SET, adapter, LOOKBACK_RE, MAX_LINES, INLINE_TEMPLATE, TRUNCATION_MARKER2, execFileAsync, FIX_RE, EMPTY_REPO_RE, execFileAsync2, TRACK_CATEGORIES2, STOPWORDS, KEYWORD_TO_CATEGORY, SECTION_ORDER, FILENAME, ProposalNotFoundError, ProposalConflictError;
13205
14010
  var init_dist3 = __esm({
13206
14011
  "../core/dist/index.mjs"() {
13207
14012
  init_chunk_FYTJQ2ZY();
13208
- init_chunk_I2JUTTPH();
14013
+ init_chunk_S4DTDJ6J();
13209
14014
  init_chunk_MUWJHO2S();
13210
14015
  init_chunk_XKLVJIPL();
13211
14016
  init_chunk_IIEDD47I();
13212
14017
  init_chunk_7P6ASYW6();
13213
14018
  init_dist();
13214
14019
  init_dist();
14020
+ init_dist();
14021
+ init_dist();
13215
14022
  init_dist2();
13216
14023
  init_dist();
13217
14024
  init_dist();
@@ -13228,6 +14035,8 @@ var init_dist3 = __esm({
13228
14035
  init_dist();
13229
14036
  init_dist2();
13230
14037
  init_dist();
14038
+ init_dist();
14039
+ init_dist();
13231
14040
  WHATWG_BAD_PORTS = Object.freeze([
13232
14041
  1,
13233
14042
  7,
@@ -13351,6 +14160,67 @@ var init_dist3 = __esm({
13351
14160
  pendingMetrics: z.array(z.string()),
13352
14161
  excludedMetrics: z.array(z.string())
13353
14162
  });
14163
+ KNOWN_SECTION_NAMES = /* @__PURE__ */ new Set([
14164
+ ...REQUIRED_STRATEGY_SECTIONS,
14165
+ ...OPTIONAL_STRATEGY_SECTIONS
14166
+ ]);
14167
+ ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
14168
+ PLACEHOLDER_INLINE = /<[^<>\n]*\s[^<>\n]*>/;
14169
+ REQUIRED = new Set(REQUIRED_STRATEGY_SECTIONS);
14170
+ ALLOWED = /* @__PURE__ */ new Set([
14171
+ ...REQUIRED_STRATEGY_SECTIONS,
14172
+ ...OPTIONAL_STRATEGY_SECTIONS
14173
+ ]);
14174
+ StrategyFrontmatterSchema = z2.object({
14175
+ name: z2.string().min(1, "frontmatter.name must be a non-empty string"),
14176
+ last_updated: z2.string().regex(ISO_DATE, "frontmatter.last_updated must be ISO date YYYY-MM-DD"),
14177
+ version: z2.number().int("frontmatter.version must be an integer").positive("frontmatter.version must be \u2265 1")
14178
+ });
14179
+ StrategySectionSchema = z2.object({
14180
+ name: z2.enum([...REQUIRED_STRATEGY_SECTIONS, ...OPTIONAL_STRATEGY_SECTIONS]),
14181
+ body: z2.string()
14182
+ });
14183
+ StrategyDocSchema = z2.object({
14184
+ frontmatter: StrategyFrontmatterSchema,
14185
+ sections: z2.array(StrategySectionSchema)
14186
+ }).superRefine((doc, ctx) => {
14187
+ const seen = /* @__PURE__ */ new Set();
14188
+ for (const section of doc.sections) {
14189
+ if (!ALLOWED.has(section.name)) {
14190
+ ctx.addIssue({
14191
+ code: z2.ZodIssueCode.custom,
14192
+ message: `unknown section "${section.name}" \u2014 only documented sections are allowed`,
14193
+ path: ["sections"]
14194
+ });
14195
+ continue;
14196
+ }
14197
+ if (seen.has(section.name)) {
14198
+ ctx.addIssue({
14199
+ code: z2.ZodIssueCode.custom,
14200
+ message: `section "${section.name}" appears more than once`,
14201
+ path: ["sections"]
14202
+ });
14203
+ }
14204
+ seen.add(section.name);
14205
+ const issue = bodyContentIssue(section.body);
14206
+ if (issue !== null) {
14207
+ ctx.addIssue({
14208
+ code: z2.ZodIssueCode.custom,
14209
+ message: `section "${section.name}": ${issue}`,
14210
+ path: ["sections", section.name]
14211
+ });
14212
+ }
14213
+ }
14214
+ for (const required of REQUIRED) {
14215
+ if (!seen.has(required)) {
14216
+ ctx.addIssue({
14217
+ code: z2.ZodIssueCode.custom,
14218
+ message: `required section "${required}" is missing`,
14219
+ path: ["sections", required]
14220
+ });
14221
+ }
14222
+ }
14223
+ });
13354
14224
  BUG_TRACK_CATEGORIES = [
13355
14225
  "build-errors",
13356
14226
  "test-failures",
@@ -13374,22 +14244,22 @@ var init_dist3 = __esm({
13374
14244
  ...BUG_TRACK_CATEGORIES,
13375
14245
  ...KNOWLEDGE_TRACK_CATEGORIES
13376
14246
  ];
13377
- ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
13378
- BugTrackSchema = z2.object({
13379
- track: z2.literal("bug-track"),
13380
- category: z2.enum(BUG_TRACK_CATEGORIES)
14247
+ ISO_DATE2 = /^\d{4}-\d{2}-\d{2}$/;
14248
+ BugTrackSchema = z3.object({
14249
+ track: z3.literal("bug-track"),
14250
+ category: z3.enum(BUG_TRACK_CATEGORIES)
13381
14251
  });
13382
- KnowledgeTrackSchema = z2.object({
13383
- track: z2.literal("knowledge-track"),
13384
- category: z2.enum(KNOWLEDGE_TRACK_CATEGORIES)
14252
+ KnowledgeTrackSchema = z3.object({
14253
+ track: z3.literal("knowledge-track"),
14254
+ category: z3.enum(KNOWLEDGE_TRACK_CATEGORIES)
13385
14255
  });
13386
- BaseFrontmatter = z2.object({
13387
- module: z2.string().min(1),
13388
- tags: z2.array(z2.string()),
13389
- problem_type: z2.string().min(1),
13390
- last_updated: z2.string().regex(ISO_DATE, "last_updated must be ISO date YYYY-MM-DD")
14256
+ BaseFrontmatter = z3.object({
14257
+ module: z3.string().min(1),
14258
+ tags: z3.array(z3.string()),
14259
+ problem_type: z3.string().min(1),
14260
+ last_updated: z3.string().regex(ISO_DATE2, "last_updated must be ISO date YYYY-MM-DD")
13391
14261
  });
13392
- SolutionDocFrontmatterSchema = z2.discriminatedUnion("track", [
14262
+ SolutionDocFrontmatterSchema = z3.discriminatedUnion("track", [
13393
14263
  BaseFrontmatter.merge(BugTrackSchema),
13394
14264
  BaseFrontmatter.merge(KnowledgeTrackSchema)
13395
14265
  ]);
@@ -13545,81 +14415,81 @@ var init_dist3 = __esm({
13545
14415
  { category: "config", patterns: ["harness.config.json", "package.json"], priority: 5 }
13546
14416
  ]
13547
14417
  };
13548
- ManifestSchema = z3.object({
13549
- name: z3.string(),
13550
- version: z3.string(),
13551
- description: z3.string().optional(),
13552
- include: z3.array(z3.string()).min(1),
13553
- minHarnessVersion: z3.string().optional(),
13554
- keywords: z3.array(z3.string()).optional().default([]),
13555
- layers: z3.record(z3.array(z3.string())).optional(),
13556
- boundaries: z3.array(
13557
- z3.object({
13558
- name: z3.string(),
13559
- layer: z3.string(),
13560
- direction: z3.enum(["input", "output"]),
13561
- schema: z3.string()
14418
+ ManifestSchema = z4.object({
14419
+ name: z4.string(),
14420
+ version: z4.string(),
14421
+ description: z4.string().optional(),
14422
+ include: z4.array(z4.string()).min(1),
14423
+ minHarnessVersion: z4.string().optional(),
14424
+ keywords: z4.array(z4.string()).optional().default([]),
14425
+ layers: z4.record(z4.array(z4.string())).optional(),
14426
+ boundaries: z4.array(
14427
+ z4.object({
14428
+ name: z4.string(),
14429
+ layer: z4.string(),
14430
+ direction: z4.enum(["input", "output"]),
14431
+ schema: z4.string()
13562
14432
  })
13563
14433
  ).optional()
13564
14434
  });
13565
- BundleConstraintsSchema = z3.object({
13566
- layers: z3.array(
13567
- z3.object({
13568
- name: z3.string(),
13569
- pattern: z3.string(),
13570
- allowedDependencies: z3.array(z3.string())
14435
+ BundleConstraintsSchema = z4.object({
14436
+ layers: z4.array(
14437
+ z4.object({
14438
+ name: z4.string(),
14439
+ pattern: z4.string(),
14440
+ allowedDependencies: z4.array(z4.string())
13571
14441
  })
13572
14442
  ).optional(),
13573
- forbiddenImports: z3.array(
13574
- z3.object({
13575
- from: z3.string(),
13576
- disallow: z3.array(z3.string()),
13577
- message: z3.string().optional()
14443
+ forbiddenImports: z4.array(
14444
+ z4.object({
14445
+ from: z4.string(),
14446
+ disallow: z4.array(z4.string()),
14447
+ message: z4.string().optional()
13578
14448
  })
13579
14449
  ).optional(),
13580
- boundaries: z3.object({
13581
- requireSchema: z3.array(z3.string()).optional()
14450
+ boundaries: z4.object({
14451
+ requireSchema: z4.array(z4.string()).optional()
13582
14452
  }).optional(),
13583
- architecture: z3.object({
13584
- thresholds: z3.record(z3.unknown()).optional(),
13585
- modules: z3.record(z3.record(z3.unknown())).optional()
14453
+ architecture: z4.object({
14454
+ thresholds: z4.record(z4.unknown()).optional(),
14455
+ modules: z4.record(z4.record(z4.unknown())).optional()
13586
14456
  }).optional(),
13587
- security: z3.object({
13588
- rules: z3.record(z3.string()).optional()
14457
+ security: z4.object({
14458
+ rules: z4.record(z4.string()).optional()
13589
14459
  }).optional()
13590
14460
  });
13591
- BundleSchema = z3.object({
13592
- name: z3.string(),
13593
- version: z3.string(),
13594
- description: z3.string().optional(),
13595
- minHarnessVersion: z3.string().optional(),
14461
+ BundleSchema = z4.object({
14462
+ name: z4.string(),
14463
+ version: z4.string(),
14464
+ description: z4.string().optional(),
14465
+ minHarnessVersion: z4.string().optional(),
13596
14466
  manifest: ManifestSchema,
13597
14467
  constraints: BundleConstraintsSchema,
13598
- contributions: z3.record(z3.unknown()).optional()
14468
+ contributions: z4.record(z4.unknown()).optional()
13599
14469
  });
13600
- ContributionsSchema = z3.record(z3.unknown());
13601
- LockfilePackageSchema = z3.object({
13602
- version: z3.string(),
13603
- source: z3.string(),
13604
- installedAt: z3.string(),
13605
- contributions: z3.record(z3.unknown()).optional().nullable(),
13606
- resolved: z3.string().optional(),
13607
- integrity: z3.string().optional(),
13608
- provenance: z3.array(z3.string()).optional()
14470
+ ContributionsSchema = z4.record(z4.unknown());
14471
+ LockfilePackageSchema = z4.object({
14472
+ version: z4.string(),
14473
+ source: z4.string(),
14474
+ installedAt: z4.string(),
14475
+ contributions: z4.record(z4.unknown()).optional().nullable(),
14476
+ resolved: z4.string().optional(),
14477
+ integrity: z4.string().optional(),
14478
+ provenance: z4.array(z4.string()).optional()
13609
14479
  });
13610
- LockfileSchema = z3.object({
13611
- version: z3.literal(1).optional(),
14480
+ LockfileSchema = z4.object({
14481
+ version: z4.literal(1).optional(),
13612
14482
  // Used by some tests
13613
- lockfileVersion: z3.literal(1).optional(),
14483
+ lockfileVersion: z4.literal(1).optional(),
13614
14484
  // Standard field
13615
- packages: z3.record(LockfilePackageSchema)
14485
+ packages: z4.record(LockfilePackageSchema)
13616
14486
  }).refine((data) => data.version !== void 0 || data.lockfileVersion !== void 0, {
13617
14487
  message: "Either 'version' or 'lockfileVersion' must be present and equal to 1"
13618
14488
  });
13619
- SharableLayerSchema = z3.unknown();
13620
- SharableForbiddenImportSchema = z3.unknown();
13621
- SharableBoundaryConfigSchema = z3.unknown();
13622
- SharableSecurityRulesSchema = z3.unknown();
14489
+ SharableLayerSchema = z4.unknown();
14490
+ SharableForbiddenImportSchema = z4.unknown();
14491
+ SharableBoundaryConfigSchema = z4.unknown();
14492
+ SharableSecurityRulesSchema = z4.unknown();
13623
14493
  VALID_SCOPES = /* @__PURE__ */ new Set([
13624
14494
  "entropy",
13625
14495
  "architecture",
@@ -13629,11 +14499,11 @@ var init_dist3 = __esm({
13629
14499
  LINE_PATTERN = /^[ \t]*(?:\/\/|#)\s*harness-ignore(?!\s*(?:SEC-|-start|-end))(?:\s+([\w,]+))?(?::\s*(.+))?$/;
13630
14500
  START_PATTERN = /^[ \t]*(?:\/\/|#)\s*harness-ignore-start(?:\s+([\w,]+))?(?::\s*(.+))?$/;
13631
14501
  END_PATTERN = /^[ \t]*(?:\/\/|#)\s*harness-ignore-end\s*$/;
13632
- readFile62 = promisify(fs6.readFile);
13633
- writeFile32 = promisify(fs6.writeFile);
13634
- unlink22 = promisify(fs6.unlink);
13635
- mkdir22 = promisify(fs6.mkdir);
13636
- copyFile2 = promisify(fs6.copyFile);
14502
+ readFile72 = promisify(fs7.readFile);
14503
+ writeFile32 = promisify(fs7.writeFile);
14504
+ unlink22 = promisify(fs7.unlink);
14505
+ mkdir22 = promisify(fs7.mkdir);
14506
+ copyFile2 = promisify(fs7.copyFile);
13637
14507
  DEFAULT_FIX_CONFIG = {
13638
14508
  dryRun: false,
13639
14509
  fixTypes: ["unused-imports", "dead-files"],
@@ -13661,45 +14531,45 @@ var init_dist3 = __esm({
13661
14531
  "commented-code": "Delete commented block",
13662
14532
  "unused-import": "Remove import"
13663
14533
  };
13664
- MustExportRuleSchema = z4.object({
13665
- type: z4.literal("must-export"),
13666
- names: z4.array(z4.string())
14534
+ MustExportRuleSchema = z5.object({
14535
+ type: z5.literal("must-export"),
14536
+ names: z5.array(z5.string())
13667
14537
  });
13668
- MustExportDefaultRuleSchema = z4.object({
13669
- type: z4.literal("must-export-default"),
13670
- kind: z4.enum(["class", "function", "object"]).optional()
14538
+ MustExportDefaultRuleSchema = z5.object({
14539
+ type: z5.literal("must-export-default"),
14540
+ kind: z5.enum(["class", "function", "object"]).optional()
13671
14541
  });
13672
- NoExportRuleSchema = z4.object({
13673
- type: z4.literal("no-export"),
13674
- names: z4.array(z4.string())
14542
+ NoExportRuleSchema = z5.object({
14543
+ type: z5.literal("no-export"),
14544
+ names: z5.array(z5.string())
13675
14545
  });
13676
- MustImportRuleSchema = z4.object({
13677
- type: z4.literal("must-import"),
13678
- from: z4.string(),
13679
- names: z4.array(z4.string()).optional()
14546
+ MustImportRuleSchema = z5.object({
14547
+ type: z5.literal("must-import"),
14548
+ from: z5.string(),
14549
+ names: z5.array(z5.string()).optional()
13680
14550
  });
13681
- NoImportRuleSchema = z4.object({
13682
- type: z4.literal("no-import"),
13683
- from: z4.string()
14551
+ NoImportRuleSchema = z5.object({
14552
+ type: z5.literal("no-import"),
14553
+ from: z5.string()
13684
14554
  });
13685
- NamingRuleSchema = z4.object({
13686
- type: z4.literal("naming"),
13687
- match: z4.string(),
13688
- convention: z4.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
14555
+ NamingRuleSchema = z5.object({
14556
+ type: z5.literal("naming"),
14557
+ match: z5.string(),
14558
+ convention: z5.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
13689
14559
  });
13690
- MaxExportsRuleSchema = z4.object({
13691
- type: z4.literal("max-exports"),
13692
- count: z4.number().positive()
14560
+ MaxExportsRuleSchema = z5.object({
14561
+ type: z5.literal("max-exports"),
14562
+ count: z5.number().positive()
13693
14563
  });
13694
- MaxLinesRuleSchema = z4.object({
13695
- type: z4.literal("max-lines"),
13696
- count: z4.number().positive()
14564
+ MaxLinesRuleSchema = z5.object({
14565
+ type: z5.literal("max-lines"),
14566
+ count: z5.number().positive()
13697
14567
  });
13698
- RequireJSDocRuleSchema = z4.object({
13699
- type: z4.literal("require-jsdoc"),
13700
- for: z4.array(z4.enum(["function", "class", "export"]))
14568
+ RequireJSDocRuleSchema = z5.object({
14569
+ type: z5.literal("require-jsdoc"),
14570
+ for: z5.array(z5.enum(["function", "class", "export"]))
13701
14571
  });
13702
- RuleSchema = z4.discriminatedUnion("type", [
14572
+ RuleSchema = z5.discriminatedUnion("type", [
13703
14573
  MustExportRuleSchema,
13704
14574
  MustExportDefaultRuleSchema,
13705
14575
  NoExportRuleSchema,
@@ -13710,53 +14580,54 @@ var init_dist3 = __esm({
13710
14580
  MaxLinesRuleSchema,
13711
14581
  RequireJSDocRuleSchema
13712
14582
  ]);
13713
- ConfigPatternSchema = z4.object({
13714
- name: z4.string().min(1),
13715
- description: z4.string(),
13716
- severity: z4.enum(["error", "warning"]),
13717
- files: z4.array(z4.string()),
14583
+ ConfigPatternSchema = z5.object({
14584
+ name: z5.string().min(1),
14585
+ description: z5.string(),
14586
+ severity: z5.enum(["error", "warning"]),
14587
+ files: z5.array(z5.string()),
13718
14588
  rule: RuleSchema,
13719
- message: z4.string().optional()
14589
+ message: z5.string().optional()
13720
14590
  });
13721
- PatternConfigSchema = z4.object({
13722
- patterns: z4.array(ConfigPatternSchema),
13723
- customPatterns: z4.array(z4.any()).optional(),
14591
+ PatternConfigSchema = z5.object({
14592
+ patterns: z5.array(ConfigPatternSchema),
14593
+ customPatterns: z5.array(z5.any()).optional(),
13724
14594
  // Code patterns are functions, can't validate
13725
- ignoreFiles: z4.array(z4.string()).optional()
14595
+ ignoreFiles: z5.array(z5.string()).optional()
13726
14596
  });
13727
- DriftConfigSchema = z4.object({
13728
- docPaths: z4.array(z4.string()).optional(),
13729
- checkApiSignatures: z4.boolean().optional(),
13730
- checkExamples: z4.boolean().optional(),
13731
- checkStructure: z4.boolean().optional(),
13732
- ignorePatterns: z4.array(z4.string()).optional()
14597
+ DriftConfigSchema = z5.object({
14598
+ docPaths: z5.array(z5.string()).optional(),
14599
+ checkApiSignatures: z5.boolean().optional(),
14600
+ checkExamples: z5.boolean().optional(),
14601
+ checkStructure: z5.boolean().optional(),
14602
+ ignorePatterns: z5.array(z5.string()).optional(),
14603
+ forwardLookingPaths: z5.array(z5.string()).optional()
13733
14604
  });
13734
- DeadCodeConfigSchema = z4.object({
13735
- entryPoints: z4.array(z4.string()).optional(),
13736
- includeTypes: z4.boolean().optional(),
13737
- includeInternals: z4.boolean().optional(),
13738
- ignorePatterns: z4.array(z4.string()).optional(),
13739
- treatDynamicImportsAs: z4.enum(["used", "unknown"]).optional()
14605
+ DeadCodeConfigSchema = z5.object({
14606
+ entryPoints: z5.array(z5.string()).optional(),
14607
+ includeTypes: z5.boolean().optional(),
14608
+ includeInternals: z5.boolean().optional(),
14609
+ ignorePatterns: z5.array(z5.string()).optional(),
14610
+ treatDynamicImportsAs: z5.enum(["used", "unknown"]).optional()
13740
14611
  });
13741
- EntropyConfigSchema = z4.object({
13742
- rootDir: z4.string(),
13743
- parser: z4.any().optional(),
14612
+ EntropyConfigSchema = z5.object({
14613
+ rootDir: z5.string(),
14614
+ parser: z5.any().optional(),
13744
14615
  // LanguageParser instance, can't validate
13745
- entryPoints: z4.array(z4.string()).optional(),
13746
- analyze: z4.object({
13747
- drift: z4.union([z4.boolean(), DriftConfigSchema]).optional(),
13748
- deadCode: z4.union([z4.boolean(), DeadCodeConfigSchema]).optional(),
13749
- patterns: z4.union([z4.boolean(), PatternConfigSchema]).optional()
14616
+ entryPoints: z5.array(z5.string()).optional(),
14617
+ analyze: z5.object({
14618
+ drift: z5.union([z5.boolean(), DriftConfigSchema]).optional(),
14619
+ deadCode: z5.union([z5.boolean(), DeadCodeConfigSchema]).optional(),
14620
+ patterns: z5.union([z5.boolean(), PatternConfigSchema]).optional()
13750
14621
  }),
13751
- include: z4.array(z4.string()).optional(),
13752
- exclude: z4.array(z4.string()).optional(),
13753
- docPaths: z4.array(z4.string()).optional()
14622
+ include: z5.array(z5.string()).optional(),
14623
+ exclude: z5.array(z5.string()).optional(),
14624
+ docPaths: z5.array(z5.string()).optional()
13754
14625
  });
13755
14626
  MAX_TOP_AFFECTED = 5;
13756
14627
  BaselineManager = class {
13757
14628
  baselinesPath;
13758
14629
  constructor(projectRoot) {
13759
- this.baselinesPath = join17(projectRoot, ".harness", "perf", "baselines.json");
14630
+ this.baselinesPath = join18(projectRoot, ".harness", "perf", "baselines.json");
13760
14631
  }
13761
14632
  /**
13762
14633
  * Load the baselines file from disk.
@@ -14030,16 +14901,16 @@ var init_dist3 = __esm({
14030
14901
  walkDir(dir, entries) {
14031
14902
  let items;
14032
14903
  try {
14033
- items = fs8.readdirSync(dir, { withFileTypes: true });
14904
+ items = fs9.readdirSync(dir, { withFileTypes: true });
14034
14905
  } catch {
14035
14906
  return;
14036
14907
  }
14037
14908
  for (const item of items) {
14038
14909
  if (item.isDirectory()) {
14039
14910
  if (DEFAULT_SKIP_DIRS.has(item.name)) continue;
14040
- this.walkDir(path5.join(dir, item.name), entries);
14041
- } else if (item.isFile() && SOURCE_EXTENSIONS.has(path5.extname(item.name))) {
14042
- this.scanFile(path5.join(dir, item.name), entries);
14911
+ this.walkDir(path6.join(dir, item.name), entries);
14912
+ } else if (item.isFile() && SOURCE_EXTENSIONS.has(path6.extname(item.name))) {
14913
+ this.scanFile(path6.join(dir, item.name), entries);
14043
14914
  }
14044
14915
  }
14045
14916
  }
@@ -14054,12 +14925,12 @@ var init_dist3 = __esm({
14054
14925
  scanFile(filePath, entries) {
14055
14926
  let content;
14056
14927
  try {
14057
- content = fs8.readFileSync(filePath, "utf-8");
14928
+ content = fs9.readFileSync(filePath, "utf-8");
14058
14929
  } catch {
14059
14930
  return;
14060
14931
  }
14061
14932
  const lines = content.split("\n");
14062
- const relativePath = path5.relative(this.projectRoot, filePath).replace(/\\/g, "/");
14933
+ const relativePath = path6.relative(this.projectRoot, filePath).replace(/\\/g, "/");
14063
14934
  for (let i = 0; i < lines.length; i++) {
14064
14935
  if (!lines[i].includes("@perf-critical")) continue;
14065
14936
  const fnName = this.resolveFunctionName(lines, i + 1);
@@ -14458,80 +15329,80 @@ var init_dist3 = __esm({
14458
15329
  return Ok(void 0);
14459
15330
  }
14460
15331
  };
14461
- ConfidenceTierSchema = z5.enum(["high", "medium", "low"]);
14462
- RegressionResultSchema = z5.object({
14463
- slope: z5.number(),
14464
- intercept: z5.number(),
14465
- rSquared: z5.number().min(0).max(1),
14466
- dataPoints: z5.number().int().min(0)
15332
+ ConfidenceTierSchema = z6.enum(["high", "medium", "low"]);
15333
+ RegressionResultSchema = z6.object({
15334
+ slope: z6.number(),
15335
+ intercept: z6.number(),
15336
+ rSquared: z6.number().min(0).max(1),
15337
+ dataPoints: z6.number().int().min(0)
14467
15338
  });
14468
- DirectionSchema = z5.enum(["improving", "stable", "declining"]);
14469
- CategoryForecastSchema = z5.object({
15339
+ DirectionSchema = z6.enum(["improving", "stable", "declining"]);
15340
+ CategoryForecastSchema = z6.object({
14470
15341
  category: ArchMetricCategorySchema,
14471
- current: z5.number(),
14472
- threshold: z5.number(),
14473
- projectedValue4w: z5.number(),
14474
- projectedValue8w: z5.number(),
14475
- projectedValue12w: z5.number(),
14476
- thresholdCrossingWeeks: z5.number().nullable(),
15342
+ current: z6.number(),
15343
+ threshold: z6.number(),
15344
+ projectedValue4w: z6.number(),
15345
+ projectedValue8w: z6.number(),
15346
+ projectedValue12w: z6.number(),
15347
+ thresholdCrossingWeeks: z6.number().nullable(),
14477
15348
  confidence: ConfidenceTierSchema,
14478
15349
  regression: RegressionResultSchema,
14479
15350
  direction: DirectionSchema
14480
15351
  });
14481
- SpecImpactSignalsSchema = z5.object({
14482
- newFileCount: z5.number().int().min(0),
14483
- affectedLayers: z5.array(z5.string()),
14484
- newDependencies: z5.number().int().min(0),
14485
- phaseCount: z5.number().int().min(0)
15352
+ SpecImpactSignalsSchema = z6.object({
15353
+ newFileCount: z6.number().int().min(0),
15354
+ affectedLayers: z6.array(z6.string()),
15355
+ newDependencies: z6.number().int().min(0),
15356
+ phaseCount: z6.number().int().min(0)
14486
15357
  });
14487
- SpecImpactEstimateSchema = z5.object({
14488
- specPath: z5.string(),
14489
- featureName: z5.string(),
15358
+ SpecImpactEstimateSchema = z6.object({
15359
+ specPath: z6.string(),
15360
+ featureName: z6.string(),
14490
15361
  signals: SpecImpactSignalsSchema,
14491
- deltas: z5.record(ArchMetricCategorySchema, z5.number()).optional()
15362
+ deltas: z6.record(ArchMetricCategorySchema, z6.number()).optional()
14492
15363
  });
14493
- ContributingFeatureSchema = z5.object({
14494
- name: z5.string(),
14495
- specPath: z5.string(),
14496
- delta: z5.number()
15364
+ ContributingFeatureSchema = z6.object({
15365
+ name: z6.string(),
15366
+ specPath: z6.string(),
15367
+ delta: z6.number()
14497
15368
  });
14498
- AdjustedForecastSchema = z5.object({
15369
+ AdjustedForecastSchema = z6.object({
14499
15370
  baseline: CategoryForecastSchema,
14500
15371
  adjusted: CategoryForecastSchema,
14501
- contributingFeatures: z5.array(ContributingFeatureSchema)
15372
+ contributingFeatures: z6.array(ContributingFeatureSchema)
14502
15373
  });
14503
- PredictionWarningSchema = z5.object({
14504
- severity: z5.enum(["critical", "warning", "info"]),
15374
+ PredictionWarningSchema = z6.object({
15375
+ severity: z6.enum(["critical", "warning", "info"]),
14505
15376
  category: ArchMetricCategorySchema,
14506
- message: z5.string(),
14507
- weeksUntil: z5.number(),
15377
+ message: z6.string(),
15378
+ weeksUntil: z6.number(),
14508
15379
  confidence: ConfidenceTierSchema,
14509
- contributingFeatures: z5.array(z5.string())
15380
+ contributingFeatures: z6.array(z6.string())
14510
15381
  });
14511
- StabilityForecastSchema = z5.object({
14512
- current: z5.number(),
14513
- projected4w: z5.number(),
14514
- projected8w: z5.number(),
14515
- projected12w: z5.number(),
15382
+ StabilityForecastSchema = z6.object({
15383
+ current: z6.number(),
15384
+ projected4w: z6.number(),
15385
+ projected8w: z6.number(),
15386
+ projected12w: z6.number(),
14516
15387
  confidence: ConfidenceTierSchema,
14517
15388
  direction: DirectionSchema
14518
15389
  });
14519
- PredictionResultSchema = z5.object({
14520
- generatedAt: z5.string(),
14521
- snapshotsUsed: z5.number().int().min(0),
14522
- timelineRange: z5.object({
14523
- from: z5.string(),
14524
- to: z5.string()
15390
+ PredictionResultSchema = z6.object({
15391
+ generatedAt: z6.string(),
15392
+ snapshotsUsed: z6.number().int().min(0),
15393
+ timelineRange: z6.object({
15394
+ from: z6.string(),
15395
+ to: z6.string()
14525
15396
  }),
14526
15397
  stabilityForecast: StabilityForecastSchema,
14527
- categories: z5.record(ArchMetricCategorySchema, AdjustedForecastSchema),
14528
- warnings: z5.array(PredictionWarningSchema)
15398
+ categories: z6.record(ArchMetricCategorySchema, AdjustedForecastSchema),
15399
+ warnings: z6.array(PredictionWarningSchema)
14529
15400
  });
14530
- PredictionOptionsSchema = z5.object({
14531
- horizon: z5.number().int().min(1).default(12),
14532
- includeRoadmap: z5.boolean().default(true),
14533
- categories: z5.array(ArchMetricCategorySchema).optional(),
14534
- thresholds: z5.record(ArchMetricCategorySchema, z5.number()).optional()
15401
+ PredictionOptionsSchema = z6.object({
15402
+ horizon: z6.number().int().min(1).default(12),
15403
+ includeRoadmap: z6.boolean().default(true),
15404
+ categories: z6.array(ArchMetricCategorySchema).optional(),
15405
+ thresholds: z6.record(ArchMetricCategorySchema, z6.number()).optional()
14535
15406
  });
14536
15407
  VALID_STATUSES = /* @__PURE__ */ new Set([
14537
15408
  "backlog",
@@ -14864,8 +15735,8 @@ var init_dist3 = __esm({
14864
15735
  return null;
14865
15736
  }
14866
15737
  try {
14867
- const roadmapPath = path6.join(this.rootDir, "roadmap.md");
14868
- const raw = fs9.readFileSync(roadmapPath, "utf-8");
15738
+ const roadmapPath = path7.join(this.rootDir, "roadmap.md");
15739
+ const raw = fs10.readFileSync(roadmapPath, "utf-8");
14869
15740
  const parseResult = parseRoadmap(raw);
14870
15741
  if (!parseResult.ok) return null;
14871
15742
  const features = parseResult.value.milestones.flatMap(
@@ -14900,8 +15771,8 @@ var init_dist3 = __esm({
14900
15771
  * @param specPath - Relative path from rootDir to the spec file.
14901
15772
  */
14902
15773
  estimate(specPath) {
14903
- const absolutePath = path7.join(this.rootDir, specPath);
14904
- const content = fs10.readFileSync(absolutePath, "utf-8");
15774
+ const absolutePath = path8.join(this.rootDir, specPath);
15775
+ const content = fs11.readFileSync(absolutePath, "utf-8");
14905
15776
  const newFileCount = this.extractNewFileCount(content);
14906
15777
  const affectedLayers = this.extractAffectedLayers(content);
14907
15778
  const newDependencies = this.extractNewDependencies(content);
@@ -14913,7 +15784,7 @@ var init_dist3 = __esm({
14913
15784
  phaseCount
14914
15785
  );
14915
15786
  const h1Match = content.match(/^#\s+(.+)$/m);
14916
- const featureName = h1Match ? h1Match[1].trim() : path7.basename(specPath, ".md");
15787
+ const featureName = h1Match ? h1Match[1].trim() : path8.basename(specPath, ".md");
14917
15788
  return {
14918
15789
  specPath,
14919
15790
  featureName,
@@ -14934,8 +15805,8 @@ var init_dist3 = __esm({
14934
15805
  const results = [];
14935
15806
  for (const feature of features) {
14936
15807
  if (!feature.spec) continue;
14937
- const absolutePath = path7.join(this.rootDir, feature.spec);
14938
- if (!fs10.existsSync(absolutePath)) continue;
15808
+ const absolutePath = path8.join(this.rootDir, feature.spec);
15809
+ if (!fs11.existsSync(absolutePath)) continue;
14939
15810
  const estimate = this.estimate(feature.spec);
14940
15811
  results.push({ ...estimate, featureName: feature.name });
14941
15812
  }
@@ -14963,8 +15834,8 @@ var init_dist3 = __esm({
14963
15834
  }
14964
15835
  let count = 0;
14965
15836
  for (const fp of filePaths) {
14966
- const absolute = path7.join(this.rootDir, fp);
14967
- if (!fs10.existsSync(absolute)) {
15837
+ const absolute = path8.join(this.rootDir, fp);
15838
+ if (!fs11.existsSync(absolute)) {
14968
15839
  count++;
14969
15840
  }
14970
15841
  }
@@ -15034,8 +15905,8 @@ var init_dist3 = __esm({
15034
15905
  // --- Private: Config Loading ---
15035
15906
  loadLayerNames() {
15036
15907
  try {
15037
- const configPath = path7.join(this.rootDir, "harness.config.json");
15038
- const raw = fs10.readFileSync(configPath, "utf-8");
15908
+ const configPath = path8.join(this.rootDir, "harness.config.json");
15909
+ const raw = fs11.readFileSync(configPath, "utf-8");
15039
15910
  const config = JSON.parse(raw);
15040
15911
  return (config.layers ?? []).map((l) => l.name);
15041
15912
  } catch {
@@ -15054,8 +15925,8 @@ var init_dist3 = __esm({
15054
15925
  historyPath;
15055
15926
  /** Load history from disk, returning empty history if file doesn't exist. */
15056
15927
  load() {
15057
- if (!fs11.existsSync(this.historyPath)) return { ...EMPTY_HISTORY, snapshots: [] };
15058
- const raw = fs11.readFileSync(this.historyPath, "utf-8");
15928
+ if (!fs12.existsSync(this.historyPath)) return { ...EMPTY_HISTORY, snapshots: [] };
15929
+ const raw = fs12.readFileSync(this.historyPath, "utf-8");
15059
15930
  return JSON.parse(raw);
15060
15931
  }
15061
15932
  /** Append a timestamped snapshot of current violations. */
@@ -15069,18 +15940,18 @@ var init_dist3 = __esm({
15069
15940
  }
15070
15941
  /** Prune snapshots older than retentionDays. */
15071
15942
  prune(retentionDays) {
15072
- if (!fs11.existsSync(this.historyPath)) return;
15943
+ if (!fs12.existsSync(this.historyPath)) return;
15073
15944
  const history = this.load();
15074
15945
  const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
15075
15946
  history.snapshots = history.snapshots.filter((s) => new Date(s.timestamp).getTime() >= cutoff);
15076
15947
  this.write(history);
15077
15948
  }
15078
15949
  write(history) {
15079
- const dir = path8.dirname(this.historyPath);
15080
- if (!fs11.existsSync(dir)) {
15081
- fs11.mkdirSync(dir, { recursive: true });
15950
+ const dir = path9.dirname(this.historyPath);
15951
+ if (!fs12.existsSync(dir)) {
15952
+ fs12.mkdirSync(dir, { recursive: true });
15082
15953
  }
15083
- fs11.writeFileSync(this.historyPath, JSON.stringify(history, null, 2), "utf-8");
15954
+ fs12.writeFileSync(this.historyPath, JSON.stringify(history, null, 2), "utf-8");
15084
15955
  }
15085
15956
  };
15086
15957
  NORMALIZERS = {
@@ -15095,81 +15966,81 @@ var init_dist3 = __esm({
15095
15966
  MAX_SAMPLES = 5;
15096
15967
  DEFAULT_WINDOW_WEEKS = 4;
15097
15968
  DEFAULT_MIN_OCCURRENCES = 3;
15098
- FailureEntrySchema = z6.object({
15099
- date: z6.string(),
15100
- skill: z6.string(),
15101
- type: z6.string(),
15102
- description: z6.string()
15969
+ FailureEntrySchema = z7.object({
15970
+ date: z7.string(),
15971
+ skill: z7.string(),
15972
+ type: z7.string(),
15973
+ description: z7.string()
15103
15974
  });
15104
- HandoffSchema = z6.object({
15105
- timestamp: z6.string(),
15106
- fromSkill: z6.string(),
15107
- phase: z6.string(),
15108
- summary: z6.string(),
15109
- completed: z6.array(z6.string()).default([]),
15110
- pending: z6.array(z6.string()).default([]),
15111
- concerns: z6.array(z6.string()).default([]),
15112
- decisions: z6.array(
15113
- z6.object({
15114
- what: z6.string(),
15115
- why: z6.string()
15975
+ HandoffSchema = z7.object({
15976
+ timestamp: z7.string(),
15977
+ fromSkill: z7.string(),
15978
+ phase: z7.string(),
15979
+ summary: z7.string(),
15980
+ completed: z7.array(z7.string()).default([]),
15981
+ pending: z7.array(z7.string()).default([]),
15982
+ concerns: z7.array(z7.string()).default([]),
15983
+ decisions: z7.array(
15984
+ z7.object({
15985
+ what: z7.string(),
15986
+ why: z7.string()
15116
15987
  })
15117
15988
  ).default([]),
15118
- blockers: z6.array(z6.string()).default([]),
15119
- contextKeywords: z6.array(z6.string()).default([]),
15120
- recommendedSkills: z6.object({
15121
- apply: z6.array(z6.string()),
15122
- reference: z6.array(z6.string()),
15123
- consider: z6.array(z6.string()),
15124
- skillsPath: z6.string()
15989
+ blockers: z7.array(z7.string()).default([]),
15990
+ contextKeywords: z7.array(z7.string()).default([]),
15991
+ recommendedSkills: z7.object({
15992
+ apply: z7.array(z7.string()),
15993
+ reference: z7.array(z7.string()),
15994
+ consider: z7.array(z7.string()),
15995
+ skillsPath: z7.string()
15125
15996
  }).optional()
15126
15997
  });
15127
- GateCheckSchema = z6.object({
15128
- name: z6.string(),
15129
- passed: z6.boolean(),
15130
- command: z6.string(),
15131
- output: z6.string().optional(),
15132
- duration: z6.number().optional()
15998
+ GateCheckSchema = z7.object({
15999
+ name: z7.string(),
16000
+ passed: z7.boolean(),
16001
+ command: z7.string(),
16002
+ output: z7.string().optional(),
16003
+ duration: z7.number().optional()
15133
16004
  });
15134
- GateResultSchema = z6.object({
15135
- passed: z6.boolean(),
15136
- checks: z6.array(GateCheckSchema)
16005
+ GateResultSchema = z7.object({
16006
+ passed: z7.boolean(),
16007
+ checks: z7.array(GateCheckSchema)
15137
16008
  });
15138
- GateConfigSchema = z6.object({
15139
- checks: z6.array(
15140
- z6.object({
15141
- name: z6.string(),
15142
- command: z6.string()
16009
+ GateConfigSchema = z7.object({
16010
+ checks: z7.array(
16011
+ z7.object({
16012
+ name: z7.string(),
16013
+ command: z7.string()
15143
16014
  })
15144
16015
  ).optional(),
15145
- trace: z6.boolean().optional()
16016
+ trace: z7.boolean().optional()
15146
16017
  });
15147
- HarnessStateSchema = z6.object({
15148
- schemaVersion: z6.literal(1),
15149
- position: z6.object({
15150
- phase: z6.string().optional(),
15151
- task: z6.string().optional()
16018
+ HarnessStateSchema = z7.object({
16019
+ schemaVersion: z7.literal(1),
16020
+ position: z7.object({
16021
+ phase: z7.string().optional(),
16022
+ task: z7.string().optional()
15152
16023
  }).default({}),
15153
- decisions: z6.array(
15154
- z6.object({
15155
- date: z6.string(),
15156
- decision: z6.string(),
15157
- context: z6.string()
16024
+ decisions: z7.array(
16025
+ z7.object({
16026
+ date: z7.string(),
16027
+ decision: z7.string(),
16028
+ context: z7.string()
15158
16029
  })
15159
16030
  ).default([]),
15160
- blockers: z6.array(
15161
- z6.object({
15162
- id: z6.string(),
15163
- description: z6.string(),
15164
- status: z6.enum(["open", "resolved"])
16031
+ blockers: z7.array(
16032
+ z7.object({
16033
+ id: z7.string(),
16034
+ description: z7.string(),
16035
+ status: z7.enum(["open", "resolved"])
15165
16036
  })
15166
16037
  ).default([]),
15167
- progress: z6.record(z6.enum(["pending", "in_progress", "complete"])).default({}),
15168
- lastSession: z6.object({
15169
- date: z6.string(),
15170
- summary: z6.string(),
15171
- lastSkill: z6.string().optional(),
15172
- pendingTasks: z6.array(z6.string()).optional()
16038
+ progress: z7.record(z7.enum(["pending", "in_progress", "complete"])).default({}),
16039
+ lastSession: z7.object({
16040
+ date: z7.string(),
16041
+ summary: z7.string(),
16042
+ lastSkill: z7.string().optional(),
16043
+ pendingTasks: z7.array(z7.string()).optional()
15173
16044
  }).optional()
15174
16045
  });
15175
16046
  DEFAULT_STATE = {
@@ -15179,16 +16050,16 @@ var init_dist3 = __esm({
15179
16050
  blockers: [],
15180
16051
  progress: {}
15181
16052
  };
15182
- StreamInfoSchema = z7.object({
15183
- name: z7.string(),
15184
- branch: z7.string().optional(),
15185
- createdAt: z7.string(),
15186
- lastActiveAt: z7.string()
16053
+ StreamInfoSchema = z8.object({
16054
+ name: z8.string(),
16055
+ branch: z8.string().optional(),
16056
+ createdAt: z8.string(),
16057
+ lastActiveAt: z8.string()
15187
16058
  });
15188
- StreamIndexSchema = z7.object({
15189
- schemaVersion: z7.literal(1),
15190
- activeStream: z7.string().nullable(),
15191
- streams: z7.record(StreamInfoSchema)
16059
+ StreamIndexSchema = z8.object({
16060
+ schemaVersion: z8.literal(1),
16061
+ activeStream: z8.string().nullable(),
16062
+ streams: z8.record(StreamInfoSchema)
15192
16063
  });
15193
16064
  DEFAULT_STREAM_INDEX = {
15194
16065
  schemaVersion: 1,
@@ -15229,15 +16100,15 @@ var init_dist3 = __esm({
15229
16100
  failuresCacheMap = /* @__PURE__ */ new Map();
15230
16101
  FAILURE_LINE_REGEX = /^- \*\*(\d{4}-\d{2}-\d{2}) \[skill:([^\]]+)\] \[type:([^\]]+)\]:\*\* (.+)$/;
15231
16102
  SAFE_GATE_COMMAND = /^(?:npm|pnpm|yarn)\s+(?:test|run\s+[\w.-]+|run-script\s+[\w.-]+)$|^go\s+(?:test|build|vet|fmt)\s+[\w./ -]+$|^(?:python|python3)\s+-m\s+[\w.-]+$|^make\s+[\w.-]+$|^cargo\s+(?:test|build|check|clippy)(?:\s+[\w./ -]+)?$|^(?:gradle|mvn)\s+[\w:.-]+$/;
15232
- SkillEventSchema = z8.object({
15233
- timestamp: z8.string(),
15234
- skill: z8.string(),
15235
- session: z8.string().optional(),
15236
- type: z8.enum(["phase_transition", "decision", "gate_result", "handoff", "error", "checkpoint"]),
15237
- summary: z8.string(),
15238
- data: z8.record(z8.unknown()).optional(),
15239
- refs: z8.array(z8.string()).optional(),
15240
- contentHash: z8.string().optional()
16103
+ SkillEventSchema = z9.object({
16104
+ timestamp: z9.string(),
16105
+ skill: z9.string(),
16106
+ session: z9.string().optional(),
16107
+ type: z9.enum(["phase_transition", "decision", "gate_result", "handoff", "error", "checkpoint"]),
16108
+ summary: z9.string(),
16109
+ data: z9.record(z9.unknown()).optional(),
16110
+ refs: z9.array(z9.string()).optional(),
16111
+ contentHash: z9.string().optional()
15241
16112
  });
15242
16113
  knownHashesCache = /* @__PURE__ */ new Map();
15243
16114
  EVENT_FORMATTERS = {
@@ -15280,19 +16151,19 @@ var init_dist3 = __esm({
15280
16151
  rules: {},
15281
16152
  exclude: [...skipDirGlobs(), "**/*.test.ts", "**/fixtures/**"]
15282
16153
  };
15283
- RuleOverrideSchema = z9.enum(["off", "error", "warning", "info"]);
15284
- SecurityConfigSchema = z9.object({
15285
- enabled: z9.boolean().default(true),
15286
- strict: z9.boolean().default(false),
15287
- rules: z9.record(z9.string(), RuleOverrideSchema).optional().default({}),
15288
- exclude: z9.array(z9.string()).optional().default([...skipDirGlobs(), "**/*.test.ts", "**/fixtures/**"]),
15289
- external: z9.object({
15290
- semgrep: z9.object({
15291
- enabled: z9.union([z9.literal("auto"), z9.boolean()]).default("auto"),
15292
- rulesets: z9.array(z9.string()).optional()
16154
+ RuleOverrideSchema = z10.enum(["off", "error", "warning", "info"]);
16155
+ SecurityConfigSchema = z10.object({
16156
+ enabled: z10.boolean().default(true),
16157
+ strict: z10.boolean().default(false),
16158
+ rules: z10.record(z10.string(), RuleOverrideSchema).optional().default({}),
16159
+ exclude: z10.array(z10.string()).optional().default([...skipDirGlobs(), "**/*.test.ts", "**/fixtures/**"]),
16160
+ external: z10.object({
16161
+ semgrep: z10.object({
16162
+ enabled: z10.union([z10.literal("auto"), z10.boolean()]).default("auto"),
16163
+ rulesets: z10.array(z10.string()).optional()
15293
16164
  }).optional(),
15294
- gitleaks: z9.object({
15295
- enabled: z9.union([z9.literal("auto"), z9.boolean()]).default("auto")
16165
+ gitleaks: z10.object({
16166
+ enabled: z10.union([z10.literal("auto"), z10.boolean()]).default("auto")
15296
16167
  }).optional()
15297
16168
  }).optional()
15298
16169
  });
@@ -16082,7 +16953,7 @@ var init_dist3 = __esm({
16082
16953
  }
16083
16954
  async scanFile(filePath) {
16084
16955
  if (!this.config.enabled) return [];
16085
- const content = await fs29.readFile(filePath, "utf-8");
16956
+ const content = await fs30.readFile(filePath, "utf-8");
16086
16957
  return this.scanContentForFile(content, filePath, 1);
16087
16958
  }
16088
16959
  scanContentForFile(content, filePath, startLine = 1) {
@@ -16204,7 +17075,7 @@ var init_dist3 = __esm({
16204
17075
  }
16205
17076
  };
16206
17077
  OSV_ENDPOINT = "https://api.osv.dev/v1/query";
16207
- DEFAULT_CACHE_DIR = path26.join(".harness", "cache", "osv");
17078
+ DEFAULT_CACHE_DIR = path27.join(".harness", "cache", "osv");
16208
17079
  DEFAULT_TTL_HOURS = 24;
16209
17080
  hiddenUnicodePatterns = [
16210
17081
  {
@@ -16415,88 +17286,88 @@ var init_dist3 = __esm({
16415
17286
  ];
16416
17287
  TAINT_DURATION_MS = 30 * 60 * 1e3;
16417
17288
  DEFAULT_SESSION_ID = "default";
16418
- SecurityCategorySnapshotSchema = z10.object({
16419
- findingCount: z10.number(),
16420
- errorCount: z10.number(),
16421
- warningCount: z10.number(),
16422
- infoCount: z10.number()
17289
+ SecurityCategorySnapshotSchema = z11.object({
17290
+ findingCount: z11.number(),
17291
+ errorCount: z11.number(),
17292
+ warningCount: z11.number(),
17293
+ infoCount: z11.number()
16423
17294
  });
16424
- SupplyChainSnapshotSchema = z10.object({
16425
- critical: z10.number(),
16426
- high: z10.number(),
16427
- moderate: z10.number(),
16428
- low: z10.number(),
16429
- info: z10.number(),
16430
- total: z10.number()
17295
+ SupplyChainSnapshotSchema = z11.object({
17296
+ critical: z11.number(),
17297
+ high: z11.number(),
17298
+ moderate: z11.number(),
17299
+ low: z11.number(),
17300
+ info: z11.number(),
17301
+ total: z11.number()
16431
17302
  });
16432
- SecurityTimelineSnapshotSchema = z10.object({
16433
- capturedAt: z10.string().datetime(),
16434
- commitHash: z10.string(),
16435
- securityScore: z10.number().min(0).max(100),
16436
- totalFindings: z10.number(),
16437
- bySeverity: z10.object({
16438
- error: z10.number(),
16439
- warning: z10.number(),
16440
- info: z10.number()
17303
+ SecurityTimelineSnapshotSchema = z11.object({
17304
+ capturedAt: z11.string().datetime(),
17305
+ commitHash: z11.string(),
17306
+ securityScore: z11.number().min(0).max(100),
17307
+ totalFindings: z11.number(),
17308
+ bySeverity: z11.object({
17309
+ error: z11.number(),
17310
+ warning: z11.number(),
17311
+ info: z11.number()
16441
17312
  }),
16442
- byCategory: z10.record(z10.string(), SecurityCategorySnapshotSchema),
17313
+ byCategory: z11.record(z11.string(), SecurityCategorySnapshotSchema),
16443
17314
  supplyChain: SupplyChainSnapshotSchema,
16444
- suppressionCount: z10.number(),
16445
- findingIds: z10.array(z10.string())
17315
+ suppressionCount: z11.number(),
17316
+ findingIds: z11.array(z11.string())
16446
17317
  });
16447
- FindingLifecycleSchema = z10.object({
16448
- findingId: z10.string(),
16449
- ruleId: z10.string(),
16450
- category: z10.string(),
16451
- severity: z10.string(),
16452
- file: z10.string(),
16453
- firstSeenAt: z10.string().datetime(),
16454
- firstSeenCommit: z10.string(),
16455
- resolvedAt: z10.string().datetime().nullable(),
16456
- resolvedCommit: z10.string().nullable()
17318
+ FindingLifecycleSchema = z11.object({
17319
+ findingId: z11.string(),
17320
+ ruleId: z11.string(),
17321
+ category: z11.string(),
17322
+ severity: z11.string(),
17323
+ file: z11.string(),
17324
+ firstSeenAt: z11.string().datetime(),
17325
+ firstSeenCommit: z11.string(),
17326
+ resolvedAt: z11.string().datetime().nullable(),
17327
+ resolvedCommit: z11.string().nullable()
16457
17328
  });
16458
- SecurityTimelineFileSchema = z10.object({
16459
- version: z10.literal(1),
16460
- snapshots: z10.array(SecurityTimelineSnapshotSchema),
16461
- findingLifecycles: z10.array(FindingLifecycleSchema)
17329
+ SecurityTimelineFileSchema = z11.object({
17330
+ version: z11.literal(1),
17331
+ snapshots: z11.array(SecurityTimelineSnapshotSchema),
17332
+ findingLifecycles: z11.array(FindingLifecycleSchema)
16462
17333
  });
16463
- DirectionSchema2 = z10.enum(["improving", "stable", "declining"]);
16464
- SecurityTrendLineSchema = z10.object({
16465
- current: z10.number(),
16466
- previous: z10.number(),
16467
- delta: z10.number(),
17334
+ DirectionSchema2 = z11.enum(["improving", "stable", "declining"]);
17335
+ SecurityTrendLineSchema = z11.object({
17336
+ current: z11.number(),
17337
+ previous: z11.number(),
17338
+ delta: z11.number(),
16468
17339
  direction: DirectionSchema2
16469
17340
  });
16470
- TrendAttributionSchema = z10.object({
16471
- category: z10.string(),
16472
- delta: z10.number(),
17341
+ TrendAttributionSchema = z11.object({
17342
+ category: z11.string(),
17343
+ delta: z11.number(),
16473
17344
  direction: DirectionSchema2,
16474
- description: z10.string()
17345
+ description: z11.string()
16475
17346
  });
16476
- SecurityTrendResultSchema = z10.object({
17347
+ SecurityTrendResultSchema = z11.object({
16477
17348
  score: SecurityTrendLineSchema,
16478
17349
  totalFindings: SecurityTrendLineSchema,
16479
- bySeverity: z10.object({
17350
+ bySeverity: z11.object({
16480
17351
  error: SecurityTrendLineSchema,
16481
17352
  warning: SecurityTrendLineSchema,
16482
17353
  info: SecurityTrendLineSchema
16483
17354
  }),
16484
17355
  supplyChain: SecurityTrendLineSchema,
16485
- snapshotCount: z10.number(),
16486
- from: z10.string(),
16487
- to: z10.string(),
16488
- attribution: z10.array(TrendAttributionSchema)
17356
+ snapshotCount: z11.number(),
17357
+ from: z11.string(),
17358
+ to: z11.string(),
17359
+ attribution: z11.array(TrendAttributionSchema)
16489
17360
  });
16490
- TimeToFixStatsSchema = z10.object({
16491
- mean: z10.number(),
16492
- median: z10.number(),
16493
- count: z10.number()
17361
+ TimeToFixStatsSchema = z11.object({
17362
+ mean: z11.number(),
17363
+ median: z11.number(),
17364
+ count: z11.number()
16494
17365
  });
16495
- TimeToFixResultSchema = z10.object({
17366
+ TimeToFixResultSchema = z11.object({
16496
17367
  overall: TimeToFixStatsSchema,
16497
- byCategory: z10.record(z10.string(), TimeToFixStatsSchema),
16498
- openFindings: z10.number(),
16499
- oldestOpenDays: z10.number().nullable()
17368
+ byCategory: z11.record(z11.string(), TimeToFixStatsSchema),
17369
+ openFindings: z11.number(),
17370
+ oldestOpenDays: z11.number().nullable()
16500
17371
  });
16501
17372
  EMPTY_SUPPLY_CHAIN = {
16502
17373
  critical: 0,
@@ -16511,7 +17382,7 @@ var init_dist3 = __esm({
16511
17382
  timelinePath;
16512
17383
  constructor(rootDir) {
16513
17384
  this.rootDir = rootDir;
16514
- this.timelinePath = join39(rootDir, ".harness", "security", "timeline.json");
17385
+ this.timelinePath = join40(rootDir, ".harness", "security", "timeline.json");
16515
17386
  }
16516
17387
  /**
16517
17388
  * Load timeline from disk.
@@ -17133,6 +18004,49 @@ var init_dist3 = __esm({
17133
18004
  ]
17134
18005
  };
17135
18006
  RELEVANCE_THRESHOLD = 0.3;
18007
+ ADVERSARIAL_DESCRIPTOR = {
18008
+ domain: "bug",
18009
+ tier: "strong",
18010
+ displayName: "Adversarial",
18011
+ focusAreas: [
18012
+ "Assumption violation \u2014 hidden invariants the diff assumes but does not enforce",
18013
+ "Composition failures \u2014 two correct pieces that misbehave together",
18014
+ "Abuse cases \u2014 adversarial input shapes the diff does not anticipate",
18015
+ "Cascade chains (Deep only) \u2014 a single failure propagating through downstream callers"
18016
+ ]
18017
+ };
18018
+ UNVERIFIED_INPUT_PATTERN = /\bJSON\.parse\s*\(\s*(?:req\.|request\.|body\.|input\.|params\.|query\.)/;
18019
+ PROMISE_NO_REJECT_PATTERN = /\bnew\s+Promise\s*\(\s*\(?\s*(?:resolve|res)\b\s*(?:\)|,\s*\))/;
18020
+ UNGUARDED_OPTIONAL_CALL = /\b([A-Za-z_$][\w$]*)\?\.\1\s*\(/;
18021
+ COMPOSE_NO_AWAIT = /^\s*[A-Za-z_$][\w$]*\s*\(.*\)\s*\.\s*then\s*\(/;
18022
+ FETCH_NO_TIMEOUT = /\bfetch\s*\(\s*[^,)]+\)/;
18023
+ TYPESCRIPT_STRICT_DESCRIPTOR = {
18024
+ domain: "bug",
18025
+ tier: "standard",
18026
+ displayName: "TypeScript-strict",
18027
+ focusAreas: [
18028
+ "Type holes that disable the checker (any, unknown-as-X assertions, ts-ignore/ts-expect-error)",
18029
+ "Existing-file complexity (large file growth as a Single-Responsibility risk)",
18030
+ "Refactor regression risk \u2014 changed exports without companion test updates",
18031
+ "Five-second rule \u2014 names a reader cannot guess from the call site"
18032
+ ]
18033
+ };
18034
+ EXPLICIT_ANY_PATTERN = /(?<![A-Za-z0-9_$])(?:any|Array\s*<\s*any\s*>)(?![A-Za-z0-9_$])/;
18035
+ TS_IGNORE_PATTERN = /\/\/\s*@?ts-(ignore|expect-error)\b/;
18036
+ UNSAFE_AS_UNKNOWN = /\bas\s+unknown\s+as\s+\w+/;
18037
+ NON_NULL_ASSERTION = /\)!|\]\s*!|[\w$]+!\.(?:[\w$])/;
18038
+ VAGUE_HELPER_NAME = /^\s*(?:export\s+)?(?:async\s+)?function\s+(handle|process|do|stuff|util|helper)\w*\s*\(/i;
18039
+ FRONTEND_RACES_DESCRIPTOR = {
18040
+ domain: "bug",
18041
+ tier: "standard",
18042
+ displayName: "Frontend-races",
18043
+ focusAreas: [
18044
+ "Lifecycle cleanup gaps \u2014 listeners or timers outliving the owner",
18045
+ "Hook timing mistakes \u2014 state set in the wrong hook, async after unmount",
18046
+ "Concurrent interactions \u2014 overlapping clicks/requests, impossible-state booleans",
18047
+ "Stale work \u2014 promises and timers whose callbacks fire after teardown"
18048
+ ]
18049
+ };
17136
18050
  AGENT_DESCRIPTORS = {
17137
18051
  compliance: COMPLIANCE_DESCRIPTOR,
17138
18052
  bug: BUG_DETECTION_DESCRIPTOR,
@@ -17140,6 +18054,11 @@ var init_dist3 = __esm({
17140
18054
  architecture: ARCHITECTURE_DESCRIPTOR,
17141
18055
  learnings: LEARNINGS_DESCRIPTOR
17142
18056
  };
18057
+ CONDITIONAL_SUBAGENT_DESCRIPTORS = {
18058
+ adversarial: ADVERSARIAL_DESCRIPTOR,
18059
+ "typescript-strict": TYPESCRIPT_STRICT_DESCRIPTOR,
18060
+ "frontend-races": FRONTEND_RACES_DESCRIPTOR
18061
+ };
17143
18062
  AGENT_RUNNERS = {
17144
18063
  compliance: runComplianceAgent,
17145
18064
  bug: runBugDetectionAgent,
@@ -17147,6 +18066,57 @@ var init_dist3 = __esm({
17147
18066
  architecture: runArchitectureAgent,
17148
18067
  learnings: runLearningsAgent
17149
18068
  };
18069
+ SUBAGENT_ORDER = [
18070
+ "compliance",
18071
+ "bug",
18072
+ "security",
18073
+ "architecture",
18074
+ "learnings",
18075
+ "adversarial",
18076
+ "typescript-strict",
18077
+ "frontend-races"
18078
+ ];
18079
+ RISK_KEYWORDS = Object.freeze([
18080
+ "auth",
18081
+ "authn",
18082
+ "authz",
18083
+ "password",
18084
+ "token",
18085
+ "payment",
18086
+ "billing",
18087
+ "migration",
18088
+ "migrate",
18089
+ "external API",
18090
+ "webhook",
18091
+ "cryptography",
18092
+ "crypto",
18093
+ "session",
18094
+ "cookie",
18095
+ "personally identifiable",
18096
+ "PII",
18097
+ "compliance"
18098
+ ]);
18099
+ EXCLUDED_PATH_PATTERNS = [
18100
+ /\.test\.(ts|tsx|js|jsx|mjs|cjs)$/,
18101
+ /\.spec\.(ts|tsx|js|jsx|mjs|cjs)$/,
18102
+ /__tests__\//,
18103
+ /(^|\/)dist\//,
18104
+ /(^|\/)build\//,
18105
+ /\.generated\.(ts|tsx|js|jsx)$/,
18106
+ /(^|\/)pnpm-lock\.yaml$/,
18107
+ /(^|\/)package-lock\.json$/,
18108
+ /(^|\/)yarn\.lock$/
18109
+ ];
18110
+ ASYNC_UI_SIGNALS = Object.freeze([
18111
+ ".tsx",
18112
+ "useEffect",
18113
+ "useState",
18114
+ "setTimeout",
18115
+ "setInterval",
18116
+ "addEventListener",
18117
+ "data-controller="
18118
+ ]);
18119
+ WORD_BOUNDARY = /[A-Za-z0-9_]/;
17150
18120
  DOWNGRADE_MAP = {
17151
18121
  critical: "important",
17152
18122
  important: "suggestion",
@@ -18160,28 +19130,28 @@ ${JSON.stringify(event)}`;
18160
19130
  runMigrationPlan: () => runMigrationPlan
18161
19131
  });
18162
19132
  HASH_RE = /<!--\s*harness-history\s+hash:([0-9a-f]{8})\s*-->/i;
18163
- InteractionTypeSchema = z11.enum(["question", "confirmation", "transition"]);
18164
- QuestionSchema = z11.object({
18165
- text: z11.string(),
18166
- options: z11.array(z11.string()).optional(),
18167
- default: z11.string().optional()
19133
+ InteractionTypeSchema = z12.enum(["question", "confirmation", "transition"]);
19134
+ QuestionSchema = z12.object({
19135
+ text: z12.string(),
19136
+ options: z12.array(z12.string()).optional(),
19137
+ default: z12.string().optional()
18168
19138
  });
18169
- ConfirmationSchema = z11.object({
18170
- text: z11.string(),
18171
- context: z11.string()
19139
+ ConfirmationSchema = z12.object({
19140
+ text: z12.string(),
19141
+ context: z12.string()
18172
19142
  });
18173
- TransitionSchema = z11.object({
18174
- completedPhase: z11.string(),
18175
- suggestedNext: z11.string(),
18176
- reason: z11.string(),
18177
- artifacts: z11.array(z11.string()),
18178
- requiresConfirmation: z11.boolean(),
18179
- summary: z11.string()
19143
+ TransitionSchema = z12.object({
19144
+ completedPhase: z12.string(),
19145
+ suggestedNext: z12.string(),
19146
+ reason: z12.string(),
19147
+ artifacts: z12.array(z12.string()),
19148
+ requiresConfirmation: z12.boolean(),
19149
+ summary: z12.string()
18180
19150
  });
18181
- EmitInteractionInputSchema = z11.object({
18182
- path: z11.string(),
19151
+ EmitInteractionInputSchema = z12.object({
19152
+ path: z12.string(),
18183
19153
  type: InteractionTypeSchema,
18184
- stream: z11.string().optional(),
19154
+ stream: z12.string().optional(),
18185
19155
  question: QuestionSchema.optional(),
18186
19156
  confirmation: ConfirmationSchema.optional(),
18187
19157
  transition: TransitionSchema.optional()
@@ -18192,10 +19162,10 @@ ${JSON.stringify(event)}`;
18192
19162
  }
18193
19163
  rootDir;
18194
19164
  async scan() {
18195
- let projectName = path36.basename(this.rootDir);
19165
+ let projectName = path37.basename(this.rootDir);
18196
19166
  try {
18197
- const pkgPath = path36.join(this.rootDir, "package.json");
18198
- const pkgRaw = await fs36.readFile(pkgPath, "utf-8");
19167
+ const pkgPath = path37.join(this.rootDir, "package.json");
19168
+ const pkgRaw = await fs37.readFile(pkgPath, "utf-8");
18199
19169
  const pkg = JSON.parse(pkgRaw);
18200
19170
  if (pkg.name) projectName = pkg.name;
18201
19171
  } catch {
@@ -18308,8 +19278,8 @@ console.log('Blueprint player initialized.');
18308
19278
  styles: STYLES,
18309
19279
  scripts: SCRIPTS
18310
19280
  });
18311
- await fs37.mkdir(options.outputDir, { recursive: true });
18312
- await fs37.writeFile(path37.join(options.outputDir, "index.html"), html);
19281
+ await fs38.mkdir(options.outputDir, { recursive: true });
19282
+ await fs38.writeFile(path38.join(options.outputDir, "index.html"), html);
18313
19283
  }
18314
19284
  };
18315
19285
  TOKENS_PER_MILLION = 1e6;
@@ -18847,6 +19817,11 @@ console.log('Blueprint player initialized.');
18847
19817
  [/\b(runtime|crash|exception|panic)\b/i, "bug-track/runtime-errors"],
18848
19818
  [/\(orchestrator\)|\bintegrat|\blease\b|\brace\b|\bconcurren/i, "bug-track/integration-issues"]
18849
19819
  ];
19820
+ SECTION_ORDER = [
19821
+ ...REQUIRED_STRATEGY_SECTIONS,
19822
+ ...OPTIONAL_STRATEGY_SECTIONS
19823
+ ];
19824
+ FILENAME = "STRATEGY.md";
18850
19825
  ProposalNotFoundError = class extends Error {
18851
19826
  constructor(id) {
18852
19827
  super(`proposal not found: ${id}`);
@@ -18891,6 +19866,11 @@ export {
18891
19866
  PulseSourcesSchema,
18892
19867
  PulseConfigSchema,
18893
19868
  validatePulseConfig,
19869
+ parseStrategyDoc,
19870
+ asStrategyDoc,
19871
+ StrategyFrontmatterSchema,
19872
+ StrategyDocSchema,
19873
+ validateStrategy,
18894
19874
  BUG_TRACK_CATEGORIES,
18895
19875
  KNOWLEDGE_TRACK_CATEGORIES,
18896
19876
  ALL_SOLUTION_CATEGORIES,
@@ -19147,8 +20127,23 @@ export {
19147
20127
  runSecurityAgent,
19148
20128
  ARCHITECTURE_DESCRIPTOR,
19149
20129
  runArchitectureAgent,
20130
+ ADVERSARIAL_DESCRIPTOR,
20131
+ runAdversarialAgent,
20132
+ TYPESCRIPT_STRICT_DESCRIPTOR,
20133
+ runTypescriptStrictAgent,
20134
+ FRONTEND_RACES_DESCRIPTOR,
20135
+ runFrontendRacesAgent,
19150
20136
  AGENT_DESCRIPTORS,
20137
+ CONDITIONAL_SUBAGENT_DESCRIPTORS,
19151
20138
  fanOutReview,
20139
+ fanOutConditionalSubagents,
20140
+ SUBAGENT_ORDER,
20141
+ RISK_KEYWORDS,
20142
+ countChangedLines,
20143
+ detectRiskKeywords,
20144
+ computeDepth,
20145
+ computeActivations,
20146
+ calibrateDepth,
19152
20147
  validateFindings,
19153
20148
  deduplicateFindings,
19154
20149
  checkEligibility,
@@ -19272,6 +20267,8 @@ export {
19272
20267
  crossReferenceUndocumentedFixes,
19273
20268
  suggestCategory,
19274
20269
  assembleCandidateReport,
20270
+ serializeStrategyDoc,
20271
+ writeStrategyDoc,
19275
20272
  proposalsDir,
19276
20273
  ProposalNotFoundError,
19277
20274
  ProposalConflictError,