autosnippet 3.3.0 → 3.3.3

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 (245) hide show
  1. package/dashboard/dist/assets/icons-BJ2mUBi8.js +1 -0
  2. package/dashboard/dist/assets/index-B659K9t5.js +128 -0
  3. package/dashboard/dist/assets/index-NCm40PMD.css +1 -0
  4. package/dashboard/dist/index.html +3 -3
  5. package/dist/bin/cli.d.ts +1 -0
  6. package/dist/bin/cli.js +284 -142
  7. package/dist/lib/agent/context/ExplorationTracker.d.ts +2 -0
  8. package/dist/lib/agent/context/ExplorationTracker.js +21 -3
  9. package/dist/lib/agent/core/ToolExecutionPipeline.d.ts +3 -1
  10. package/dist/lib/agent/core/ToolExecutionPipeline.js +8 -1
  11. package/dist/lib/agent/forge/DynamicComposer.d.ts +58 -0
  12. package/dist/lib/agent/forge/DynamicComposer.js +99 -0
  13. package/dist/lib/agent/forge/SandboxRunner.d.ts +60 -0
  14. package/dist/lib/agent/forge/SandboxRunner.js +251 -0
  15. package/dist/lib/agent/forge/TemporaryToolRegistry.d.ts +76 -0
  16. package/dist/lib/agent/forge/TemporaryToolRegistry.js +154 -0
  17. package/dist/lib/agent/forge/ToolForge.d.ts +92 -0
  18. package/dist/lib/agent/forge/ToolForge.js +239 -0
  19. package/dist/lib/agent/forge/ToolRequirementAnalyzer.d.ts +44 -0
  20. package/dist/lib/agent/forge/ToolRequirementAnalyzer.js +119 -0
  21. package/dist/lib/agent/tools/ToolRegistry.d.ts +2 -0
  22. package/dist/lib/agent/tools/ToolRegistry.js +4 -0
  23. package/dist/lib/agent/tools/composite.js +0 -1
  24. package/dist/lib/agent/tools/index.d.ts +2 -50
  25. package/dist/lib/agent/tools/index.js +2 -3
  26. package/dist/lib/agent/tools/lifecycle.d.ts +1 -58
  27. package/dist/lib/agent/tools/lifecycle.js +2 -75
  28. package/dist/lib/cli/KnowledgeSyncService.d.ts +26 -0
  29. package/dist/lib/cli/KnowledgeSyncService.js +33 -1
  30. package/dist/lib/cli/deploy/FileManifest.d.ts +0 -21
  31. package/dist/lib/cli/deploy/FileManifest.js +0 -11
  32. package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +10 -0
  33. package/dist/lib/domain/knowledge/KnowledgeEntry.js +2 -0
  34. package/dist/lib/domain/knowledge/Lifecycle.d.ts +19 -2
  35. package/dist/lib/domain/knowledge/Lifecycle.js +32 -6
  36. package/dist/lib/domain/knowledge/UnifiedValidator.d.ts +1 -5
  37. package/dist/lib/domain/knowledge/UnifiedValidator.js +7 -44
  38. package/dist/lib/domain/knowledge/values/Stats.d.ts +29 -0
  39. package/dist/lib/domain/knowledge/values/Stats.js +41 -0
  40. package/dist/lib/external/mcp/McpServer.d.ts +19 -38
  41. package/dist/lib/external/mcp/McpServer.js +145 -117
  42. package/dist/lib/external/mcp/autoApproveInjector.js +0 -2
  43. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +26 -1
  44. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +41 -0
  45. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -0
  46. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +3 -0
  47. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +27 -0
  48. package/dist/lib/external/mcp/handlers/bootstrap/skills.js +1 -1
  49. package/dist/lib/external/mcp/handlers/bootstrap-external.js +1 -0
  50. package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
  51. package/dist/lib/external/mcp/handlers/browse.d.ts +1 -0
  52. package/dist/lib/external/mcp/handlers/browse.js +2 -1
  53. package/dist/lib/external/mcp/handlers/consolidated.d.ts +117 -6
  54. package/dist/lib/external/mcp/handlers/consolidated.js +251 -71
  55. package/dist/lib/external/mcp/handlers/guard.d.ts +150 -0
  56. package/dist/lib/external/mcp/handlers/guard.js +239 -5
  57. package/dist/lib/external/mcp/handlers/knowledge.d.ts +0 -29
  58. package/dist/lib/external/mcp/handlers/knowledge.js +1 -76
  59. package/dist/lib/external/mcp/handlers/panorama.d.ts +36 -0
  60. package/dist/lib/external/mcp/handlers/panorama.js +156 -0
  61. package/dist/lib/external/mcp/handlers/system.d.ts +2 -54
  62. package/dist/lib/external/mcp/handlers/system.js +3 -113
  63. package/dist/lib/external/mcp/handlers/task.d.ts +13 -24
  64. package/dist/lib/external/mcp/handlers/task.js +218 -557
  65. package/dist/lib/external/mcp/handlers/types.d.ts +91 -8
  66. package/dist/lib/external/mcp/handlers/types.js +18 -1
  67. package/dist/lib/external/mcp/handlers/wiki-external.d.ts +18 -1
  68. package/dist/lib/external/mcp/handlers/wiki-external.js +16 -1
  69. package/dist/lib/external/mcp/tools.d.ts +18 -24
  70. package/dist/lib/external/mcp/tools.js +132 -159
  71. package/dist/lib/http/HttpServer.js +52 -0
  72. package/dist/lib/http/middleware/validate.js +7 -3
  73. package/dist/lib/http/routes/audit.d.ts +8 -0
  74. package/dist/lib/http/routes/audit.js +51 -0
  75. package/dist/lib/http/routes/guardReport.d.ts +10 -0
  76. package/dist/lib/http/routes/guardReport.js +143 -0
  77. package/dist/lib/http/routes/knowledge.js +32 -1
  78. package/dist/lib/http/routes/panorama.d.ts +11 -0
  79. package/dist/lib/http/routes/panorama.js +322 -0
  80. package/dist/lib/http/routes/signals.d.ts +10 -0
  81. package/dist/lib/http/routes/signals.js +104 -0
  82. package/dist/lib/http/routes/task.d.ts +2 -3
  83. package/dist/lib/http/routes/task.js +17 -347
  84. package/dist/lib/http/routes/violations.js +1 -1
  85. package/dist/lib/infrastructure/audit/AuditLogger.d.ts +6 -1
  86. package/dist/lib/infrastructure/audit/AuditLogger.js +14 -1
  87. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +202 -504
  88. package/dist/lib/infrastructure/database/drizzle/schema.js +38 -69
  89. package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.d.ts +8 -0
  90. package/dist/lib/infrastructure/database/migrations/004_evolution_proposals.js +43 -0
  91. package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.d.ts +9 -0
  92. package/dist/lib/infrastructure/database/migrations/005_recipe_source_refs.js +24 -0
  93. package/dist/lib/infrastructure/logging/Logger.d.ts +2 -0
  94. package/dist/lib/infrastructure/logging/Logger.js +34 -7
  95. package/dist/lib/infrastructure/monitoring/ErrorTracker.js +3 -1
  96. package/dist/lib/infrastructure/monitoring/PerformanceMonitor.d.ts +2 -2
  97. package/dist/lib/infrastructure/monitoring/PerformanceMonitor.js +12 -10
  98. package/dist/lib/infrastructure/notification/LarkNotifier.d.ts +24 -0
  99. package/dist/lib/infrastructure/notification/LarkNotifier.js +97 -0
  100. package/dist/lib/infrastructure/report/ReportStore.d.ts +45 -0
  101. package/dist/lib/infrastructure/report/ReportStore.js +133 -0
  102. package/dist/lib/infrastructure/signal/SignalAggregator.d.ts +18 -0
  103. package/dist/lib/infrastructure/signal/SignalAggregator.js +84 -0
  104. package/dist/lib/infrastructure/signal/SignalBridge.d.ts +13 -0
  105. package/dist/lib/infrastructure/signal/SignalBridge.js +20 -0
  106. package/dist/lib/infrastructure/signal/SignalBus.d.ts +63 -0
  107. package/dist/lib/infrastructure/signal/SignalBus.js +106 -0
  108. package/dist/lib/infrastructure/signal/SignalTraceWriter.d.ts +36 -0
  109. package/dist/lib/infrastructure/signal/SignalTraceWriter.js +130 -0
  110. package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +18 -2
  111. package/dist/lib/injection/ServiceContainer.js +8 -0
  112. package/dist/lib/injection/ServiceMap.d.ts +16 -10
  113. package/dist/lib/injection/modules/AgentModule.d.ts +1 -1
  114. package/dist/lib/injection/modules/AgentModule.js +7 -1
  115. package/dist/lib/injection/modules/AppModule.d.ts +1 -1
  116. package/dist/lib/injection/modules/AppModule.js +4 -13
  117. package/dist/lib/injection/modules/GuardModule.js +27 -2
  118. package/dist/lib/injection/modules/InfraModule.d.ts +0 -1
  119. package/dist/lib/injection/modules/InfraModule.js +9 -7
  120. package/dist/lib/injection/modules/KnowledgeModule.d.ts +5 -0
  121. package/dist/lib/injection/modules/KnowledgeModule.js +131 -0
  122. package/dist/lib/injection/modules/PanoramaModule.d.ts +18 -0
  123. package/dist/lib/injection/modules/PanoramaModule.js +76 -0
  124. package/dist/lib/injection/modules/SignalModule.d.ts +10 -0
  125. package/dist/lib/injection/modules/SignalModule.js +84 -0
  126. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +1 -0
  127. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +6 -0
  128. package/dist/lib/service/bootstrap/BootstrapTaskManager.d.ts +3 -1
  129. package/dist/lib/service/bootstrap/BootstrapTaskManager.js +20 -1
  130. package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +45 -0
  131. package/dist/lib/service/bootstrap/UiStartupTasks.js +101 -0
  132. package/dist/lib/service/delivery/AgentInstructionsGenerator.js +4 -5
  133. package/dist/lib/service/delivery/CursorDeliveryPipeline.d.ts +3 -1
  134. package/dist/lib/service/delivery/CursorDeliveryPipeline.js +13 -10
  135. package/dist/lib/service/delivery/RulesGenerator.js +3 -2
  136. package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +114 -0
  137. package/dist/lib/service/evolution/ConsolidationAdvisor.js +542 -0
  138. package/dist/lib/service/evolution/ContradictionDetector.d.ts +54 -0
  139. package/dist/lib/service/evolution/ContradictionDetector.js +253 -0
  140. package/dist/lib/service/evolution/DecayDetector.d.ts +71 -0
  141. package/dist/lib/service/evolution/DecayDetector.js +244 -0
  142. package/dist/lib/service/evolution/EnhancementSuggester.d.ts +38 -0
  143. package/dist/lib/service/evolution/EnhancementSuggester.js +220 -0
  144. package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +82 -0
  145. package/dist/lib/service/evolution/KnowledgeMetabolism.js +167 -0
  146. package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +53 -0
  147. package/dist/lib/service/evolution/RedundancyAnalyzer.js +210 -0
  148. package/dist/lib/service/evolution/StagingManager.d.ts +57 -0
  149. package/dist/lib/service/evolution/StagingManager.js +201 -0
  150. package/dist/lib/service/guard/ComplianceReporter.d.ts +42 -2
  151. package/dist/lib/service/guard/ComplianceReporter.js +43 -5
  152. package/dist/lib/service/guard/CoverageAnalyzer.d.ts +54 -0
  153. package/dist/lib/service/guard/CoverageAnalyzer.js +149 -0
  154. package/dist/lib/service/guard/GuardCheckEngine.d.ts +42 -0
  155. package/dist/lib/service/guard/GuardCheckEngine.js +465 -14
  156. package/dist/lib/service/guard/GuardFeedbackLoop.d.ts +3 -0
  157. package/dist/lib/service/guard/GuardFeedbackLoop.js +9 -0
  158. package/dist/lib/service/guard/ReverseGuard.d.ts +73 -0
  159. package/dist/lib/service/guard/ReverseGuard.js +256 -0
  160. package/dist/lib/service/guard/RuleLearner.d.ts +12 -0
  161. package/dist/lib/service/guard/RuleLearner.js +38 -0
  162. package/dist/lib/service/guard/UncertaintyCollector.d.ts +83 -0
  163. package/dist/lib/service/guard/UncertaintyCollector.js +149 -0
  164. package/dist/lib/service/guard/ViolationsStore.d.ts +1 -0
  165. package/dist/lib/service/guard/ViolationsStore.js +33 -3
  166. package/dist/lib/service/knowledge/ConfidenceRouter.d.ts +13 -0
  167. package/dist/lib/service/knowledge/ConfidenceRouter.js +14 -0
  168. package/dist/lib/service/knowledge/KnowledgeService.js +22 -4
  169. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +68 -0
  170. package/dist/lib/service/knowledge/SourceRefReconciler.js +309 -0
  171. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +27 -0
  172. package/dist/lib/service/panorama/CouplingAnalyzer.js +192 -0
  173. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +28 -0
  174. package/dist/lib/service/panorama/DimensionAnalyzer.js +320 -0
  175. package/dist/lib/service/panorama/LayerInferrer.d.ts +19 -0
  176. package/dist/lib/service/panorama/LayerInferrer.js +182 -0
  177. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +24 -0
  178. package/dist/lib/service/panorama/ModuleDiscoverer.js +185 -0
  179. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +29 -0
  180. package/dist/lib/service/panorama/PanoramaAggregator.js +228 -0
  181. package/dist/lib/service/panorama/PanoramaScanner.d.ts +52 -0
  182. package/dist/lib/service/panorama/PanoramaScanner.js +188 -0
  183. package/dist/lib/service/panorama/PanoramaService.d.ts +125 -0
  184. package/dist/lib/service/panorama/PanoramaService.js +363 -0
  185. package/dist/lib/service/panorama/PanoramaTypes.d.ts +134 -0
  186. package/dist/lib/service/panorama/PanoramaTypes.js +6 -0
  187. package/dist/lib/service/panorama/RoleRefiner.d.ts +48 -0
  188. package/dist/lib/service/panorama/RoleRefiner.js +535 -0
  189. package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
  190. package/dist/lib/service/search/CoarseRanker.d.ts +7 -6
  191. package/dist/lib/service/search/CoarseRanker.js +11 -10
  192. package/dist/lib/service/search/FieldWeightedScorer.d.ts +81 -0
  193. package/dist/lib/service/search/FieldWeightedScorer.js +318 -0
  194. package/dist/lib/service/search/MultiSignalRanker.d.ts +3 -2
  195. package/dist/lib/service/search/MultiSignalRanker.js +17 -1
  196. package/dist/lib/service/search/SearchEngine.d.ts +9 -7
  197. package/dist/lib/service/search/SearchEngine.js +67 -10
  198. package/dist/lib/service/search/SearchTypes.d.ts +25 -3
  199. package/dist/lib/service/search/SearchTypes.js +6 -1
  200. package/dist/lib/service/signal/HitRecorder.d.ts +68 -0
  201. package/dist/lib/service/signal/HitRecorder.js +173 -0
  202. package/dist/lib/service/skills/SignalCollector.d.ts +3 -1
  203. package/dist/lib/service/skills/SignalCollector.js +31 -1
  204. package/dist/lib/service/task/IntentExtractor.d.ts +66 -0
  205. package/dist/lib/service/task/IntentExtractor.js +256 -0
  206. package/dist/lib/service/task/PrimeSearchPipeline.d.ts +54 -0
  207. package/dist/lib/service/task/PrimeSearchPipeline.js +113 -0
  208. package/dist/lib/service/vector/VectorService.d.ts +3 -0
  209. package/dist/lib/service/vector/VectorService.js +38 -4
  210. package/dist/lib/shared/schemas/mcp-tools.d.ts +41 -96
  211. package/dist/lib/shared/schemas/mcp-tools.js +59 -119
  212. package/dist/scripts/analyze-signals.d.ts +20 -0
  213. package/dist/scripts/analyze-signals.js +155 -0
  214. package/dist/scripts/diagnose-mcp.js +1 -1
  215. package/package.json +1 -1
  216. package/skills/autosnippet-create/SKILL.md +98 -89
  217. package/skills/autosnippet-devdocs/SKILL.md +55 -57
  218. package/templates/claude-code/hooks/autosnippet-session.sh +10 -15
  219. package/templates/cursor-hooks/hooks/session-start.sh +1 -1
  220. package/templates/guard-ci.yml +2 -2
  221. package/templates/instructions/agent-static.md +2 -1
  222. package/templates/instructions/conventions.md +5 -6
  223. package/templates/recipes-setup/README.md +1 -2
  224. package/templates/recipes-setup/_template.md +39 -39
  225. package/dashboard/dist/assets/icons-BofcEZ3f.js +0 -1
  226. package/dashboard/dist/assets/index-D0whuycy.css +0 -1
  227. package/dashboard/dist/assets/index-SiN1GChm.js +0 -128
  228. package/dist/lib/domain/task/Task.d.ts +0 -140
  229. package/dist/lib/domain/task/Task.js +0 -254
  230. package/dist/lib/domain/task/TaskDependency.d.ts +0 -23
  231. package/dist/lib/domain/task/TaskDependency.js +0 -34
  232. package/dist/lib/domain/task/TaskIdGenerator.d.ts +0 -40
  233. package/dist/lib/domain/task/TaskIdGenerator.js +0 -75
  234. package/dist/lib/domain/task/index.d.ts +0 -4
  235. package/dist/lib/domain/task/index.js +0 -4
  236. package/dist/lib/infrastructure/database/migrations/002_add_tasks.d.ts +0 -11
  237. package/dist/lib/infrastructure/database/migrations/002_add_tasks.js +0 -86
  238. package/dist/lib/repository/task/TaskRepository.impl.d.ts +0 -171
  239. package/dist/lib/repository/task/TaskRepository.impl.js +0 -347
  240. package/dist/lib/service/task/TaskGraphService.d.ts +0 -222
  241. package/dist/lib/service/task/TaskGraphService.js +0 -597
  242. package/dist/lib/service/task/TaskKnowledgeBridge.d.ts +0 -95
  243. package/dist/lib/service/task/TaskKnowledgeBridge.js +0 -298
  244. package/dist/lib/service/task/TaskReadyEngine.d.ts +0 -84
  245. package/dist/lib/service/task/TaskReadyEngine.js +0 -115
