@unerr-ai/unerr 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/cli.js +37236 -35793
- package/package.json +1 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Multi-Factor Risk Classifier — computes entity risk from 4 factors:
|
|
3
|
-
* 1. Fan-in (centrality): how many callers depend on this entity
|
|
4
|
-
* 2. Bridge score: does this entity connect two communities?
|
|
5
|
-
* 3. Mutation score: does it write to shared state?
|
|
6
|
-
* 4. Guard score: is it protected by try/catch or similar?
|
|
7
|
-
*
|
|
8
|
-
* Output: "critical" | "high" | "medium" | "low" with numeric score (0-100).
|
|
9
|
-
*
|
|
10
|
-
* Note (Issue #5): The legacy enum used "normal" for the bottom rung; the
|
|
11
|
-
* health-map and other consumers use "low". We canonicalize on "low" here
|
|
12
|
-
* and provide `normalizeRiskLevel()` to translate stale "normal" values from
|
|
13
|
-
* existing graph data. Both names are accepted in `isRiskLevel()` to keep
|
|
14
|
-
* older snapshots working until they're re-indexed.
|
|
15
|
-
*/
|
|
16
|
-
/** Translate a possibly-stale risk_level string to the canonical enum.
|
|
17
|
-
* Old snapshots used "normal" for the bottom rung; map it to "low". */
|
|
18
|
-
export function normalizeRiskLevel(value) {
|
|
19
|
-
if (value === "critical" || value === "high" || value === "medium") {
|
|
20
|
-
return value;
|
|
21
|
-
}
|
|
22
|
-
// "normal" (legacy) and any unknown / null fall through to "low".
|
|
23
|
-
return "low";
|
|
24
|
-
}
|
|
25
|
-
const WEIGHTS = {
|
|
26
|
-
fanIn: 0.4,
|
|
27
|
-
bridge: 0.25,
|
|
28
|
-
mutation: 0.2,
|
|
29
|
-
guard: 0.15,
|
|
30
|
-
};
|
|
31
|
-
const THRESHOLDS = {
|
|
32
|
-
critical: 35,
|
|
33
|
-
high: 22,
|
|
34
|
-
medium: 10,
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* Classify risk for a single entity.
|
|
38
|
-
*/
|
|
39
|
-
export function classifyRisk(entityKey, fanIn, isBridge, hasMutations, isGuarded) {
|
|
40
|
-
const fanInScore = Math.min(100, fanIn * 2);
|
|
41
|
-
const bridgeScore = isBridge ? 80 : 0;
|
|
42
|
-
const mutationScore = hasMutations ? 60 : 0;
|
|
43
|
-
const guardScore = isGuarded ? -20 : 0;
|
|
44
|
-
const rawScore = WEIGHTS.fanIn * fanInScore +
|
|
45
|
-
WEIGHTS.bridge * bridgeScore +
|
|
46
|
-
WEIGHTS.mutation * mutationScore +
|
|
47
|
-
WEIGHTS.guard * Math.abs(guardScore);
|
|
48
|
-
const score = Math.max(0, Math.min(100, Math.round(rawScore + guardScore * WEIGHTS.guard)));
|
|
49
|
-
const level = score >= THRESHOLDS.critical
|
|
50
|
-
? "critical"
|
|
51
|
-
: score >= THRESHOLDS.high
|
|
52
|
-
? "high"
|
|
53
|
-
: score >= THRESHOLDS.medium
|
|
54
|
-
? "medium"
|
|
55
|
-
: "low";
|
|
56
|
-
const reasons = [];
|
|
57
|
-
if (fanIn >= 50)
|
|
58
|
-
reasons.push(`${fanIn} callers (chokepoint)`);
|
|
59
|
-
else if (fanIn >= 20)
|
|
60
|
-
reasons.push(`${fanIn} callers (high traffic)`);
|
|
61
|
-
if (isBridge)
|
|
62
|
-
reasons.push("bridges 2+ communities");
|
|
63
|
-
if (hasMutations)
|
|
64
|
-
reasons.push("mutates shared state");
|
|
65
|
-
if (isGuarded)
|
|
66
|
-
reasons.push("protected by error handling");
|
|
67
|
-
return {
|
|
68
|
-
entityKey,
|
|
69
|
-
level,
|
|
70
|
-
score,
|
|
71
|
-
factors: {
|
|
72
|
-
fanIn: fanInScore,
|
|
73
|
-
bridgeScore,
|
|
74
|
-
mutationScore,
|
|
75
|
-
guardScore,
|
|
76
|
-
},
|
|
77
|
-
reasoning: reasons.length > 0
|
|
78
|
-
? reasons.join("; ")
|
|
79
|
-
: "low risk (no significant factors)",
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Detect bridge entities (entities that connect two communities).
|
|
84
|
-
*/
|
|
85
|
-
export function detectBridges(entityKey, edges, communityAssignments) {
|
|
86
|
-
const myCommunity = communityAssignments.get(entityKey);
|
|
87
|
-
if (myCommunity === undefined)
|
|
88
|
-
return false;
|
|
89
|
-
const connectedCommunities = new Set();
|
|
90
|
-
for (const edge of edges) {
|
|
91
|
-
if (edge.from_key === entityKey) {
|
|
92
|
-
const targetCommunity = communityAssignments.get(edge.to_key);
|
|
93
|
-
if (targetCommunity !== undefined)
|
|
94
|
-
connectedCommunities.add(targetCommunity);
|
|
95
|
-
}
|
|
96
|
-
if (edge.to_key === entityKey) {
|
|
97
|
-
const sourceCommunity = communityAssignments.get(edge.from_key);
|
|
98
|
-
if (sourceCommunity !== undefined)
|
|
99
|
-
connectedCommunities.add(sourceCommunity);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
connectedCommunities.delete(myCommunity);
|
|
103
|
-
return connectedCommunities.size >= 1;
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Batch classify risk for all entities.
|
|
107
|
-
*/
|
|
108
|
-
export function classifyAllRisks(entities, fanInMap, edges, communityAssignments, mutationEntities, guardedEntities) {
|
|
109
|
-
return entities.map((entity) => {
|
|
110
|
-
const fanIn = fanInMap.get(entity.key) ?? 0;
|
|
111
|
-
const isBridge = detectBridges(entity.key, edges, communityAssignments);
|
|
112
|
-
const hasMutations = mutationEntities.has(entity.key);
|
|
113
|
-
const isGuarded = guardedEntities.has(entity.key);
|
|
114
|
-
return classifyRisk(entity.key, fanIn, isBridge, hasMutations, isGuarded);
|
|
115
|
-
});
|
|
116
|
-
}
|
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Phase 10b + Sprint 9: Local rule evaluator — tree-sitter structural + naming evaluation.
|
|
3
|
-
*
|
|
4
|
-
* Evaluates rules locally.
|
|
5
|
-
* - Structural rules: Uses tree-sitter to parse AST and match node types
|
|
6
|
-
* - Naming rules: Uses regex against entity names from CozoDB
|
|
7
|
-
* - Semgrep/LLM rules: Skipped (not yet supported locally)
|
|
8
|
-
*
|
|
9
|
-
* Sprint 9 additions:
|
|
10
|
-
* - 9.1: JIT Rule Filtering — score rules by relevance, cap at 30
|
|
11
|
-
* - 9.2: Auto-Remediation Passthrough — ast_grep_fix → autoFix in violations
|
|
12
|
-
* - 9.5: STAGED Rule Dry-Run — evaluate but separate into stagedRuleResults
|
|
13
|
-
* - 9.6: Rule Exception TTL — active exceptions downgrade to "suggest"
|
|
14
|
-
*/
|
|
15
|
-
// Lazy-loaded tree-sitter instance
|
|
16
|
-
let parserCache = null;
|
|
17
|
-
const LANGUAGE_MAP = {
|
|
18
|
-
".ts": "typescript",
|
|
19
|
-
".tsx": "typescript",
|
|
20
|
-
".js": "typescript",
|
|
21
|
-
".jsx": "typescript",
|
|
22
|
-
".py": "python",
|
|
23
|
-
".go": "go",
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Detect language from file extension.
|
|
27
|
-
*/
|
|
28
|
-
function detectLanguage(filePath) {
|
|
29
|
-
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
30
|
-
return LANGUAGE_MAP[ext] ?? null;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Lazy-load tree-sitter parser for a language.
|
|
34
|
-
* Returns null if the language is not supported or tree-sitter is not available.
|
|
35
|
-
*/
|
|
36
|
-
async function getParser(language) {
|
|
37
|
-
if (!parserCache) {
|
|
38
|
-
parserCache = new Map();
|
|
39
|
-
}
|
|
40
|
-
if (parserCache.has(language)) {
|
|
41
|
-
return parserCache.get(language);
|
|
42
|
-
}
|
|
43
|
-
try {
|
|
44
|
-
const TreeSitter = (await import("web-tree-sitter")).default;
|
|
45
|
-
await TreeSitter.init();
|
|
46
|
-
const parser = new TreeSitter();
|
|
47
|
-
// Try to load language grammar from node_modules
|
|
48
|
-
const langFile = `tree-sitter-${language}.wasm`;
|
|
49
|
-
try {
|
|
50
|
-
const { join } = await import("node:path");
|
|
51
|
-
const { existsSync } = await import("node:fs");
|
|
52
|
-
// Check multiple possible locations for WASM files
|
|
53
|
-
const possiblePaths = [
|
|
54
|
-
join(process.cwd(), "node_modules", `tree-sitter-${language}`, langFile),
|
|
55
|
-
join(process.cwd(), "node_modules", "web-tree-sitter", langFile),
|
|
56
|
-
];
|
|
57
|
-
let wasmPath = null;
|
|
58
|
-
for (const p of possiblePaths) {
|
|
59
|
-
if (existsSync(p)) {
|
|
60
|
-
wasmPath = p;
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (!wasmPath) {
|
|
65
|
-
// Grammar WASM not found — degrade gracefully
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
const lang = await TreeSitter.Language.load(wasmPath);
|
|
69
|
-
parser.setLanguage(lang);
|
|
70
|
-
parserCache.set(language, parser);
|
|
71
|
-
return parser;
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch {
|
|
78
|
-
// web-tree-sitter not available
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Walk tree-sitter AST and collect nodes matching target types.
|
|
84
|
-
*/
|
|
85
|
-
function collectNodesByType(node, targetTypes) {
|
|
86
|
-
const results = [];
|
|
87
|
-
function walk(n) {
|
|
88
|
-
if (targetTypes.includes(n.type)) {
|
|
89
|
-
results.push(n);
|
|
90
|
-
}
|
|
91
|
-
for (const child of n.children) {
|
|
92
|
-
walk(child);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
walk(node);
|
|
96
|
-
return results;
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Evaluate a structural rule using tree-sitter AST matching.
|
|
100
|
-
*/
|
|
101
|
-
async function evaluateStructural(rule, filePath, content) {
|
|
102
|
-
const language = detectLanguage(filePath);
|
|
103
|
-
if (!language)
|
|
104
|
-
return [];
|
|
105
|
-
const parser = await getParser(language);
|
|
106
|
-
if (!parser)
|
|
107
|
-
return [];
|
|
108
|
-
const tree = parser.parse(content);
|
|
109
|
-
// Parse rule.query as comma-separated node types to match
|
|
110
|
-
const targetTypes = rule.query
|
|
111
|
-
.split(",")
|
|
112
|
-
.map((t) => t.trim())
|
|
113
|
-
.filter(Boolean);
|
|
114
|
-
if (targetTypes.length === 0)
|
|
115
|
-
return [];
|
|
116
|
-
const matches = collectNodesByType(tree.rootNode, targetTypes);
|
|
117
|
-
return matches.map((node) => ({
|
|
118
|
-
ruleKey: rule.key,
|
|
119
|
-
ruleName: rule.name,
|
|
120
|
-
severity: rule.severity,
|
|
121
|
-
message: rule.message ||
|
|
122
|
-
`Structural rule "${rule.name}" matched node type "${node.type}"`,
|
|
123
|
-
filePath,
|
|
124
|
-
line: node.startPosition.row + 1,
|
|
125
|
-
matchedCode: node.text.slice(0, 200),
|
|
126
|
-
}));
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Evaluate a naming rule using regex against entity names in the file.
|
|
130
|
-
*/
|
|
131
|
-
async function evaluateNaming(rule, filePath, localGraph) {
|
|
132
|
-
const entities = await localGraph.getEntitiesByFile(filePath);
|
|
133
|
-
if (entities.length === 0)
|
|
134
|
-
return [];
|
|
135
|
-
let regex;
|
|
136
|
-
try {
|
|
137
|
-
regex = new RegExp(rule.query);
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
return [];
|
|
141
|
-
}
|
|
142
|
-
const violations = [];
|
|
143
|
-
for (const entity of entities) {
|
|
144
|
-
if (regex.test(entity.name)) {
|
|
145
|
-
violations.push({
|
|
146
|
-
ruleKey: rule.key,
|
|
147
|
-
ruleName: rule.name,
|
|
148
|
-
severity: rule.severity,
|
|
149
|
-
message: rule.message ||
|
|
150
|
-
`Naming rule "${rule.name}" matched entity "${entity.name}"`,
|
|
151
|
-
filePath,
|
|
152
|
-
line: entity.start_line,
|
|
153
|
-
matchedCode: entity.name,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
return violations;
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Sprint 9.1: JIT Rule Filtering — score rules by relevance to target entity context.
|
|
161
|
-
*
|
|
162
|
-
* Scoring (Phase 6 §1.4b):
|
|
163
|
-
* +0.4 if rule.file_glob matches filePath
|
|
164
|
-
* +0.2 if rule.scope == "repo"
|
|
165
|
-
* +0.3 if entity kind ∈ rule.target_kinds
|
|
166
|
-
* +0.1 if rule.severity == "block" (always included)
|
|
167
|
-
*
|
|
168
|
-
* Returns rules with score > 0.3 OR severity == "block", capped at 30.
|
|
169
|
-
*/
|
|
170
|
-
export async function getRelevantRules(allRules, filePath, entityKey, localGraph) {
|
|
171
|
-
const entityMeta = entityKey ? await localGraph.getEntity(entityKey) : null;
|
|
172
|
-
const scored = allRules.map((rule) => {
|
|
173
|
-
let score = 0.0;
|
|
174
|
-
// +0.4 for file_glob match
|
|
175
|
-
if (rule.file_glob && matchesGlob(filePath, rule.file_glob)) {
|
|
176
|
-
score += 0.4;
|
|
177
|
-
}
|
|
178
|
-
else if (!rule.file_glob) {
|
|
179
|
-
// No glob means rule applies to all files — partial credit
|
|
180
|
-
score += 0.2;
|
|
181
|
-
}
|
|
182
|
-
// +0.2 for repo scope
|
|
183
|
-
if (rule.scope === "repo") {
|
|
184
|
-
score += 0.2;
|
|
185
|
-
}
|
|
186
|
-
// +0.3 for entity kind match
|
|
187
|
-
if (entityMeta && rule.target_kinds) {
|
|
188
|
-
const targetKinds = rule.target_kinds.split(",").map((k) => k.trim());
|
|
189
|
-
if (targetKinds.includes(entityMeta.kind)) {
|
|
190
|
-
score += 0.3;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
// +0.1 for block severity (ensures inclusion)
|
|
194
|
-
if (rule.severity === "block") {
|
|
195
|
-
score += 0.1;
|
|
196
|
-
}
|
|
197
|
-
return { ...rule, relevanceScore: score };
|
|
198
|
-
});
|
|
199
|
-
// Filter: score > 0.3 OR severity == "block"
|
|
200
|
-
const relevant = scored
|
|
201
|
-
.filter((r) => r.relevanceScore > 0.3 || r.severity === "block")
|
|
202
|
-
.sort((a, b) => b.relevanceScore - a.relevanceScore)
|
|
203
|
-
.slice(0, 30);
|
|
204
|
-
const maxScore = relevant.length > 0 ? relevant[0]?.relevanceScore : 0;
|
|
205
|
-
return { rules: relevant, totalRules: allRules.length, maxScore };
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* Simple glob matching for file paths.
|
|
209
|
-
*/
|
|
210
|
-
function matchesGlob(filePath, glob) {
|
|
211
|
-
// Convert glob to regex: * → [^/]*, ** → .*, ? → .
|
|
212
|
-
const escaped = glob
|
|
213
|
-
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
214
|
-
.replace(/\*\*/g, "<<<GLOBSTAR>>>")
|
|
215
|
-
.replace(/\*/g, "[^/]*")
|
|
216
|
-
.replace(/<<<GLOBSTAR>>>/g, ".*")
|
|
217
|
-
.replace(/\?/g, ".");
|
|
218
|
-
try {
|
|
219
|
-
return new RegExp(`^${escaped}$`).test(filePath);
|
|
220
|
-
}
|
|
221
|
-
catch {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Evaluate all applicable rules against a file.
|
|
227
|
-
*
|
|
228
|
-
* Sprint 9 enhancements:
|
|
229
|
-
* - 9.1: JIT filtering (when entityKey provided)
|
|
230
|
-
* - 9.2: Auto-remediation passthrough (ast_grep_fix → autoFix)
|
|
231
|
-
* - 9.5: STAGED rules evaluated but separated into the evaluator's internal `_meta.stagedRuleResults` return field (not the wire envelope)
|
|
232
|
-
* - 9.6: Rule exceptions downgrade enforcement to "suggest"
|
|
233
|
-
*
|
|
234
|
-
* Partitions rules by engine:
|
|
235
|
-
* - structural: tree-sitter AST matching
|
|
236
|
-
* - naming: regex match against entity names
|
|
237
|
-
* - semgrep/llm: skipped (not supported locally)
|
|
238
|
-
*/
|
|
239
|
-
export async function evaluateRules(rules, filePath, content, localGraph, options) {
|
|
240
|
-
const violations = [];
|
|
241
|
-
const stagedResults = [];
|
|
242
|
-
let structuralCount = 0;
|
|
243
|
-
let namingCount = 0;
|
|
244
|
-
let skippedCount = 0;
|
|
245
|
-
let exceptionsApplied = 0;
|
|
246
|
-
// Sprint 9.1: JIT filtering
|
|
247
|
-
let jitMeta;
|
|
248
|
-
let activeRules;
|
|
249
|
-
if (options?.entityKey) {
|
|
250
|
-
const jit = await getRelevantRules(rules, filePath, options.entityKey, localGraph);
|
|
251
|
-
activeRules = jit.rules;
|
|
252
|
-
jitMeta = {
|
|
253
|
-
totalRules: jit.totalRules,
|
|
254
|
-
relevantRules: jit.rules.length,
|
|
255
|
-
maxScore: jit.maxScore,
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
else {
|
|
259
|
-
activeRules = rules;
|
|
260
|
-
}
|
|
261
|
-
// Sprint 9.5: Separate staged rules
|
|
262
|
-
const enforceableRules = [];
|
|
263
|
-
const stagedRules = [];
|
|
264
|
-
for (const rule of activeRules) {
|
|
265
|
-
if ((rule.status ?? "active") === "staged") {
|
|
266
|
-
stagedRules.push(rule);
|
|
267
|
-
}
|
|
268
|
-
else {
|
|
269
|
-
enforceableRules.push(rule);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
// Build exception lookup: ruleKey → exception[]
|
|
273
|
-
const exceptionMap = new Map();
|
|
274
|
-
if (options?.exceptions) {
|
|
275
|
-
for (const ex of options.exceptions) {
|
|
276
|
-
const existing = exceptionMap.get(ex.rule_key) ?? [];
|
|
277
|
-
existing.push(ex);
|
|
278
|
-
exceptionMap.set(ex.rule_key, existing);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// Evaluate enforceable rules
|
|
282
|
-
for (const rule of enforceableRules) {
|
|
283
|
-
if (!rule.enabled) {
|
|
284
|
-
skippedCount++;
|
|
285
|
-
continue;
|
|
286
|
-
}
|
|
287
|
-
const ruleViolations = await evaluateSingleRule(rule, filePath, content, localGraph);
|
|
288
|
-
if (ruleViolations.engine === "skipped") {
|
|
289
|
-
skippedCount++;
|
|
290
|
-
continue;
|
|
291
|
-
}
|
|
292
|
-
if (ruleViolations.engine === "structural")
|
|
293
|
-
structuralCount++;
|
|
294
|
-
else if (ruleViolations.engine === "naming")
|
|
295
|
-
namingCount++;
|
|
296
|
-
// Sprint 9.6: Check exceptions for each violation
|
|
297
|
-
for (const v of ruleViolations.violations) {
|
|
298
|
-
const ruleExceptions = exceptionMap.get(rule.key);
|
|
299
|
-
if (ruleExceptions && ruleExceptions.length > 0) {
|
|
300
|
-
const activeEx = ruleExceptions[0];
|
|
301
|
-
v.severity = "suggest";
|
|
302
|
-
v.exception = {
|
|
303
|
-
key: activeEx.key,
|
|
304
|
-
reason: activeEx.reason,
|
|
305
|
-
expires_at: activeEx.expires_at,
|
|
306
|
-
granted_by: activeEx.granted_by,
|
|
307
|
-
};
|
|
308
|
-
exceptionsApplied++;
|
|
309
|
-
}
|
|
310
|
-
// Sprint 9.2: Auto-remediation passthrough
|
|
311
|
-
if (rule.ast_grep_fix) {
|
|
312
|
-
v.autoFix = {
|
|
313
|
-
rule: rule.key,
|
|
314
|
-
diff: rule.ast_grep_fix,
|
|
315
|
-
confidence: 0.95,
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
violations.push(v);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
// Sprint 9.5: Evaluate staged rules (dry-run only)
|
|
322
|
-
for (const rule of stagedRules) {
|
|
323
|
-
if (!rule.enabled)
|
|
324
|
-
continue;
|
|
325
|
-
const ruleViolations = await evaluateSingleRule(rule, filePath, content, localGraph);
|
|
326
|
-
if (ruleViolations.engine === "skipped")
|
|
327
|
-
continue;
|
|
328
|
-
stagedResults.push({
|
|
329
|
-
ruleKey: rule.key,
|
|
330
|
-
ruleName: rule.name,
|
|
331
|
-
wouldViolate: ruleViolations.violations.length > 0,
|
|
332
|
-
violationCount: ruleViolations.violations.length,
|
|
333
|
-
message: ruleViolations.violations.length > 0
|
|
334
|
-
? ruleViolations.violations[0]?.message
|
|
335
|
-
: `No violations for staged rule "${rule.name}"`,
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
// Sprint 9.6: Check for expiring exceptions (7 day window)
|
|
339
|
-
const SEVEN_DAYS_MS = 7 * 24 * 60 * 60 * 1000;
|
|
340
|
-
const expiringExceptions = (await localGraph.getExpiringExceptions(SEVEN_DAYS_MS)).map((ex) => ({
|
|
341
|
-
ruleKey: ex.rule_key,
|
|
342
|
-
entityKey: ex.entity_key,
|
|
343
|
-
expires_at: ex.expires_at,
|
|
344
|
-
jira_ticket: ex.jira_ticket,
|
|
345
|
-
}));
|
|
346
|
-
return {
|
|
347
|
-
violations,
|
|
348
|
-
_meta: {
|
|
349
|
-
source: "local",
|
|
350
|
-
evaluatedRules: structuralCount + namingCount,
|
|
351
|
-
skippedRules: skippedCount,
|
|
352
|
-
engines: {
|
|
353
|
-
structural: structuralCount,
|
|
354
|
-
naming: namingCount,
|
|
355
|
-
skipped: skippedCount,
|
|
356
|
-
},
|
|
357
|
-
...(jitMeta ? { jitFiltering: jitMeta } : {}),
|
|
358
|
-
...(stagedResults.length > 0 ? { stagedRuleResults: stagedResults } : {}),
|
|
359
|
-
...(exceptionsApplied > 0 ? { exceptionsApplied } : {}),
|
|
360
|
-
...(expiringExceptions.length > 0 ? { expiringExceptions } : {}),
|
|
361
|
-
},
|
|
362
|
-
};
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Evaluate a single rule and return violations + engine type.
|
|
366
|
-
*/
|
|
367
|
-
async function evaluateSingleRule(rule, filePath, content, localGraph) {
|
|
368
|
-
switch (rule.engine) {
|
|
369
|
-
case "structural": {
|
|
370
|
-
const violations = await evaluateStructural(rule, filePath, content);
|
|
371
|
-
return { violations, engine: "structural" };
|
|
372
|
-
}
|
|
373
|
-
case "naming": {
|
|
374
|
-
const violations = await evaluateNaming(rule, filePath, localGraph);
|
|
375
|
-
return { violations, engine: "naming" };
|
|
376
|
-
}
|
|
377
|
-
default:
|
|
378
|
-
return { violations: [], engine: "skipped" };
|
|
379
|
-
}
|
|
380
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auto-Rule Generation — promotes high-confidence corrections into enforced rules.
|
|
3
|
-
*
|
|
4
|
-
* U.6: When a correction has 3+ occurrences and confidence > 0.9,
|
|
5
|
-
* it graduates from "correction" to "rule" — proactively injected
|
|
6
|
-
* to prevent the same mistake in future sessions.
|
|
7
|
-
*/
|
|
8
|
-
const PROMOTION_THRESHOLD_CONFIDENCE = 0.9;
|
|
9
|
-
const PROMOTION_THRESHOLD_OCCURRENCES = 3;
|
|
10
|
-
/**
|
|
11
|
-
* Evaluate corrections for rule promotion.
|
|
12
|
-
* Returns rules that should be auto-generated.
|
|
13
|
-
*/
|
|
14
|
-
export function evaluateForPromotion(corrections) {
|
|
15
|
-
const rules = [];
|
|
16
|
-
for (const correction of corrections) {
|
|
17
|
-
if (correction.confidence >= PROMOTION_THRESHOLD_CONFIDENCE &&
|
|
18
|
-
correction.occurrences >= PROMOTION_THRESHOLD_OCCURRENCES) {
|
|
19
|
-
rules.push({
|
|
20
|
-
id: `auto-rule:${correction.entityKey}:${correction.pattern}`,
|
|
21
|
-
entityKey: correction.entityKey,
|
|
22
|
-
scope: extractScope(correction.entityKey),
|
|
23
|
-
severity: "warn",
|
|
24
|
-
message: correction.description,
|
|
25
|
-
source: "correction-auto",
|
|
26
|
-
confidence: correction.confidence,
|
|
27
|
-
occurrences: correction.occurrences,
|
|
28
|
-
createdAt: new Date().toISOString(),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return rules;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Check if a specific correction should be promoted.
|
|
36
|
-
*/
|
|
37
|
-
export function shouldPromote(correction) {
|
|
38
|
-
return (correction.confidence >= PROMOTION_THRESHOLD_CONFIDENCE &&
|
|
39
|
-
correction.occurrences >= PROMOTION_THRESHOLD_OCCURRENCES);
|
|
40
|
-
}
|
|
41
|
-
function extractScope(entityKey) {
|
|
42
|
-
if (entityKey.includes("::"))
|
|
43
|
-
return entityKey.split("::")[0] ?? entityKey;
|
|
44
|
-
if (entityKey.includes("/")) {
|
|
45
|
-
const parts = entityKey.split("/");
|
|
46
|
-
return parts.slice(0, -1).join("/");
|
|
47
|
-
}
|
|
48
|
-
return entityKey;
|
|
49
|
-
}
|