@descryy/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/capabilities/index.d.ts +3 -0
- package/dist/capabilities/index.d.ts.map +1 -0
- package/dist/capabilities/index.js +2 -0
- package/dist/capabilities/index.js.map +1 -0
- package/dist/capabilities/preconditions.d.ts +99 -0
- package/dist/capabilities/preconditions.d.ts.map +1 -0
- package/dist/capabilities/preconditions.js +94 -0
- package/dist/capabilities/preconditions.js.map +1 -0
- package/dist/contracts/engine.d.ts +90 -0
- package/dist/contracts/engine.d.ts.map +1 -0
- package/dist/contracts/engine.js +291 -0
- package/dist/contracts/engine.js.map +1 -0
- package/dist/contracts/index.d.ts +9 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +5 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/orphans.d.ts +96 -0
- package/dist/contracts/orphans.d.ts.map +1 -0
- package/dist/contracts/orphans.js +241 -0
- package/dist/contracts/orphans.js.map +1 -0
- package/dist/contracts/paths.d.ts +132 -0
- package/dist/contracts/paths.d.ts.map +1 -0
- package/dist/contracts/paths.js +245 -0
- package/dist/contracts/paths.js.map +1 -0
- package/dist/contracts/shapes.d.ts +99 -0
- package/dist/contracts/shapes.d.ts.map +1 -0
- package/dist/contracts/shapes.js +196 -0
- package/dist/contracts/shapes.js.map +1 -0
- package/dist/governance/budget.d.ts +89 -0
- package/dist/governance/budget.d.ts.map +1 -0
- package/dist/governance/budget.js +101 -0
- package/dist/governance/budget.js.map +1 -0
- package/dist/governance/candidate-boundary.d.ts +49 -0
- package/dist/governance/candidate-boundary.d.ts.map +1 -0
- package/dist/governance/candidate-boundary.js +51 -0
- package/dist/governance/candidate-boundary.js.map +1 -0
- package/dist/governance/escalation.d.ts +63 -0
- package/dist/governance/escalation.d.ts.map +1 -0
- package/dist/governance/escalation.js +72 -0
- package/dist/governance/escalation.js.map +1 -0
- package/dist/governance/fact-boundary.d.ts +77 -0
- package/dist/governance/fact-boundary.d.ts.map +1 -0
- package/dist/governance/fact-boundary.js +91 -0
- package/dist/governance/fact-boundary.js.map +1 -0
- package/dist/governance/finding-funnel.d.ts +109 -0
- package/dist/governance/finding-funnel.d.ts.map +1 -0
- package/dist/governance/finding-funnel.js +137 -0
- package/dist/governance/finding-funnel.js.map +1 -0
- package/dist/governance/index.d.ts +11 -0
- package/dist/governance/index.d.ts.map +1 -0
- package/dist/governance/index.js +6 -0
- package/dist/governance/index.js.map +1 -0
- package/dist/graph/build.d.ts +127 -0
- package/dist/graph/build.d.ts.map +1 -0
- package/dist/graph/build.js +0 -0
- package/dist/graph/build.js.map +1 -0
- package/dist/graph/cross-language.d.ts +70 -0
- package/dist/graph/cross-language.d.ts.map +1 -0
- package/dist/graph/cross-language.js +104 -0
- package/dist/graph/cross-language.js.map +1 -0
- package/dist/graph/index.d.ts +11 -0
- package/dist/graph/index.d.ts.map +1 -0
- package/dist/graph/index.js +6 -0
- package/dist/graph/index.js.map +1 -0
- package/dist/graph/merge.d.ts +95 -0
- package/dist/graph/merge.d.ts.map +1 -0
- package/dist/graph/merge.js +131 -0
- package/dist/graph/merge.js.map +1 -0
- package/dist/graph/persist.d.ts +42 -0
- package/dist/graph/persist.d.ts.map +1 -0
- package/dist/graph/persist.js +52 -0
- package/dist/graph/persist.js.map +1 -0
- package/dist/graph/runtime-confirmation.d.ts +169 -0
- package/dist/graph/runtime-confirmation.d.ts.map +1 -0
- package/dist/graph/runtime-confirmation.js +318 -0
- package/dist/graph/runtime-confirmation.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/ledger/index.d.ts +5 -0
- package/dist/ledger/index.d.ts.map +1 -0
- package/dist/ledger/index.js +3 -0
- package/dist/ledger/index.js.map +1 -0
- package/dist/ledger/mint.d.ts +81 -0
- package/dist/ledger/mint.d.ts.map +1 -0
- package/dist/ledger/mint.js +138 -0
- package/dist/ledger/mint.js.map +1 -0
- package/dist/ledger/resolve.d.ts +124 -0
- package/dist/ledger/resolve.d.ts.map +1 -0
- package/dist/ledger/resolve.js +290 -0
- package/dist/ledger/resolve.js.map +1 -0
- package/dist/multipr/fingerprint.d.ts +56 -0
- package/dist/multipr/fingerprint.d.ts.map +1 -0
- package/dist/multipr/fingerprint.js +169 -0
- package/dist/multipr/fingerprint.js.map +1 -0
- package/dist/multipr/hidden-dependency.d.ts +120 -0
- package/dist/multipr/hidden-dependency.d.ts.map +1 -0
- package/dist/multipr/hidden-dependency.js +185 -0
- package/dist/multipr/hidden-dependency.js.map +1 -0
- package/dist/multipr/index.d.ts +14 -0
- package/dist/multipr/index.d.ts.map +1 -0
- package/dist/multipr/index.js +8 -0
- package/dist/multipr/index.js.map +1 -0
- package/dist/multipr/mechanical.d.ts +239 -0
- package/dist/multipr/mechanical.d.ts.map +1 -0
- package/dist/multipr/mechanical.js +333 -0
- package/dist/multipr/mechanical.js.map +1 -0
- package/dist/multipr/migration-heads.d.ts +160 -0
- package/dist/multipr/migration-heads.d.ts.map +1 -0
- package/dist/multipr/migration-heads.js +212 -0
- package/dist/multipr/migration-heads.js.map +1 -0
- package/dist/multipr/overlap.d.ts +186 -0
- package/dist/multipr/overlap.d.ts.map +1 -0
- package/dist/multipr/overlap.js +254 -0
- package/dist/multipr/overlap.js.map +1 -0
- package/dist/multipr/scope-store.d.ts +105 -0
- package/dist/multipr/scope-store.d.ts.map +1 -0
- package/dist/multipr/scope-store.js +182 -0
- package/dist/multipr/scope-store.js.map +1 -0
- package/dist/multipr/superseded.d.ts +133 -0
- package/dist/multipr/superseded.d.ts.map +1 -0
- package/dist/multipr/superseded.js +128 -0
- package/dist/multipr/superseded.js.map +1 -0
- package/dist/query/confirmed-facts.d.ts +56 -0
- package/dist/query/confirmed-facts.d.ts.map +1 -0
- package/dist/query/confirmed-facts.js +64 -0
- package/dist/query/confirmed-facts.js.map +1 -0
- package/dist/query/declared-value-closure.d.ts +126 -0
- package/dist/query/declared-value-closure.d.ts.map +1 -0
- package/dist/query/declared-value-closure.js +156 -0
- package/dist/query/declared-value-closure.js.map +1 -0
- package/dist/query/index.d.ts +28 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +15 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/memory.d.ts +20 -0
- package/dist/query/memory.d.ts.map +1 -0
- package/dist/query/memory.js +55 -0
- package/dist/query/memory.js.map +1 -0
- package/dist/query/provider.d.ts +127 -0
- package/dist/query/provider.d.ts.map +1 -0
- package/dist/query/provider.js +54 -0
- package/dist/query/provider.js.map +1 -0
- package/dist/query/queries.d.ts +119 -0
- package/dist/query/queries.d.ts.map +1 -0
- package/dist/query/queries.js +267 -0
- package/dist/query/queries.js.map +1 -0
- package/dist/query/refusal-fetch.d.ts +114 -0
- package/dist/query/refusal-fetch.d.ts.map +1 -0
- package/dist/query/refusal-fetch.js +200 -0
- package/dist/query/refusal-fetch.js.map +1 -0
- package/dist/query/refusal-questions.d.ts +123 -0
- package/dist/query/refusal-questions.d.ts.map +1 -0
- package/dist/query/refusal-questions.js +122 -0
- package/dist/query/refusal-questions.js.map +1 -0
- package/dist/query/root-cause-score.d.ts +218 -0
- package/dist/query/root-cause-score.d.ts.map +1 -0
- package/dist/query/root-cause-score.js +251 -0
- package/dist/query/root-cause-score.js.map +1 -0
- package/dist/query/row-closure-picture.d.ts +108 -0
- package/dist/query/row-closure-picture.d.ts.map +1 -0
- package/dist/query/row-closure-picture.js +131 -0
- package/dist/query/row-closure-picture.js.map +1 -0
- package/dist/query/similar-incidents.d.ts +76 -0
- package/dist/query/similar-incidents.d.ts.map +1 -0
- package/dist/query/similar-incidents.js +169 -0
- package/dist/query/similar-incidents.js.map +1 -0
- package/dist/query/sqlite.d.ts +13 -0
- package/dist/query/sqlite.d.ts.map +1 -0
- package/dist/query/sqlite.js +54 -0
- package/dist/query/sqlite.js.map +1 -0
- package/dist/query/traverse.d.ts +110 -0
- package/dist/query/traverse.d.ts.map +1 -0
- package/dist/query/traverse.js +0 -0
- package/dist/query/traverse.js.map +1 -0
- package/dist/query/unresolved.d.ts +195 -0
- package/dist/query/unresolved.d.ts.map +1 -0
- package/dist/query/unresolved.js +225 -0
- package/dist/query/unresolved.js.map +1 -0
- package/dist/query/verification-status.d.ts +134 -0
- package/dist/query/verification-status.d.ts.map +1 -0
- package/dist/query/verification-status.js +163 -0
- package/dist/query/verification-status.js.map +1 -0
- package/dist/recording/index.d.ts +26 -0
- package/dist/recording/index.d.ts.map +1 -0
- package/dist/recording/index.js +30 -0
- package/dist/recording/index.js.map +1 -0
- package/dist/recording/migrate.d.ts +22 -0
- package/dist/recording/migrate.d.ts.map +1 -0
- package/dist/recording/migrate.js +51 -0
- package/dist/recording/migrate.js.map +1 -0
- package/dist/recording/reader.d.ts +27 -0
- package/dist/recording/reader.d.ts.map +1 -0
- package/dist/recording/reader.js +55 -0
- package/dist/recording/reader.js.map +1 -0
- package/dist/recording/redact.d.ts +63 -0
- package/dist/recording/redact.d.ts.map +1 -0
- package/dist/recording/redact.js +98 -0
- package/dist/recording/redact.js.map +1 -0
- package/dist/recording/schema.d.ts +41 -0
- package/dist/recording/schema.d.ts.map +1 -0
- package/dist/recording/schema.js +104 -0
- package/dist/recording/schema.js.map +1 -0
- package/dist/recording/writer.d.ts +120 -0
- package/dist/recording/writer.d.ts.map +1 -0
- package/dist/recording/writer.js +81 -0
- package/dist/recording/writer.js.map +1 -0
- package/dist/scoping/fanout.d.ts +86 -0
- package/dist/scoping/fanout.d.ts.map +1 -0
- package/dist/scoping/fanout.js +95 -0
- package/dist/scoping/fanout.js.map +1 -0
- package/dist/scoping/index.d.ts +10 -0
- package/dist/scoping/index.d.ts.map +1 -0
- package/dist/scoping/index.js +6 -0
- package/dist/scoping/index.js.map +1 -0
- package/dist/scoping/score.d.ts +83 -0
- package/dist/scoping/score.d.ts.map +1 -0
- package/dist/scoping/score.js +125 -0
- package/dist/scoping/score.js.map +1 -0
- package/dist/scoping/tiers.d.ts +32 -0
- package/dist/scoping/tiers.d.ts.map +1 -0
- package/dist/scoping/tiers.js +35 -0
- package/dist/scoping/tiers.js.map +1 -0
- package/dist/scoping/traverse.d.ts +60 -0
- package/dist/scoping/traverse.d.ts.map +1 -0
- package/dist/scoping/traverse.js +172 -0
- package/dist/scoping/traverse.js.map +1 -0
- package/dist/scoping/weights.d.ts +97 -0
- package/dist/scoping/weights.d.ts.map +1 -0
- package/dist/scoping/weights.js +167 -0
- package/dist/scoping/weights.js.map +1 -0
- package/dist/sources/git/diff.d.ts +53 -0
- package/dist/sources/git/diff.d.ts.map +1 -0
- package/dist/sources/git/diff.js +140 -0
- package/dist/sources/git/diff.js.map +1 -0
- package/dist/sources/git/env.d.ts +2 -0
- package/dist/sources/git/env.d.ts.map +1 -0
- package/dist/sources/git/env.js +21 -0
- package/dist/sources/git/env.js.map +1 -0
- package/dist/sources/git/history.d.ts +140 -0
- package/dist/sources/git/history.d.ts.map +1 -0
- package/dist/sources/git/history.js +314 -0
- package/dist/sources/git/history.js.map +1 -0
- package/dist/sources/git/index.d.ts +7 -0
- package/dist/sources/git/index.d.ts.map +1 -0
- package/dist/sources/git/index.js +4 -0
- package/dist/sources/git/index.js.map +1 -0
- package/dist/sources/git/source.d.ts +95 -0
- package/dist/sources/git/source.d.ts.map +1 -0
- package/dist/sources/git/source.js +0 -0
- package/dist/sources/git/source.js.map +1 -0
- package/dist/sources/migrations/dialects.d.ts +59 -0
- package/dist/sources/migrations/dialects.d.ts.map +1 -0
- package/dist/sources/migrations/dialects.js +84 -0
- package/dist/sources/migrations/dialects.js.map +1 -0
- package/dist/sources/migrations/index.d.ts +3 -0
- package/dist/sources/migrations/index.d.ts.map +1 -0
- package/dist/sources/migrations/index.js +2 -0
- package/dist/sources/migrations/index.js.map +1 -0
- package/dist/sources/migrations/read.d.ts +113 -0
- package/dist/sources/migrations/read.d.ts.map +1 -0
- package/dist/sources/migrations/read.js +270 -0
- package/dist/sources/migrations/read.js.map +1 -0
- package/dist/sources/workspace/index.d.ts +3 -0
- package/dist/sources/workspace/index.d.ts.map +1 -0
- package/dist/sources/workspace/index.js +2 -0
- package/dist/sources/workspace/index.js.map +1 -0
- package/dist/sources/workspace/workspace.d.ts +64 -0
- package/dist/sources/workspace/workspace.d.ts.map +1 -0
- package/dist/sources/workspace/workspace.js +294 -0
- package/dist/sources/workspace/workspace.js.map +1 -0
- package/dist/store/driver/driver.d.ts +82 -0
- package/dist/store/driver/driver.d.ts.map +1 -0
- package/dist/store/driver/driver.js +31 -0
- package/dist/store/driver/driver.js.map +1 -0
- package/dist/store/driver/node-sqlite.d.ts +28 -0
- package/dist/store/driver/node-sqlite.d.ts.map +1 -0
- package/dist/store/driver/node-sqlite.js +188 -0
- package/dist/store/driver/node-sqlite.js.map +1 -0
- package/dist/store/index.d.ts +33 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +46 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/migrate.d.ts +68 -0
- package/dist/store/migrate.d.ts.map +1 -0
- package/dist/store/migrate.js +140 -0
- package/dist/store/migrate.js.map +1 -0
- package/dist/store/reader.d.ts +127 -0
- package/dist/store/reader.d.ts.map +1 -0
- package/dist/store/reader.js +269 -0
- package/dist/store/reader.js.map +1 -0
- package/dist/store/schema.d.ts +87 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +322 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/store/writer.d.ts +116 -0
- package/dist/store/writer.d.ts.map +1 -0
- package/dist/store/writer.js +279 -0
- package/dist/store/writer.js.map +1 -0
- package/dist/tiers/certify.d.ts +375 -0
- package/dist/tiers/certify.d.ts.map +1 -0
- package/dist/tiers/certify.js +434 -0
- package/dist/tiers/certify.js.map +1 -0
- package/dist/tiers/index.d.ts +5 -0
- package/dist/tiers/index.d.ts.map +1 -0
- package/dist/tiers/index.js +3 -0
- package/dist/tiers/index.js.map +1 -0
- package/dist/tiers/ladder.d.ts +73 -0
- package/dist/tiers/ladder.d.ts.map +1 -0
- package/dist/tiers/ladder.js +197 -0
- package/dist/tiers/ladder.js.map +1 -0
- package/dist/validation/config-graph.d.ts +104 -0
- package/dist/validation/config-graph.d.ts.map +1 -0
- package/dist/validation/config-graph.js +205 -0
- package/dist/validation/config-graph.js.map +1 -0
- package/dist/validation/env.d.ts +243 -0
- package/dist/validation/env.d.ts.map +1 -0
- package/dist/validation/env.js +447 -0
- package/dist/validation/env.js.map +1 -0
- package/dist/validation/index.d.ts +16 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +9 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/join-substitution.d.ts +186 -0
- package/dist/validation/join-substitution.d.ts.map +1 -0
- package/dist/validation/join-substitution.js +191 -0
- package/dist/validation/join-substitution.js.map +1 -0
- package/dist/validation/migrations.d.ts +133 -0
- package/dist/validation/migrations.d.ts.map +1 -0
- package/dist/validation/migrations.js +315 -0
- package/dist/validation/migrations.js.map +1 -0
- package/dist/validation/nodes.d.ts +32 -0
- package/dist/validation/nodes.d.ts.map +1 -0
- package/dist/validation/nodes.js +45 -0
- package/dist/validation/nodes.js.map +1 -0
- package/dist/validation/rollback.d.ts +124 -0
- package/dist/validation/rollback.d.ts.map +1 -0
- package/dist/validation/rollback.js +302 -0
- package/dist/validation/rollback.js.map +1 -0
- package/dist/validation/route-drift.d.ts +108 -0
- package/dist/validation/route-drift.d.ts.map +1 -0
- package/dist/validation/route-drift.js +103 -0
- package/dist/validation/route-drift.js.map +1 -0
- package/dist/validation/sources.d.ts +153 -0
- package/dist/validation/sources.d.ts.map +1 -0
- package/dist/validation/sources.js +409 -0
- package/dist/validation/sources.js.map +1 -0
- package/dist/worker/index.d.ts +5 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +3 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/pool.d.ts +73 -0
- package/dist/worker/pool.d.ts.map +1 -0
- package/dist/worker/pool.js +203 -0
- package/dist/worker/pool.js.map +1 -0
- package/dist/worker/protocol.d.ts +91 -0
- package/dist/worker/protocol.d.ts.map +1 -0
- package/dist/worker/protocol.js +19 -0
- package/dist/worker/protocol.js.map +1 -0
- package/dist/worker/traversal.worker.d.ts +25 -0
- package/dist/worker/traversal.worker.d.ts.map +1 -0
- package/dist/worker/traversal.worker.js +124 -0
- package/dist/worker/traversal.worker.js.map +1 -0
- package/package.json +29 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The impact score — §12.4.
|
|
3
|
+
*
|
|
4
|
+
* NewScore = ParentScore × EdgeWeight × ChangeCouplingFactor
|
|
5
|
+
* × IncidentCorrelationFactor × EdgeConfidence
|
|
6
|
+
*
|
|
7
|
+
* Both history factors **default to 1.0** so a brand-new file, or a repo with no
|
|
8
|
+
* logged incidents, is not unfairly under-scored. `EdgeConfidence` defaults to
|
|
9
|
+
* the edge's own value, which is 1.0 for a well-resolved adapter — so in a clean
|
|
10
|
+
* R3 repository the last three terms vanish and the formula is just weight
|
|
11
|
+
* decay. They do their work in a degraded one, which is the point.
|
|
12
|
+
*
|
|
13
|
+
* **The formula is applied in two parts, not as one product per hop.** Path
|
|
14
|
+
* decay uses only `EdgeWeight × EdgeConfidence`, which is strictly below 1 and
|
|
15
|
+
* therefore always terminates; the two history factors are applied once to a
|
|
16
|
+
* node's final score. See the long note in `weights.ts` for why the literal
|
|
17
|
+
* per-hop form cannot hold alongside §12.3 and §12.7, and DEC-037 for the
|
|
18
|
+
* decision. The short version: compounding a node's incident history at every
|
|
19
|
+
* hop of a path that merely passes through it counts the same evidence
|
|
20
|
+
* repeatedly.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Per-node history factors, computed once and cached.
|
|
24
|
+
*
|
|
25
|
+
* Both are looked up on the node being *entered*, not on the edge: "this file
|
|
26
|
+
* has been involved in incidents" and "this file moves with the change" are
|
|
27
|
+
* properties of the destination, and computing them per edge would multiply the
|
|
28
|
+
* same boost in twice for a node reached two ways.
|
|
29
|
+
*/
|
|
30
|
+
export class HistoryFactors {
|
|
31
|
+
#provider;
|
|
32
|
+
#config;
|
|
33
|
+
#seeds;
|
|
34
|
+
#coupling = new Map();
|
|
35
|
+
#incident = new Map();
|
|
36
|
+
constructor(provider, config, seeds) {
|
|
37
|
+
this.#provider = provider;
|
|
38
|
+
this.#config = config;
|
|
39
|
+
this.#seeds = seeds;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* §12.4's `ChangeCouplingFactor`, from `CHANGES_WITH` edges (§11.7).
|
|
43
|
+
*
|
|
44
|
+
* Boosts a node that historically moves with something in the diff. Two things
|
|
45
|
+
* make this narrower than it first appears, and both are honest limits rather
|
|
46
|
+
* than bugs:
|
|
47
|
+
*
|
|
48
|
+
* - `CHANGES_WITH` is **file-to-file**, so this only fires for `FILE` nodes.
|
|
49
|
+
* A `FUNCTION` never has co-change history of its own.
|
|
50
|
+
* - The edge is stored in one canonical direction (DEC-036), so both
|
|
51
|
+
* directions must be read. This is the one edge type where `outgoing` alone
|
|
52
|
+
* is wrong.
|
|
53
|
+
*/
|
|
54
|
+
couplingFor(nodeId) {
|
|
55
|
+
const cached = this.#coupling.get(nodeId);
|
|
56
|
+
if (cached !== undefined)
|
|
57
|
+
return cached;
|
|
58
|
+
const types = ["CHANGES_WITH"];
|
|
59
|
+
const edges = [
|
|
60
|
+
...this.#provider.outgoing(nodeId, { types }),
|
|
61
|
+
...this.#provider.incoming(nodeId, { types }),
|
|
62
|
+
];
|
|
63
|
+
let best = 0;
|
|
64
|
+
for (const edge of edges) {
|
|
65
|
+
const other = edge.from === nodeId ? edge.to : edge.from;
|
|
66
|
+
if (!this.#seeds.has(other))
|
|
67
|
+
continue;
|
|
68
|
+
// The stored confidence is already clamped to R0's ceiling, so it lands in
|
|
69
|
+
// [0, 0.5]; doubling maps it back onto [0, 1] as a coupling *strength*.
|
|
70
|
+
best = Math.max(best, Math.min(1, edge.confidence * 2));
|
|
71
|
+
}
|
|
72
|
+
const factor = 1 + best * (this.#config.maxCouplingFactor - 1);
|
|
73
|
+
this.#coupling.set(nodeId, factor);
|
|
74
|
+
return factor;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* §12.4's `IncidentCorrelationFactor`, from `INCIDENT_CORRELATED` edges (§11.6).
|
|
78
|
+
*
|
|
79
|
+
* Saturating rather than linear: the third incident on a file says much less
|
|
80
|
+
* than the first, and an unbounded count would let one pathological file
|
|
81
|
+
* dominate every blast radius it appears in.
|
|
82
|
+
*/
|
|
83
|
+
incidentFor(nodeId) {
|
|
84
|
+
const cached = this.#incident.get(nodeId);
|
|
85
|
+
if (cached !== undefined)
|
|
86
|
+
return cached;
|
|
87
|
+
const count = this.#provider.incoming(nodeId, { types: ["INCIDENT_CORRELATED"] }).length;
|
|
88
|
+
const saturated = count === 0 ? 0 : count / (count + 2);
|
|
89
|
+
const factor = 1 + saturated * (this.#config.maxIncidentFactor - 1);
|
|
90
|
+
this.#incident.set(nodeId, factor);
|
|
91
|
+
return factor;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The per-hop decay: `EdgeWeight × EdgeConfidence`, and nothing else.
|
|
96
|
+
*
|
|
97
|
+
* Strictly below 1 for any valid configuration, so the path score decreases
|
|
98
|
+
* monotonically and traversal terminates on any graph, cyclic or not. That is
|
|
99
|
+
* the whole termination argument, and it does not depend on what the history
|
|
100
|
+
* factors happen to be — which is what makes it a guarantee rather than a hope.
|
|
101
|
+
*/
|
|
102
|
+
export function hopDecay(edge, config) {
|
|
103
|
+
return config.edgeWeights[edge.type] * edge.confidence;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* A node's final score: its best path decay, adjusted once by its own history.
|
|
107
|
+
*
|
|
108
|
+
* Capped at `maxScore` because §12.7 tiers on absolute thresholds — a boosted
|
|
109
|
+
* score above 1.0 would put a two-hop node above the diff itself.
|
|
110
|
+
*/
|
|
111
|
+
export function finalScore(pathScore, nodeId, config, history) {
|
|
112
|
+
const boosted = pathScore * history.couplingFor(nodeId) * history.incidentFor(nodeId);
|
|
113
|
+
return Math.min(boosted, config.maxScore);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The largest amount any node's score can be raised by history.
|
|
117
|
+
*
|
|
118
|
+
* Pruning happens on the *unboosted* path score, so the prune point must be
|
|
119
|
+
* lowered by this much — otherwise a node that would clear the threshold once
|
|
120
|
+
* boosted is discarded before its boost is ever computed.
|
|
121
|
+
*/
|
|
122
|
+
export function maxBoost(config) {
|
|
123
|
+
return config.maxCouplingFactor * config.maxIncidentFactor;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"score.js","sourceRoot":"","sources":["../../src/scoping/score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope tiers — §12.7.
|
|
3
|
+
*
|
|
4
|
+
* | Tier | Criteria | Treatment |
|
|
5
|
+
* | --- | --- | --- |
|
|
6
|
+
* | 1 | Directly changed (the diff itself) | Always included |
|
|
7
|
+
* | 2 | Impact score > ~0.7 | Included |
|
|
8
|
+
* | 3 | Impact score ~0.3–0.7 | Included if budget allows |
|
|
9
|
+
* | 4 | Impact score < ~0.3 | Dropped under budget pressure |
|
|
10
|
+
*/
|
|
11
|
+
export type ScopeTier = 1 | 2 | 3 | 4;
|
|
12
|
+
export declare const TIER_THRESHOLDS: {
|
|
13
|
+
readonly tier2: 0.7;
|
|
14
|
+
readonly tier3: 0.3;
|
|
15
|
+
};
|
|
16
|
+
export declare function tierFor(score: number, isSeed: boolean): ScopeTier;
|
|
17
|
+
/**
|
|
18
|
+
* Node budgets by trigger — DEC-007 / §12.6 for pre-push/pr/nightly.
|
|
19
|
+
* `ide-hover` added P28: architecture §7.5 calls hover out as needing the
|
|
20
|
+
* tightest budget of the seven AI-layer triggers but never gives a figure —
|
|
21
|
+
* no DEC, no blueprint number, unlike the other three. 50 is a deliberate
|
|
22
|
+
* starting value (a quarter of pre-push's 200), not a measurement; tune from
|
|
23
|
+
* data like the rest.
|
|
24
|
+
*/
|
|
25
|
+
export declare const NODE_BUDGETS: {
|
|
26
|
+
readonly "pre-push": 200;
|
|
27
|
+
readonly "ide-hover": 50;
|
|
28
|
+
readonly pr: 1000;
|
|
29
|
+
readonly nightly: 5000;
|
|
30
|
+
};
|
|
31
|
+
export type Trigger = keyof typeof NODE_BUDGETS;
|
|
32
|
+
//# sourceMappingURL=tiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiers.d.ts","sourceRoot":"","sources":["../../src/scoping/tiers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEtC,eAAO,MAAM,eAAe;;;CAAsC,CAAC;AAEnE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,SAAS,CAKjE;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY;;;;;CAKf,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,YAAY,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope tiers — §12.7.
|
|
3
|
+
*
|
|
4
|
+
* | Tier | Criteria | Treatment |
|
|
5
|
+
* | --- | --- | --- |
|
|
6
|
+
* | 1 | Directly changed (the diff itself) | Always included |
|
|
7
|
+
* | 2 | Impact score > ~0.7 | Included |
|
|
8
|
+
* | 3 | Impact score ~0.3–0.7 | Included if budget allows |
|
|
9
|
+
* | 4 | Impact score < ~0.3 | Dropped under budget pressure |
|
|
10
|
+
*/
|
|
11
|
+
export const TIER_THRESHOLDS = { tier2: 0.7, tier3: 0.3 };
|
|
12
|
+
export function tierFor(score, isSeed) {
|
|
13
|
+
if (isSeed)
|
|
14
|
+
return 1;
|
|
15
|
+
if (score > TIER_THRESHOLDS.tier2)
|
|
16
|
+
return 2;
|
|
17
|
+
if (score >= TIER_THRESHOLDS.tier3)
|
|
18
|
+
return 3;
|
|
19
|
+
return 4;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Node budgets by trigger — DEC-007 / §12.6 for pre-push/pr/nightly.
|
|
23
|
+
* `ide-hover` added P28: architecture §7.5 calls hover out as needing the
|
|
24
|
+
* tightest budget of the seven AI-layer triggers but never gives a figure —
|
|
25
|
+
* no DEC, no blueprint number, unlike the other three. 50 is a deliberate
|
|
26
|
+
* starting value (a quarter of pre-push's 200), not a measurement; tune from
|
|
27
|
+
* data like the rest.
|
|
28
|
+
*/
|
|
29
|
+
export const NODE_BUDGETS = {
|
|
30
|
+
"pre-push": 200,
|
|
31
|
+
"ide-hover": 50,
|
|
32
|
+
pr: 1_000,
|
|
33
|
+
nightly: 5_000,
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=tiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tiers.js","sourceRoot":"","sources":["../../src/scoping/tiers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAW,CAAC;AAEnE,MAAM,UAAU,OAAO,CAAC,KAAa,EAAE,MAAe;IACpD,IAAI,MAAM;QAAE,OAAO,CAAC,CAAC;IACrB,IAAI,KAAK,GAAG,eAAe,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IAC5C,IAAI,KAAK,IAAI,eAAe,CAAC,KAAK;QAAE,OAAO,CAAC,CAAC;IAC7C,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,UAAU,EAAE,GAAG;IACf,WAAW,EAAE,EAAE;IACf,EAAE,EAAE,KAAK;IACT,OAAO,EAAE,KAAK;CACN,CAAC"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff scoping — §12.5's algorithm.
|
|
3
|
+
*
|
|
4
|
+
* Seeds are the changed nodes at score 1.0. Expansion is best-first on score and
|
|
5
|
+
* stops when the score falls below the threshold — **never on hop count**, which
|
|
6
|
+
* is the entire argument of §12.2: a delete reaching a payment gateway four hops
|
|
7
|
+
* away matters, a logging import one hop away does not, and hop-count scoping
|
|
8
|
+
* cannot tell them apart.
|
|
9
|
+
*
|
|
10
|
+
* ## Tier 1 is budget-exempt, and reported separately
|
|
11
|
+
*
|
|
12
|
+
* The diff itself is not a *result* of scoping, it is its input. A PR touching
|
|
13
|
+
* 1,200 files would otherwise consume the entire 1,000-node PR budget on tier 1
|
|
14
|
+
* alone and report zero impact — the answer would be least useful exactly when
|
|
15
|
+
* the change is largest. Seeds are therefore always included and never counted
|
|
16
|
+
* against the budget, and `budgetUsed` reports only what expansion spent.
|
|
17
|
+
*
|
|
18
|
+
* ## Pruning happens on the unboosted score
|
|
19
|
+
*
|
|
20
|
+
* A node's history boost is applied to its final score, not per hop (DEC-037),
|
|
21
|
+
* so expansion cannot know a node's boosted score until it has been reached. The
|
|
22
|
+
* prune point is therefore lowered by the largest boost the configuration
|
|
23
|
+
* permits — otherwise a node that *would* clear the threshold once boosted is
|
|
24
|
+
* discarded before its boost is ever computed. Admissible, in the A* sense: it
|
|
25
|
+
* can keep a node that later scores below threshold, never drop one that would
|
|
26
|
+
* have scored above it.
|
|
27
|
+
*/
|
|
28
|
+
import type { IREdge, IRNode } from "@descryy/ir";
|
|
29
|
+
import type { AdjacencyProvider, QueryResult } from "../query/provider.ts";
|
|
30
|
+
import { type ScopeTier, type Trigger } from "./tiers.ts";
|
|
31
|
+
import { type ScopingConfig } from "./weights.ts";
|
|
32
|
+
export interface ScopedNode {
|
|
33
|
+
readonly node: IRNode;
|
|
34
|
+
readonly score: number;
|
|
35
|
+
readonly tier: ScopeTier;
|
|
36
|
+
readonly depth: number;
|
|
37
|
+
/** The edge on the best-known path to this node. Null for a seed. */
|
|
38
|
+
readonly via: IREdge | null;
|
|
39
|
+
}
|
|
40
|
+
export interface Scope {
|
|
41
|
+
/** Tier 1: the diff itself. Always complete, never truncated. */
|
|
42
|
+
readonly changed: readonly ScopedNode[];
|
|
43
|
+
/** Tiers 2–4, ranked by score. Truncated under budget pressure. */
|
|
44
|
+
readonly affected: readonly ScopedNode[];
|
|
45
|
+
/** Nodes expansion reached but the budget dropped, highest-scoring first. */
|
|
46
|
+
readonly dropped: readonly ScopedNode[];
|
|
47
|
+
readonly budget: number;
|
|
48
|
+
readonly budgetUsed: number;
|
|
49
|
+
}
|
|
50
|
+
export interface ScopeOptions {
|
|
51
|
+
readonly trigger?: Trigger;
|
|
52
|
+
/** Overrides the trigger's budget. Mostly for tests and the nightly sweep. */
|
|
53
|
+
readonly budget?: number;
|
|
54
|
+
readonly config?: ScopingConfig;
|
|
55
|
+
readonly maxDepth?: number;
|
|
56
|
+
/** Cap on edges expanded per node. Guards hubs with in-degree in the thousands. */
|
|
57
|
+
readonly perNodeLimit?: number;
|
|
58
|
+
}
|
|
59
|
+
export declare function scopeChange(provider: AdjacencyProvider, changedIds: readonly string[], options?: ScopeOptions): QueryResult<Scope>;
|
|
60
|
+
//# sourceMappingURL=traverse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traverse.d.ts","sourceRoot":"","sources":["../../src/scoping/traverse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAY,MAAM,EAAE,MAAM,EAAmB,MAAM,aAAa,CAAC;AAE7E,OAAO,KAAK,EAAE,iBAAiB,EAAc,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAGvF,OAAO,EAAyB,KAAK,SAAS,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACjF,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,KAAK;IACpB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IACxC,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,SAAS,UAAU,EAAE,CAAC;IACzC,6EAA6E;IAC7E,QAAQ,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,mFAAmF;IACnF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AASD,wBAAgB,WAAW,CACzB,QAAQ,EAAE,iBAAiB,EAC3B,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,OAAO,GAAE,YAAiB,GACzB,WAAW,CAAC,KAAK,CAAC,CAuJpB"}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diff scoping — §12.5's algorithm.
|
|
3
|
+
*
|
|
4
|
+
* Seeds are the changed nodes at score 1.0. Expansion is best-first on score and
|
|
5
|
+
* stops when the score falls below the threshold — **never on hop count**, which
|
|
6
|
+
* is the entire argument of §12.2: a delete reaching a payment gateway four hops
|
|
7
|
+
* away matters, a logging import one hop away does not, and hop-count scoping
|
|
8
|
+
* cannot tell them apart.
|
|
9
|
+
*
|
|
10
|
+
* ## Tier 1 is budget-exempt, and reported separately
|
|
11
|
+
*
|
|
12
|
+
* The diff itself is not a *result* of scoping, it is its input. A PR touching
|
|
13
|
+
* 1,200 files would otherwise consume the entire 1,000-node PR budget on tier 1
|
|
14
|
+
* alone and report zero impact — the answer would be least useful exactly when
|
|
15
|
+
* the change is largest. Seeds are therefore always included and never counted
|
|
16
|
+
* against the budget, and `budgetUsed` reports only what expansion spent.
|
|
17
|
+
*
|
|
18
|
+
* ## Pruning happens on the unboosted score
|
|
19
|
+
*
|
|
20
|
+
* A node's history boost is applied to its final score, not per hop (DEC-037),
|
|
21
|
+
* so expansion cannot know a node's boosted score until it has been reached. The
|
|
22
|
+
* prune point is therefore lowered by the largest boost the configuration
|
|
23
|
+
* permits — otherwise a node that *would* clear the threshold once boosted is
|
|
24
|
+
* discarded before its boost is ever computed. Admissible, in the A* sense: it
|
|
25
|
+
* can keep a node that later scores below threshold, never drop one that would
|
|
26
|
+
* have scored above it.
|
|
27
|
+
*/
|
|
28
|
+
import { attenuationFor } from "./fanout.js";
|
|
29
|
+
import { finalScore, HistoryFactors, hopDecay, maxBoost } from "./score.js";
|
|
30
|
+
import { NODE_BUDGETS, tierFor } from "./tiers.js";
|
|
31
|
+
import { DEFAULT_SCOPING_CONFIG, ScopingConfigError, validateScopingConfig, } from "./weights.js";
|
|
32
|
+
/**
|
|
33
|
+
* `CHANGES_WITH` is stored in one canonical direction (DEC-036), so scoping must
|
|
34
|
+
* read it both ways. Every other edge type is genuinely directional: impact
|
|
35
|
+
* flows from a changed callee to its callers, not the reverse.
|
|
36
|
+
*/
|
|
37
|
+
const UNDIRECTED = new Set(["CHANGES_WITH"]);
|
|
38
|
+
export function scopeChange(provider, changedIds, options = {}) {
|
|
39
|
+
const config = options.config ?? DEFAULT_SCOPING_CONFIG;
|
|
40
|
+
// Refuse to start rather than fail mid-traversal — DEC-028.
|
|
41
|
+
validateScopingConfig(config);
|
|
42
|
+
// DEC-067: `perNodeLimit` truncates the very count attenuation is derived
|
|
43
|
+
// from, so a limit at or below the last finite boundary would put a hub in a
|
|
44
|
+
// gentler bucket than it belongs in and under-attenuate it. Refused rather
|
|
45
|
+
// than silently mis-bucketed.
|
|
46
|
+
const finite = config.fanOutBuckets.filter((b) => Number.isFinite(b.upTo));
|
|
47
|
+
const lastBoundary = finite.length === 0 ? 0 : finite[finite.length - 1].upTo;
|
|
48
|
+
if (options.perNodeLimit !== undefined && options.perNodeLimit <= lastBoundary) {
|
|
49
|
+
throw new ScopingConfigError(`perNodeLimit ${options.perNodeLimit} is at or below the last fan-out boundary ` +
|
|
50
|
+
`(${lastBoundary}), so a hub's fan-out would be truncated to the limit and scored in a ` +
|
|
51
|
+
`gentler bucket than it belongs in (DEC-067). Raise the limit above ${lastBoundary}.`);
|
|
52
|
+
}
|
|
53
|
+
const trigger = options.trigger ?? "pr";
|
|
54
|
+
const budget = options.budget ?? NODE_BUDGETS[trigger];
|
|
55
|
+
const maxDepth = options.maxDepth ?? Number.POSITIVE_INFINITY;
|
|
56
|
+
const seeds = new Set(changedIds);
|
|
57
|
+
const history = new HistoryFactors(provider, config, seeds);
|
|
58
|
+
const pruneAt = config.threshold / maxBoost(config);
|
|
59
|
+
const best = new Map();
|
|
60
|
+
const queue = [];
|
|
61
|
+
let resolutionFloor = 4;
|
|
62
|
+
for (const id of changedIds) {
|
|
63
|
+
best.set(id, { score: 1, depth: 0, via: null });
|
|
64
|
+
queue.push({ id, score: 1, depth: 0 });
|
|
65
|
+
}
|
|
66
|
+
// Best-first. A binary heap would be faster, but expansion is bounded by the
|
|
67
|
+
// score threshold rather than by the queue, so the queue stays small in
|
|
68
|
+
// practice and a sorted pop keeps the algorithm readable.
|
|
69
|
+
while (queue.length > 0) {
|
|
70
|
+
queue.sort((a, b) => b.score - a.score);
|
|
71
|
+
const current = queue.shift();
|
|
72
|
+
const known = best.get(current.id);
|
|
73
|
+
if (known === undefined || known.score > current.score)
|
|
74
|
+
continue; // stale entry
|
|
75
|
+
if (current.depth >= maxDepth)
|
|
76
|
+
continue;
|
|
77
|
+
// Impact flows to the *dependents* of a changed node: what breaks if this
|
|
78
|
+
// changes is its callers, not its callees.
|
|
79
|
+
const adjacency = options.perNodeLimit === undefined ? {} : { limit: options.perNodeLimit };
|
|
80
|
+
const candidates = provider
|
|
81
|
+
.incoming(current.id, adjacency)
|
|
82
|
+
.map((edge) => ({ edge, next: edge.from }));
|
|
83
|
+
for (const edge of provider.outgoing(current.id, adjacency)) {
|
|
84
|
+
// Only the undirected types expand outward as well; otherwise a changed
|
|
85
|
+
// node would report everything it depends on as affected by it.
|
|
86
|
+
if (UNDIRECTED.has(edge.type))
|
|
87
|
+
candidates.push({ edge, next: edge.to });
|
|
88
|
+
}
|
|
89
|
+
// DEC-067. `candidates.length` is this node's operational fan-out — the
|
|
90
|
+
// number of ways expansion can explode out of it — and it is already in
|
|
91
|
+
// hand, so no index or second query is needed. The seed exemption lives
|
|
92
|
+
// inside `attenuationFor`: a hub that was *changed* expands at full
|
|
93
|
+
// strength, a hub that was merely *reached* does not.
|
|
94
|
+
const attenuation = attenuationFor(candidates.length, current.depth, config.fanOutBuckets);
|
|
95
|
+
for (const { edge, next } of candidates) {
|
|
96
|
+
// Attenuation can only shrink a score, so `pruneAt` stays admissible —
|
|
97
|
+
// nothing that could clear the threshold once boosted is discarded here
|
|
98
|
+
// that would not have been discarded before.
|
|
99
|
+
const score = current.score * hopDecay(edge, config) * attenuation;
|
|
100
|
+
// §12.5: prune this path. Lowered by the maximum boost, so nothing that
|
|
101
|
+
// could clear the threshold once boosted is discarded unseen.
|
|
102
|
+
if (score < pruneAt)
|
|
103
|
+
continue;
|
|
104
|
+
const existing = best.get(next);
|
|
105
|
+
if (existing !== undefined && existing.score >= score)
|
|
106
|
+
continue;
|
|
107
|
+
if (edge.resolution < resolutionFloor)
|
|
108
|
+
resolutionFloor = edge.resolution;
|
|
109
|
+
best.set(next, { score, depth: current.depth + 1, via: edge });
|
|
110
|
+
queue.push({ id: next, score, depth: current.depth + 1 });
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// --- assemble ------------------------------------------------------------
|
|
114
|
+
const changed = [];
|
|
115
|
+
const expanded = [];
|
|
116
|
+
for (const [id, entry] of best) {
|
|
117
|
+
const node = provider.node(id);
|
|
118
|
+
if (node === undefined)
|
|
119
|
+
continue;
|
|
120
|
+
const isSeed = seeds.has(id);
|
|
121
|
+
const score = isSeed ? 1 : finalScore(entry.score, id, config, history);
|
|
122
|
+
// A node kept by the lowered prune point that does not clear the real
|
|
123
|
+
// threshold once boosted was correctly explored and is correctly discarded.
|
|
124
|
+
if (!isSeed && score < config.threshold)
|
|
125
|
+
continue;
|
|
126
|
+
const scoped = {
|
|
127
|
+
node,
|
|
128
|
+
score,
|
|
129
|
+
tier: tierFor(score, isSeed),
|
|
130
|
+
depth: entry.depth,
|
|
131
|
+
via: entry.via,
|
|
132
|
+
};
|
|
133
|
+
(isSeed ? changed : expanded).push(scoped);
|
|
134
|
+
}
|
|
135
|
+
// §12.6: when the cap is hit, keep the highest-impact nodes and drop the rest.
|
|
136
|
+
// Ranked by score, then id, so truncation is deterministic rather than
|
|
137
|
+
// dependent on the order the map happened to fill.
|
|
138
|
+
expanded.sort((a, b) => b.score - a.score || a.node.id.localeCompare(b.node.id));
|
|
139
|
+
changed.sort((a, b) => a.node.id.localeCompare(b.node.id));
|
|
140
|
+
const affected = expanded.slice(0, budget);
|
|
141
|
+
const dropped = expanded.slice(budget);
|
|
142
|
+
const notes = [];
|
|
143
|
+
if (dropped.length > 0) {
|
|
144
|
+
const worstKept = affected.at(-1)?.score ?? 0;
|
|
145
|
+
notes.push(`The ${trigger} budget of ${budget} nodes was reached: ${dropped.length} lower-impact ` +
|
|
146
|
+
`node(s) were dropped, all scoring at or below ${worstKept.toFixed(3)}. They were not ` +
|
|
147
|
+
`examined and are not reported as clean.`);
|
|
148
|
+
}
|
|
149
|
+
if (resolutionFloor <= 1) {
|
|
150
|
+
notes.push(`This blast radius rests on edges resolved no better than R${resolutionFloor}. Cross-file ` +
|
|
151
|
+
"calls are not reliably resolved at that level, so the radius is a lower bound.");
|
|
152
|
+
}
|
|
153
|
+
if (changed.length > budget) {
|
|
154
|
+
notes.push(`The diff itself contains ${changed.length} changed node(s), more than the ${budget}-node ` +
|
|
155
|
+
"budget. Tier 1 is reported in full and does not consume the budget, so impact analysis " +
|
|
156
|
+
"still ran.");
|
|
157
|
+
}
|
|
158
|
+
const stamp = { ...provider.baseStamp(), resolutionFloor };
|
|
159
|
+
return {
|
|
160
|
+
data: {
|
|
161
|
+
changed,
|
|
162
|
+
affected,
|
|
163
|
+
dropped,
|
|
164
|
+
budget,
|
|
165
|
+
budgetUsed: affected.length,
|
|
166
|
+
},
|
|
167
|
+
stamp,
|
|
168
|
+
truncated: dropped.length > 0,
|
|
169
|
+
notes,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=traverse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traverse.js","sourceRoot":"","sources":["../../src/scoping/traverse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scoring configuration for diff scoping — §12.4.
|
|
3
|
+
*
|
|
4
|
+
* ## The one number in the architecture that cannot be used as written
|
|
5
|
+
*
|
|
6
|
+
* §12.3's worked example decays a `CALLS` edge by **×1.0** on its first hop and
|
|
7
|
+
* by **×0.95** on its third. The 0.95 is the usable one; 1.0 is not, for two
|
|
8
|
+
* independent reasons:
|
|
9
|
+
*
|
|
10
|
+
* 1. **It breaks the premise of the whole engine.** §12.2 argues that hop count
|
|
11
|
+
* is a poor proxy for importance and that expansion must stop on *score*. An
|
|
12
|
+
* edge weight of 1.0 means a `CALLS` chain never decays — every node reachable
|
|
13
|
+
* by calls scores exactly 1.0 forever, so the stopping rule degenerates into
|
|
14
|
+
* "stop when the budget runs out", which is hop-count scoping wearing a
|
|
15
|
+
* different name.
|
|
16
|
+
* 2. **It does not terminate.** With `EdgeConfidence` at 1.0 for an R3 edge, the
|
|
17
|
+
* hop factor is exactly 1.0, and on a `CALLS` cycle the traversal never ends.
|
|
18
|
+
* DEC-028 requires the factor to be strictly inside (0, 1).
|
|
19
|
+
*
|
|
20
|
+
* The defaults below therefore use 0.95, and §12.3's stated outcome still holds:
|
|
21
|
+
* the payment gateway three hops out survives and `logger.ts` two hops out is
|
|
22
|
+
* dropped. That test discriminates on the *shape* of the decay, not on whether
|
|
23
|
+
* the first hop was 1.00 or 0.95.
|
|
24
|
+
*
|
|
25
|
+
* ## Three parts of the architecture cannot all be true — and the resolution
|
|
26
|
+
*
|
|
27
|
+
* Writing this file surfaced a genuine inconsistency between §12.3, §12.4 and
|
|
28
|
+
* §12.7. Any two can hold; all three cannot.
|
|
29
|
+
*
|
|
30
|
+
* - **§12.3** needs *high* weights. Its worked example decays `CALLS` and
|
|
31
|
+
* `PROPAGATES_TO` at ~0.95 so a three-hop chain still scores 0.90.
|
|
32
|
+
* - **§12.7** tiers on *absolute* thresholds — tier 2 above 0.7, tier 4 below
|
|
33
|
+
* 0.3 — which are only meaningful if a few hops of a strong edge stay high.
|
|
34
|
+
* Scale the weights down for headroom and the payment gateway §12.3 calls
|
|
35
|
+
* critical lands in tier 4, "dropped under budget pressure".
|
|
36
|
+
* - **§12.4** multiplies by `ChangeCouplingFactor` and
|
|
37
|
+
* `IncidentCorrelationFactor` *at every hop*, and both are boosts above 1.0.
|
|
38
|
+
* A weight of 0.95 with any coupling above 1.053 makes the hop factor exceed
|
|
39
|
+
* 1.0, at which point the score stops decaying and a cycle never terminates.
|
|
40
|
+
*
|
|
41
|
+
* **Resolution: the history factors adjust a node's score once, they do not
|
|
42
|
+
* compound per hop.** Path decay is the product of `edgeWeight × confidence`
|
|
43
|
+
* over the hops, strictly below 1 at every step, so termination is structural.
|
|
44
|
+
* Coupling and incident are then applied to the node's final score.
|
|
45
|
+
*
|
|
46
|
+
* This keeps §12.3's numbers exactly, keeps §12.7's thresholds meaningful, and
|
|
47
|
+
* keeps §12.4's terms doing the work they were introduced for. It is also more
|
|
48
|
+
* defensible than the literal formula: compounding a node's incident history at
|
|
49
|
+
* every hop of a path that merely *passes through* it counts the same evidence
|
|
50
|
+
* repeatedly, which the multiplicative form does silently. See DEC-037.
|
|
51
|
+
*/
|
|
52
|
+
import { type EdgeType } from "@descryy/ir";
|
|
53
|
+
import { type FanOutBucket } from "./fanout.ts";
|
|
54
|
+
export interface ScopingConfig {
|
|
55
|
+
/** Per-type decay, §12.4's `EdgeWeight`. Never per-edge — see DEC-021 correction 2. */
|
|
56
|
+
readonly edgeWeights: Readonly<Record<EdgeType, number>>;
|
|
57
|
+
/** Ceiling on §12.4's `ChangeCouplingFactor`. 1.0 disables the boost. */
|
|
58
|
+
readonly maxCouplingFactor: number;
|
|
59
|
+
/** Ceiling on §12.4's `IncidentCorrelationFactor`. 1.0 disables the boost. */
|
|
60
|
+
readonly maxIncidentFactor: number;
|
|
61
|
+
/**
|
|
62
|
+
* Ceiling on a node's final boosted score. Scores are compared against §12.7's
|
|
63
|
+
* absolute tier thresholds, so they must stay on the same scale as an
|
|
64
|
+
* unboosted one.
|
|
65
|
+
*/
|
|
66
|
+
readonly maxScore: number;
|
|
67
|
+
/** §12.6: expansion halts once the score falls below this. Not a hop count. */
|
|
68
|
+
readonly threshold: number;
|
|
69
|
+
/**
|
|
70
|
+
* DEC-067's fan-out attenuation table, applied to expansion out of a hub that
|
|
71
|
+
* traversal *arrived at* — never to a seed, and never to an edge's weight.
|
|
72
|
+
*/
|
|
73
|
+
readonly fanOutBuckets: readonly FanOutBucket[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Default weights.
|
|
77
|
+
*
|
|
78
|
+
* **Tuning, not architecture.** §12 is marked `DECIDED, TUNING OPEN` and §32
|
|
79
|
+
* items 1 and 4 list edge weights as values to set from real traversal data.
|
|
80
|
+
* These are starting points chosen to reproduce §12.3's example, and the three
|
|
81
|
+
* that matter are the ones that example names: `CALLS`, `PROPAGATES_TO` and
|
|
82
|
+
* `IMPORTS`.
|
|
83
|
+
*/
|
|
84
|
+
export declare const DEFAULT_EDGE_WEIGHTS: Readonly<Record<EdgeType, number>>;
|
|
85
|
+
export declare const DEFAULT_SCOPING_CONFIG: ScopingConfig;
|
|
86
|
+
export declare class ScopingConfigError extends Error {
|
|
87
|
+
readonly name = "ScopingConfigError";
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Refuse a configuration that could fail to terminate — DEC-028's config-load half.
|
|
91
|
+
*
|
|
92
|
+
* The check is per edge type because the failure is: one type's weight, boosted
|
|
93
|
+
* to its declared maxima, reaching 1.0. A global average would hide exactly the
|
|
94
|
+
* case that matters.
|
|
95
|
+
*/
|
|
96
|
+
export declare function validateScopingConfig(config: ScopingConfig): void;
|
|
97
|
+
//# sourceMappingURL=weights.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weights.d.ts","sourceRoot":"","sources":["../../src/scoping/weights.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAEH,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,aAAa,CAAC;AAExD,OAAO,EAA0C,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAExF,MAAM,WAAW,aAAa;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACzD,yEAAyE;IACzE,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,aAAa,EAAE,SAAS,YAAY,EAAE,CAAC;CACjD;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CA2BnE,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,aAUpC,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAkB,IAAI,wBAAwB;CAC/C;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAmEjE"}
|