@@ -10,6 +10,7 @@ import { LanguageService } from '../../shared/LanguageService.js';
10
10
  import { runCodeLevelChecks } from './GuardCodeChecks.js';
11
11
  import { runCrossFileChecks } from './GuardCrossFileChecks.js';
12
12
  import { buildCommentMask, buildTestBlockMask, clearPatternCache, compilePattern, detectLanguage, } from './GuardPatternUtils.js';
13
+ import { UncertaintyCollector } from './UncertaintyCollector.js';
13
14
  /**
14
15
  * 内置默认规则集 — 多语言基础规则
15
16
  *
@@ -80,7 +81,7 @@ const BUILT_IN_RULES = {
80
81
  'dequeueReusableCell.*as\\s*!',
81
82
  'dequeueReusableSupplementaryView.*as\\s*!',
82
83
  'dequeueReusableHeaderFooterView.*as\\s*!',
83
- 'layerClass.*layer\\s+as\\s*!',
84
+ '\\blayer\\s+as\\s*!',
84
85
  ],
85
86
  },
86
87
  'swift-force-try': {
@@ -456,6 +457,11 @@ export class GuardCheckEngine {
456
457
  _epInjected;
457
458
  _externalRules;
458
459
  _guardConfig;
460
+ _signalBus;
461
+ /** 上次 guard 信号指纹,用于去重(相同结果不重复发射) */
462
+ _lastGuardSignalKey;
463
+ _lastBlindSpotSignalKey;
464
+ _uncertaintyCollector;
459
465
  db;
460
466
  logger;
461
467
  constructor(db, options = {}) {
@@ -473,6 +479,10 @@ export class GuardCheckEngine {
473
479
  this._epInjected = false;
474
480
  /** Guard 配置 — 允许禁用特定规则或调整 Code-Level 检查阈值 */
475
481
  this._guardConfig = options.guardConfig || {};
482
+ this._signalBus = options.signalBus || null;
483
+ this._lastGuardSignalKey = '';
484
+ this._lastBlindSpotSignalKey = '';
485
+ this._uncertaintyCollector = new UncertaintyCollector();
476
486
  }
477
487
  /**
478
488
  * 注入 Enhancement Pack 外部规则(支持 RegExp 和 string pattern)
@@ -534,10 +544,10 @@ export class GuardCheckEngine {
534
544
  let rows = [];
535
545
  try {
536
546
  rows = this.db
537
- .prepare(`SELECT id, title, description, language, scope, constraints
547
+ .prepare(`SELECT id, title, description, language, scope, constraints, lifecycle
538
548
  FROM knowledge_entries
539
549
  WHERE (kind = 'rule' OR knowledgeType = 'boundary-constraint')
540
- AND lifecycle = 'active'`)
550
+ AND lifecycle IN ('active', 'staging', 'evolving', 'decaying')`)
541
551
  .all();
542
552
  }
543
553
  catch {
@@ -557,12 +567,14 @@ export class GuardCheckEngine {
557
567
  for (const g of guards) {
558
568
  const ruleType = g.type || 'regex';
559
569
  const lang = r.language;
570
+ const isDecaying = r.lifecycle === 'decaying';
571
+ const rawSeverity = (g.severity || 'warning');
560
572
  const base = {
561
573
  id: (g.id || r.id),
562
574
  name: (g.name || r.title),
563
575
  message: (g.message || r.description || r.title),
564
576
  languages: lang ? [lang, LanguageService.toGuardLangId(lang)] : [],
565
- severity: (g.severity || 'warning'),
577
+ severity: isDecaying && rawSeverity === 'error' ? 'warning' : rawSeverity,
566
578
  dimension: (r.scope || 'file'),
567
579
  source: 'database',
568
580
  fixSuggestion: (g.fixSuggestion || null),
@@ -607,6 +619,7 @@ export class GuardCheckEngine {
607
619
  ...(rule.excludePaths ? { excludePaths: rule.excludePaths } : {}),
608
620
  ...(rule.skipComments ? { skipComments: true } : {}),
609
621
  ...(rule.skipTestBlocks ? { skipTestBlocks: true } : {}),
622
+ ...(rule.excludeLinePatterns ? { excludeLinePatterns: rule.excludeLinePatterns } : {}),
610
623
  });
611
624
  }
612
625
  }
@@ -692,6 +705,8 @@ export class GuardCheckEngine {
692
705
  }
693
706
  catch {
694
707
  this.logger.debug(`Invalid regex in rule ${rule.id}: ${rule.pattern}`);
708
+ this._uncertaintyCollector.recordSkip('regex', 'invalid_regex', `Rule ${rule.id}: pattern "${rule.pattern}" failed to compile`, { ruleId: rule.id || rule.name });
709
+ this._uncertaintyCollector.addUncertain(rule.id || rule.name, rule.message, 'regex', 'invalid_regex', `Pattern compilation failed: ${rule.pattern}`);
695
710
  continue;
696
711
  }
697
712
  const shouldSkipComments = !!rule.skipComments;
@@ -736,8 +751,10 @@ export class GuardCheckEngine {
736
751
  disabledRules: this._guardConfig.disabledRules,
737
752
  codeLevelThresholds: numericThresholds,
738
753
  }));
739
- // AST 语义规则检查
754
+ // AST 语义规则检查(Layer 1: 3 查询函数)
740
755
  violations.push(...this._runAstRuleChecks(code, language));
756
+ // AST Layer 2: analyzeFile() 深层检查(复杂度、类膨胀、深嵌套)
757
+ violations.push(...this._runAstLayer2Checks(code, language, filePath));
741
758
  // 跟踪 Guard 命中次数(回写 Recipe 统计)
742
759
  this.trackGuardHits(violations);
743
760
  // ── Reasoning Enrichment: 推理信息跟随数据流动 ──
@@ -780,11 +797,24 @@ export class GuardCheckEngine {
780
797
  // AstAnalyzer 作为 ESM 模块,在 constructor 时已被引入
781
798
  AstAnalyzer = this._getAstAnalyzer();
782
799
  if (!AstAnalyzer || !AstAnalyzer.isAvailable()) {
800
+ // AST 不可用 — 记录 uncertain
801
+ for (const rule of astRules) {
802
+ this._uncertaintyCollector.recordSkip('ast', 'ast_unavailable', `AST check skipped: tree-sitter not available for lang "${language}"`, { ruleId: rule.id });
803
+ this._uncertaintyCollector.addUncertain(rule.id, rule.message, 'ast', 'ast_unavailable', `Tree-sitter not available for language "${language}"`);
804
+ }
805
+ this._uncertaintyCollector.recordLayerStats('ast', astRules.length, 0);
783
806
  return [];
784
807
  }
785
808
  }
786
809
  catch {
787
810
  this.logger.debug('AstAnalyzer not available, skipping AST rules');
811
+ for (const rule of astRules) {
812
+ this._uncertaintyCollector.recordSkip('ast', 'ast_unavailable', `AST module load failed`, {
813
+ ruleId: rule.id,
814
+ });
815
+ this._uncertaintyCollector.addUncertain(rule.id, rule.message, 'ast', 'ast_unavailable', 'AstAnalyzer module failed to load');
816
+ }
817
+ this._uncertaintyCollector.recordLayerStats('ast', astRules.length, 0);
788
818
  return [];
789
819
  }
790
820
  const violations = [];
@@ -867,6 +897,371 @@ export class GuardCheckEngine {
867
897
  this.logger.debug(`AST rule ${rule.id} check failed: ${err.message}`);
868
898
  }
869
899
  }
900
+ // AST 层统计
901
+ this._uncertaintyCollector.recordLayerStats('ast', astRules.length, astRules.length);
902
+ return violations;
903
+ }
904
+ /**
905
+ * AST Layer 2: analyzeFile() 深层检查
906
+ *
907
+ * 利用 AstAnalyzer.analyzeFile() 的完整输出产出 violations:
908
+ *
909
+ * --- 方法度量 ---
910
+ * - ast_class_bloat: 类方法数过多 (>20)
911
+ * - ast_method_complexity: 高圈复杂度 (>15)
912
+ * - ast_method_too_long: 方法行数过长 (>80)
913
+ * - ast_deep_nesting: 方法嵌套过深 (>5)
914
+ *
915
+ * --- 继承图检查 ---
916
+ * - ast_deep_inheritance: 继承链过深 (>4)
917
+ * - ast_wide_protocol_conformance: 单类遵守协议过多 (>5)
918
+ * - ast_missing_super: 子类未调用 super 的关键方法
919
+ *
920
+ * --- 属性规范 ---
921
+ * - ast_assign_object_property: ObjC assign 修饰对象类型属性
922
+ * - ast_missing_nonatomic: ObjC 属性缺少 nonatomic
923
+ * - ast_mutable_public_collection: 公开可变集合属性
924
+ *
925
+ * --- 设计模式/反模式检测 ---
926
+ * - ast_god_class: 方法+属性过多的上帝类 (>30 methods + >15 properties)
927
+ * - ast_singleton_abuse: 过多单例模式
928
+ * - ast_missing_weakify: block 内 self 捕获但未使用 weakify
929
+ */
930
+ _runAstLayer2Checks(code, language, filePath) {
931
+ const disabled = this._guardConfig.disabledRules || [];
932
+ const allLayer2Rules = [
933
+ 'ast_class_bloat',
934
+ 'ast_method_complexity',
935
+ 'ast_method_too_long',
936
+ 'ast_deep_nesting',
937
+ 'ast_deep_inheritance',
938
+ 'ast_wide_protocol_conformance',
939
+ 'ast_missing_super',
940
+ 'ast_assign_object_property',
941
+ 'ast_missing_nonatomic',
942
+ 'ast_mutable_public_collection',
943
+ 'ast_god_class',
944
+ 'ast_singleton_abuse',
945
+ 'ast_missing_weakify',
946
+ ];
947
+ const allDisabled = allLayer2Rules.every((id) => disabled.includes(id));
948
+ if (allDisabled) {
949
+ return [];
950
+ }
951
+ // 语言标准化
952
+ const astLang = LanguageService.isKnownLang(language)
953
+ ? language
954
+ : language === 'objc'
955
+ ? 'objectivec'
956
+ : language;
957
+ if (!LanguageService.isKnownLang(astLang)) {
958
+ return [];
959
+ }
960
+ let AstAnalyzer;
961
+ try {
962
+ AstAnalyzer = this._getAstAnalyzer();
963
+ if (!AstAnalyzer || !AstAnalyzer.isAvailable()) {
964
+ this._uncertaintyCollector.recordSkip('ast', 'ast_unavailable', `AST Layer 2 skipped: tree-sitter not available for "${language}"`);
965
+ return [];
966
+ }
967
+ }
968
+ catch {
969
+ return [];
970
+ }
971
+ let fileSummary;
972
+ try {
973
+ fileSummary = AstAnalyzer.analyzeFile(code, astLang, { extractCallSites: false });
974
+ }
975
+ catch (err) {
976
+ this.logger.debug(`AST Layer 2 analyzeFile failed: ${err.message}`);
977
+ return [];
978
+ }
979
+ if (!fileSummary) {
980
+ return [];
981
+ }
982
+ const violations = [];
983
+ // — 阈值配置(可通过 codeLevelThresholds 覆盖) —
984
+ const thresholds = this._guardConfig.codeLevelThresholds || {};
985
+ const classBloatLimit = (typeof thresholds['ast_class_bloat'] === 'number' ? thresholds['ast_class_bloat'] : 20);
986
+ const complexityLimit = (typeof thresholds['ast_method_complexity'] === 'number'
987
+ ? thresholds['ast_method_complexity']
988
+ : 15);
989
+ const methodLengthLimit = (typeof thresholds['ast_method_too_long'] === 'number' ? thresholds['ast_method_too_long'] : 80);
990
+ const nestingLimit = (typeof thresholds['ast_deep_nesting'] === 'number' ? thresholds['ast_deep_nesting'] : 5);
991
+ const inheritanceDepthLimit = (typeof thresholds['ast_deep_inheritance'] === 'number'
992
+ ? thresholds['ast_deep_inheritance']
993
+ : 4);
994
+ const protocolConformanceLimit = (typeof thresholds['ast_wide_protocol_conformance'] === 'number'
995
+ ? thresholds['ast_wide_protocol_conformance']
996
+ : 5);
997
+ const godClassMethodLimit = (typeof thresholds['ast_god_class_methods'] === 'number'
998
+ ? thresholds['ast_god_class_methods']
999
+ : 30);
1000
+ const godClassPropertyLimit = (typeof thresholds['ast_god_class_properties'] === 'number'
1001
+ ? thresholds['ast_god_class_properties']
1002
+ : 15);
1003
+ // ══════════════════════════════════════════════════════════
1004
+ // Section A: 方法度量(原有 4 条规则)
1005
+ // ══════════════════════════════════════════════════════════
1006
+ // 1. Class bloat — 类方法数过多
1007
+ if (!disabled.includes('ast_class_bloat')) {
1008
+ const methodCountByClass = {};
1009
+ for (const m of fileSummary.methods) {
1010
+ if (m.className && m.kind === 'definition') {
1011
+ if (!methodCountByClass[m.className]) {
1012
+ const cls = fileSummary.classes.find((c) => c.name === m.className);
1013
+ methodCountByClass[m.className] = { count: 0, line: cls?.line || 1 };
1014
+ }
1015
+ methodCountByClass[m.className].count++;
1016
+ }
1017
+ }
1018
+ for (const [className, { count, line }] of Object.entries(methodCountByClass)) {
1019
+ if (count > classBloatLimit) {
1020
+ violations.push({
1021
+ ruleId: 'ast_class_bloat',
1022
+ message: `类 ${className} 有 ${count} 个方法,超过阈值 ${classBloatLimit},建议拆分职责`,
1023
+ severity: 'warning',
1024
+ line,
1025
+ snippet: `class ${className} — ${count} methods`,
1026
+ dimension: 'file',
1027
+ fixSuggestion: '将职责拆分到多个类或使用 Extension/Category 分组',
1028
+ });
1029
+ }
1030
+ }
1031
+ }
1032
+ // 2. Method complexity — 高圈复杂度
1033
+ if (!disabled.includes('ast_method_complexity')) {
1034
+ for (const m of fileSummary.methods) {
1035
+ if (m.complexity && m.complexity > complexityLimit) {
1036
+ violations.push({
1037
+ ruleId: 'ast_method_complexity',
1038
+ message: `方法 ${m.className ? `${m.className}.` : ''}${m.name} 圈复杂度 ${m.complexity},超过阈值 ${complexityLimit}`,
1039
+ severity: 'warning',
1040
+ line: m.line || 1,
1041
+ snippet: `${m.name} — complexity: ${m.complexity}`,
1042
+ dimension: 'file',
1043
+ fixSuggestion: '提取子方法、使用 early return 或策略模式降低复杂度',
1044
+ });
1045
+ }
1046
+ }
1047
+ }
1048
+ // 3. Method too long — 方法行数过长
1049
+ if (!disabled.includes('ast_method_too_long')) {
1050
+ for (const m of fileSummary.methods) {
1051
+ if (m.bodyLines && m.bodyLines > methodLengthLimit) {
1052
+ violations.push({
1053
+ ruleId: 'ast_method_too_long',
1054
+ message: `方法 ${m.className ? `${m.className}.` : ''}${m.name} 有 ${m.bodyLines} 行,超过阈值 ${methodLengthLimit}`,
1055
+ severity: 'warning',
1056
+ line: m.line || 1,
1057
+ snippet: `${m.name} — ${m.bodyLines} lines`,
1058
+ dimension: 'file',
1059
+ fixSuggestion: '将长方法拆分为多个更小的、职责单一的方法',
1060
+ });
1061
+ }
1062
+ }
1063
+ }
1064
+ // 4. Deep nesting — 方法嵌套过深
1065
+ if (!disabled.includes('ast_deep_nesting')) {
1066
+ for (const m of fileSummary.methods) {
1067
+ if (m.nestingDepth && m.nestingDepth > nestingLimit) {
1068
+ violations.push({
1069
+ ruleId: 'ast_deep_nesting',
1070
+ message: `方法 ${m.className ? `${m.className}.` : ''}${m.name} 嵌套深度 ${m.nestingDepth},超过阈值 ${nestingLimit}`,
1071
+ severity: 'warning',
1072
+ line: m.line || 1,
1073
+ snippet: `${m.name} — nesting depth: ${m.nestingDepth}`,
1074
+ dimension: 'file',
1075
+ fixSuggestion: '使用 guard/early return 减少嵌套,或提取内层逻辑为独立方法',
1076
+ });
1077
+ }
1078
+ }
1079
+ }
1080
+ // ══════════════════════════════════════════════════════════
1081
+ // Section B: 继承图检查(inheritanceGraph + classes.protocols)
1082
+ // ══════════════════════════════════════════════════════════
1083
+ // 5. Deep inheritance — 继承链过深
1084
+ if (!disabled.includes('ast_deep_inheritance') && fileSummary.inheritanceGraph?.length > 0) {
1085
+ // 构建父类映射: child → parent
1086
+ const parentMap = {};
1087
+ for (const edge of fileSummary.inheritanceGraph) {
1088
+ if (edge.type === 'extends' || edge.type === 'inherits') {
1089
+ parentMap[edge.from] = edge.to;
1090
+ }
1091
+ }
1092
+ // 计算每个类的继承深度
1093
+ for (const cls of fileSummary.classes) {
1094
+ let depth = 0;
1095
+ let current = cls.name;
1096
+ const visited = new Set();
1097
+ while (parentMap[current] && !visited.has(current)) {
1098
+ visited.add(current);
1099
+ current = parentMap[current];
1100
+ depth++;
1101
+ }
1102
+ if (depth > inheritanceDepthLimit) {
1103
+ violations.push({
1104
+ ruleId: 'ast_deep_inheritance',
1105
+ message: `类 ${cls.name} 继承链深度 ${depth},超过阈值 ${inheritanceDepthLimit},过深继承增加理解和维护成本`,
1106
+ severity: 'warning',
1107
+ line: cls.line || 1,
1108
+ snippet: `class ${cls.name} — inheritance depth: ${depth}`,
1109
+ dimension: 'file',
1110
+ fixSuggestion: '优先使用组合(Composition)替代继承,或使用协议/接口解耦',
1111
+ });
1112
+ }
1113
+ }
1114
+ }
1115
+ // 6. Wide protocol conformance — 单类遵守协议过多
1116
+ if (!disabled.includes('ast_wide_protocol_conformance')) {
1117
+ for (const cls of fileSummary.classes) {
1118
+ const protocolCount = cls.protocols?.length || 0;
1119
+ if (protocolCount > protocolConformanceLimit) {
1120
+ violations.push({
1121
+ ruleId: 'ast_wide_protocol_conformance',
1122
+ message: `类 ${cls.name} 遵守 ${protocolCount} 个协议,超过阈值 ${protocolConformanceLimit},职责可能过重`,
1123
+ severity: 'warning',
1124
+ line: cls.line || 1,
1125
+ snippet: `class ${cls.name} — ${protocolCount} protocols: ${cls.protocols.slice(0, 5).join(', ')}${protocolCount > 5 ? '...' : ''}`,
1126
+ dimension: 'file',
1127
+ fixSuggestion: '将协议实现拆分到 Extension/Category 中,或拆分类职责',
1128
+ });
1129
+ }
1130
+ }
1131
+ }
1132
+ // ══════════════════════════════════════════════════════════
1133
+ // Section C: 属性规范(properties + attributes)
1134
+ // ══════════════════════════════════════════════════════════
1135
+ const isObjcLike = ['objc', 'objectivec', 'objective-c'].includes(language.toLowerCase());
1136
+ if (isObjcLike && fileSummary.properties?.length > 0) {
1137
+ for (const prop of fileSummary.properties) {
1138
+ const attrs = prop.attributes || [];
1139
+ const attrsLower = attrs.map((a) => a.toLowerCase());
1140
+ // 7. assign 修饰对象类型属性
1141
+ if (!disabled.includes('ast_assign_object_property')) {
1142
+ if (attrsLower.includes('assign') && !attrsLower.includes('readonly')) {
1143
+ // assign 用于对象类型(通过属性名启发:delegate, block, handler 等常为对象)
1144
+ const likelyObject = /delegate|block|handler|callback|completion|dataSource|view|controller|manager|service/i.test(prop.name);
1145
+ if (likelyObject) {
1146
+ violations.push({
1147
+ ruleId: 'ast_assign_object_property',
1148
+ message: `属性 ${prop.className ? `${prop.className}.` : ''}${prop.name} 使用 assign 修饰,疑似对象类型,应改为 weak`,
1149
+ severity: 'warning',
1150
+ line: prop.line || 1,
1151
+ snippet: `@property (assign) ... ${prop.name}`,
1152
+ dimension: 'file',
1153
+ fixSuggestion: '对象类型属性使用 weak(delegate)或 strong/copy,避免悬垂指针',
1154
+ });
1155
+ }
1156
+ }
1157
+ }
1158
+ // 8. 缺少 nonatomic
1159
+ if (!disabled.includes('ast_missing_nonatomic')) {
1160
+ if (!attrsLower.includes('nonatomic') &&
1161
+ !attrsLower.includes('atomic') &&
1162
+ attrs.length > 0) {
1163
+ violations.push({
1164
+ ruleId: 'ast_missing_nonatomic',
1165
+ message: `属性 ${prop.className ? `${prop.className}.` : ''}${prop.name} 缺少 nonatomic,iOS 中应默认使用 nonatomic 提升性能`,
1166
+ severity: 'info',
1167
+ line: prop.line || 1,
1168
+ snippet: `@property (${attrs.join(', ')}) ... ${prop.name}`,
1169
+ dimension: 'file',
1170
+ fixSuggestion: '添加 nonatomic 修饰符:@property (nonatomic, ...) ...',
1171
+ });
1172
+ }
1173
+ }
1174
+ // 9. 公开可变集合属性
1175
+ if (!disabled.includes('ast_mutable_public_collection')) {
1176
+ const isMutable = /NSMutableArray|NSMutableDictionary|NSMutableSet|NSMutableString|NSMutableData|NSMutableOrderedSet/i.test(`${attrs.join(' ')} ${prop.name}`);
1177
+ if (isMutable && !attrsLower.includes('readonly')) {
1178
+ violations.push({
1179
+ ruleId: 'ast_mutable_public_collection',
1180
+ message: `属性 ${prop.className ? `${prop.className}.` : ''}${prop.name} 暴露可变集合,外部可直接修改内部状态`,
1181
+ severity: 'warning',
1182
+ line: prop.line || 1,
1183
+ snippet: `@property ... NSMutable* ${prop.name}`,
1184
+ dimension: 'file',
1185
+ fixSuggestion: '对外使用 readonly + 不可变类型(NSArray/NSDictionary),内部用 readwrite + 可变类型',
1186
+ });
1187
+ }
1188
+ }
1189
+ }
1190
+ }
1191
+ // ══════════════════════════════════════════════════════════
1192
+ // Section D: 设计模式 / 反模式检测(patterns + aggregated metrics)
1193
+ // ══════════════════════════════════════════════════════════
1194
+ // 10. God class — 方法+属性过多的上帝类
1195
+ if (!disabled.includes('ast_god_class')) {
1196
+ // 按类聚合方法数和属性数
1197
+ const classStats = {};
1198
+ for (const m of fileSummary.methods) {
1199
+ if (m.className && m.kind === 'definition') {
1200
+ if (!classStats[m.className]) {
1201
+ const cls = fileSummary.classes.find((c) => c.name === m.className);
1202
+ classStats[m.className] = { methods: 0, properties: 0, line: cls?.line || 1 };
1203
+ }
1204
+ classStats[m.className].methods++;
1205
+ }
1206
+ }
1207
+ for (const p of fileSummary.properties) {
1208
+ if (p.className) {
1209
+ if (!classStats[p.className]) {
1210
+ const cls = fileSummary.classes.find((c) => c.name === p.className);
1211
+ classStats[p.className] = { methods: 0, properties: 0, line: cls?.line || 1 };
1212
+ }
1213
+ classStats[p.className].properties++;
1214
+ }
1215
+ }
1216
+ for (const [className, stats] of Object.entries(classStats)) {
1217
+ if (stats.methods > godClassMethodLimit && stats.properties > godClassPropertyLimit) {
1218
+ violations.push({
1219
+ ruleId: 'ast_god_class',
1220
+ message: `类 ${className} 有 ${stats.methods} 个方法和 ${stats.properties} 个属性,疑似上帝类(God Class),职责过重`,
1221
+ severity: 'warning',
1222
+ line: stats.line,
1223
+ snippet: `class ${className} — ${stats.methods} methods, ${stats.properties} properties`,
1224
+ dimension: 'file',
1225
+ fixSuggestion: '遵循单一职责原则(SRP),将类拆分为多个更小的、职责明确的类',
1226
+ });
1227
+ }
1228
+ }
1229
+ }
1230
+ // 11. Singleton abuse — 过多单例模式(文件级别)
1231
+ if (!disabled.includes('ast_singleton_abuse') && fileSummary.patterns?.length > 0) {
1232
+ const singletonPatterns = fileSummary.patterns.filter((p) => p.type === 'singleton');
1233
+ if (singletonPatterns.length > 2) {
1234
+ violations.push({
1235
+ ruleId: 'ast_singleton_abuse',
1236
+ message: `文件中检测到 ${singletonPatterns.length} 个单例模式,过多单例增加耦合和测试难度`,
1237
+ severity: 'info',
1238
+ line: singletonPatterns[0]?.line || 1,
1239
+ snippet: `${singletonPatterns.length} singletons: ${singletonPatterns
1240
+ .map((p) => p.className || p.methodName || 'unknown')
1241
+ .slice(0, 3)
1242
+ .join(', ')}`,
1243
+ dimension: 'file',
1244
+ fixSuggestion: '考虑使用依赖注入(DI)替代单例,提升可测试性和解耦',
1245
+ });
1246
+ }
1247
+ }
1248
+ // 12. Missing weakify — block 内 self 捕获但未使用 weakify 模式
1249
+ if (!disabled.includes('ast_missing_weakify') &&
1250
+ isObjcLike &&
1251
+ fileSummary.patterns?.length > 0) {
1252
+ const selfCaptures = fileSummary.patterns.filter((p) => p.type === 'block_self_capture' && !p.isWeakRef);
1253
+ for (const cap of selfCaptures) {
1254
+ violations.push({
1255
+ ruleId: 'ast_missing_weakify',
1256
+ message: `${cap.className ? `${cap.className}.` : ''}${cap.methodName || 'block'} 中 block 捕获 self 但未使用 @weakify/@strongify`,
1257
+ severity: 'warning',
1258
+ line: cap.line || 1,
1259
+ snippet: `block captures self without weakify in ${cap.methodName || 'anonymous block'}`,
1260
+ dimension: 'file',
1261
+ fixSuggestion: '使用 @weakify(self) / @strongify(self) 或 __weak typeof(self) weakSelf = self',
1262
+ });
1263
+ }
1264
+ }
870
1265
  return violations;
871
1266
  }
872
1267
  /** 获取 AstAnalyzer 模块(静态 import,带可用性检测) */
@@ -943,15 +1338,20 @@ export class GuardCheckEngine {
943
1338
  */
944
1339
  auditFile(filePath, code, options = {}) {
945
1340
  const language = detectLanguage(filePath);
1341
+ // 每次文件审计前重置 collector(单文件粒度)
1342
+ this._uncertaintyCollector.reset();
946
1343
  const violations = this.checkCode(code, language, { ...options, filePath });
1344
+ const report = this._uncertaintyCollector.buildReport();
947
1345
  return {
948
1346
  filePath,
949
1347
  language,
950
1348
  violations,
1349
+ uncertainResults: report.uncertainResults,
951
1350
  summary: {
952
1351
  total: violations.length,
953
1352
  errors: violations.filter((v) => v.severity === 'error').length,
954
1353
  warnings: violations.filter((v) => v.severity === 'warning').length,
1354
+ uncertain: report.uncertainResults.length,
955
1355
  },
956
1356
  };
957
1357
  }
@@ -977,16 +1377,67 @@ export class GuardCheckEngine {
977
1377
  });
978
1378
  totalViolations += crossFileViolations.length;
979
1379
  totalErrors += crossFileViolations.filter((v) => v.severity === 'error').length;
980
- return {
981
- files: results,
982
- crossFileViolations,
983
- summary: {
984
- filesChecked: results.length,
985
- totalViolations,
986
- totalErrors,
987
- filesWithViolations: results.filter((r) => r.summary.total > 0).length,
988
- },
1380
+ const summary = {
1381
+ filesChecked: results.length,
1382
+ totalViolations,
1383
+ totalErrors,
1384
+ totalUncertain: results.reduce((s, r) => s + r.summary.uncertain, 0),
1385
+ filesWithViolations: results.filter((r) => r.summary.total > 0).length,
989
1386
  };
1387
+ // ── Signal emission (去重:相同检查结果不重复发射) ──
1388
+ if (this._signalBus && totalViolations > 0) {
1389
+ const signalKey = `${summary.filesChecked}:${summary.totalViolations}:${summary.totalErrors}:${summary.totalUncertain}:${summary.filesWithViolations}`;
1390
+ if (signalKey !== this._lastGuardSignalKey) {
1391
+ this._lastGuardSignalKey = signalKey;
1392
+ this._signalBus.send('guard', 'GuardCheckEngine', totalErrors > 0 ? 1 : 0.5, {
1393
+ metadata: { ...summary },
1394
+ });
1395
+ }
1396
+ }
1397
+ // ── 聚合 capability report ──
1398
+ const aggregateCollector = new UncertaintyCollector();
1399
+ for (const r of results) {
1400
+ for (const u of r.uncertainResults) {
1401
+ aggregateCollector.addUncertain(u.ruleId, u.message, u.layer, u.reason, u.detail);
1402
+ }
1403
+ }
1404
+ const capabilityReport = aggregateCollector.buildReport();
1405
+ // ── guard_blind_spot: uncertain 超阈值时发射 CapabilityRequest 信号(去重) ──
1406
+ if (this._signalBus && capabilityReport.uncertainResults.length > 0) {
1407
+ const uncertainTotal = capabilityReport.uncertainResults.length;
1408
+ const blindSpotThreshold = 5; // 触发阈值
1409
+ if (uncertainTotal >= blindSpotThreshold) {
1410
+ const blindSpotKey = `${uncertainTotal}:${capabilityReport.checkCoverage}`;
1411
+ if (blindSpotKey !== this._lastBlindSpotSignalKey) {
1412
+ this._lastBlindSpotSignalKey = blindSpotKey;
1413
+ // 按 layer 聚合盲区
1414
+ const byLayer = {};
1415
+ for (const u of capabilityReport.uncertainResults) {
1416
+ byLayer[u.layer] = (byLayer[u.layer] || 0) + 1;
1417
+ }
1418
+ this._signalBus.send('guard_blind_spot', 'GuardCheckEngine', uncertainTotal >= 20 ? 1 : 0.5, {
1419
+ metadata: {
1420
+ type: 'CapabilityRequest',
1421
+ uncertainTotal,
1422
+ checkCoverage: capabilityReport.checkCoverage,
1423
+ byLayer,
1424
+ boundaries: capabilityReport.boundaries.map((b) => ({
1425
+ type: b.type,
1426
+ description: b.description,
1427
+ affectedRules: b.affectedRules,
1428
+ suggestedAction: b.suggestedAction,
1429
+ })),
1430
+ suggestedAction: 'Extend Guard capability: add AST support for uncovered languages or implement missing cross-file checks',
1431
+ },
1432
+ });
1433
+ }
1434
+ }
1435
+ }
1436
+ return { files: results, crossFileViolations, summary, capabilityReport };
1437
+ }
1438
+ /** 获取 uncertainty collector(供外部读取单文件 uncertain 状态) */
1439
+ getUncertaintyCollector() {
1440
+ return this._uncertaintyCollector;
990
1441
  }
991
1442
  /** 清除规则缓存 */
992
1443
  clearCache() {
@@ -7,6 +7,7 @@
7
7
  * 3. 集成到 guardAuditFiles MCP handler 和 GuardHandler (FileWatcher)
8
8
  */
9
9
  import Logger from '../../infrastructure/logging/Logger.js';
10
+ import type { SignalBus } from '../../infrastructure/signal/SignalBus.js';
10
11
  interface ViolationsStoreLike {
11
12
  getRunsByFile(filePath: string): {
12
13
  violations: {
@@ -40,9 +41,11 @@ export declare class GuardFeedbackLoop {
40
41
  guardCheckEngine: GuardCheckEngineLike | null;
41
42
  logger: ReturnType<typeof Logger.getInstance>;
42
43
  violationsStore: ViolationsStoreLike | null;
44
+ _signalBus: SignalBus | null;
43
45
  /** @param [options.guardCheckEngine] 用于查找规则 */
44
46
  constructor(violationsStore: ViolationsStoreLike | null, feedbackCollector: FeedbackCollectorLike | null, options?: {
45
47
  guardCheckEngine?: GuardCheckEngineLike;
48
+ signalBus?: SignalBus;
46
49
  });
47
50
  /**
48
51
  * 对比当前和历史 violations,检测已修复的违规
@@ -12,11 +12,13 @@ export class GuardFeedbackLoop {
12
12
  guardCheckEngine;
13
13
  logger;
14
14
  violationsStore;
15
+ _signalBus;
15
16
  /** @param [options.guardCheckEngine] 用于查找规则 */
16
17
  constructor(violationsStore, feedbackCollector, options = {}) {
17
18
  this.violationsStore = violationsStore;
18
19
  this.feedbackCollector = feedbackCollector;
19
20
  this.guardCheckEngine = options.guardCheckEngine || null;
21
+ this._signalBus = options.signalBus || null;
20
22
  this.logger = Logger.getInstance();
21
23
  }
22
24
  /**
@@ -72,6 +74,13 @@ export class GuardFeedbackLoop {
72
74
  filePath,
73
75
  });
74
76
  this.logger.info(`[GuardFeedbackLoop] Auto-confirmed usage: recipe=${fixRecipeId} from fixing rule=${ruleId}`);
77
+ // ── Signal: usage confirmation ──
78
+ if (this._signalBus) {
79
+ this._signalBus.send('usage', 'GuardFeedbackLoop', 1, {
80
+ target: fixRecipeId,
81
+ metadata: { ruleId, filePath, source: 'guard_fix_detection' },
82
+ });
83
+ }
75
84
  }
76
85
  catch (err) {
77
86
  this.logger.debug(`[GuardFeedbackLoop] autoConfirmUsage error: ${err.message}`);