@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,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RootCauseScore` — architecture §18.3a, the deterministic ranking half.
|
|
3
|
+
*
|
|
4
|
+
* ```
|
|
5
|
+
* RootCauseScore = RuntimeEvidence + GraphDistance + ChangeCoupling + IncidentCorrelation
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* §18.3 establishes why ranking exists at all: traversal alone returns a
|
|
9
|
+
* *path*, not a culprit. A five-hop chain has five candidate locations, and
|
|
10
|
+
* presenting all five is diagnostically equivalent to presenting none.
|
|
11
|
+
*
|
|
12
|
+
* ## Reuses three existing queries — does not re-derive them
|
|
13
|
+
*
|
|
14
|
+
* `documents/decisions-inbox/NOTE-4-correlation-graph-integration-prep.md`
|
|
15
|
+
* §1 maps three of the four terms onto queries this package already has,
|
|
16
|
+
* checked line by line against `queries.ts` rather than assumed:
|
|
17
|
+
*
|
|
18
|
+
* | Term | Query | Role here |
|
|
19
|
+
* | --- | --- | --- |
|
|
20
|
+
* | `GraphDistance` | `weightedBfs` (`impactOf`'s own underlying primitive, called directly — see below) | `direction: "out"`, walking `CALLS`/`USES_API`/`SERVES_API` from the failure point toward the deep implementation — §18.3's own worked example, and its own hop×confidence-decayed `score` is used as-is. |
|
|
21
|
+
* | `ChangeCoupling` | `changesWith` | Was this candidate, or a co-change partner of it, in the current diff. |
|
|
22
|
+
* | `IncidentCorrelation` | `rootCauseSearch` | Structural half only — inherits that query's own disclosed gap (no vector store, §8.2) unchanged. |
|
|
23
|
+
* | `RuntimeEvidence` | **none, and none should exist here** | See below. |
|
|
24
|
+
*
|
|
25
|
+
* **`weightedBfs` directly, not `impactOf`** — a deliberate, narrow deviation
|
|
26
|
+
* from "reuses, does not re-derive." `RankedRootCauseCandidate.path` (the
|
|
27
|
+
* real hop-by-hop `GraphPathHop[]` `Finding.graphPath` needs,
|
|
28
|
+
* `documents/decisions-inbox/DEC-D-NEXT-graph-path-hop-reconstruction.md`,
|
|
29
|
+
* `descry-desktop`) is reconstructed by walking `Reached.via`'s parent
|
|
30
|
+
* pointers back to the seed — the exact traversal that produced `depth`/
|
|
31
|
+
* `score`. `impactOf` discards `via` in its own `hydrate()` step before this
|
|
32
|
+
* module ever sees the result, so getting it means calling `weightedBfs`
|
|
33
|
+
* itself. The scoring math is not re-derived — `entry.score`/`entry.depth`
|
|
34
|
+
* are still `weightedBfs`'s own values, used as-is, same as before; only the
|
|
35
|
+
* thin hydrate-plus-note wrapper `impactOf` adds is now inlined here so the
|
|
36
|
+
* `via` chain survives.
|
|
37
|
+
*
|
|
38
|
+
* `RootCauseScore`'s own walk crosses the `SERVES_API` join the same way
|
|
39
|
+
* §18.3's example does: `SERVES_API` is stored route → endpoint
|
|
40
|
+
* (`queries.ts`'s own comment), so a plain `direction: "out"` walk reaches
|
|
41
|
+
* the endpoint from a caller's `USES_API` edge and stops — it never looks
|
|
42
|
+
* incoming at that one node to find the route serving it. `crossApiEndpoint`
|
|
43
|
+
* (DEC-138) is passed through for exactly this reason; without it, this
|
|
44
|
+
* module's own fixture (`InvoicePage → deleteInvoice → DELETE
|
|
45
|
+
* /invoice/:id`) would never reach `InvoiceController`/`InvoiceService` at
|
|
46
|
+
* all, and the architecture's own worked example would not walk. The join
|
|
47
|
+
* crossover means a hop's own `from`/`to` sometimes runs opposite the walk's
|
|
48
|
+
* direction (the route→endpoint edge is walked endpoint→controller) — `path`
|
|
49
|
+
* reports the edge exactly as stored, never flipped to look like a forward
|
|
50
|
+
* chain that was never actually walked that way; see `pathTo`'s own comment.
|
|
51
|
+
*
|
|
52
|
+
* ## `RuntimeEvidence` is a precondition, never a fourth addend — read this before changing anything
|
|
53
|
+
*
|
|
54
|
+
* NOTE-4 §5 settles this and `ai-layer-plan.md` §5 (G3) preserves it: a
|
|
55
|
+
* candidate with maximal `GraphDistance + ChangeCoupling + IncidentCorrelation`
|
|
56
|
+
* and zero `RuntimeEvidence` is a plausible guess ranked well, and it must
|
|
57
|
+
* **never** reach `confirmed` however the other three terms sum — because
|
|
58
|
+
* summing all four together *is* the failure mode where a well-ranked guess
|
|
59
|
+
* reads as an established fact. DEC-276 encodes the same rule one layer up
|
|
60
|
+
* as `E == 0 → unconfirmed`.
|
|
61
|
+
*
|
|
62
|
+
* So this module never computes `structuralScore + runtimeEvidence` — not
|
|
63
|
+
* as an internal step, not as a returned field, nowhere. Two design choices
|
|
64
|
+
* make this a **type constraint**, not just a documented convention (proven
|
|
65
|
+
* by a `@ts-expect-error` test in this module's test file, checked by
|
|
66
|
+
* `tsc`, not merely asserted at runtime):
|
|
67
|
+
*
|
|
68
|
+
* 1. `structuralScore: number` carries only `GraphDistance + ChangeCoupling
|
|
69
|
+
* + IncidentCorrelation`. Nothing runtime-evidence-shaped is a term in
|
|
70
|
+
* it, ever.
|
|
71
|
+
* 2. `namedByEvidence: readonly string[]` — the verbatim evidence
|
|
72
|
+
* descriptions naming this exact candidate, supplied by the caller
|
|
73
|
+
* (`descry-runtime`'s evidence-to-node resolution; NOTE-4 §2 is explicit
|
|
74
|
+
* that no graph query for this term exists or should — it is not a
|
|
75
|
+
* graph fact). A `string[]`, not a `number` or a boolean count:
|
|
76
|
+
* `structuralScore + namedByEvidence` does not typecheck, so a caller
|
|
77
|
+
* cannot casually fold it into the ranking arithmetic by accident. A
|
|
78
|
+
* caller who really wants to misuse it has to write new code to do so —
|
|
79
|
+
* this module supplies no field shaped like the thing G3 forbids
|
|
80
|
+
* summing.
|
|
81
|
+
*
|
|
82
|
+
* §18.3a's own table still calls `RuntimeEvidence` *"the strongest
|
|
83
|
+
* signal"* — a node named directly in a backend log outranks one merely on
|
|
84
|
+
* the path. That is honored **as a sort key, not a score term**:
|
|
85
|
+
* `namedByEvidence`-non-empty candidates sort before every candidate
|
|
86
|
+
* without it, and only within each of those two groups does
|
|
87
|
+
* `structuralScore` decide. Categorical partitioning, never arithmetic
|
|
88
|
+
* combination — the distinction NOTE-4 §5 draws is preserved exactly.
|
|
89
|
+
*
|
|
90
|
+
* `RootCauseScore` the architecture names is therefore not a function this
|
|
91
|
+
* module returns as one number. It ranks; a gate above this layer (G3/G4,
|
|
92
|
+
* `descry-runtime`/the pipeline) decides what `namedByEvidence` and the
|
|
93
|
+
* finding's actual evidence bundle are allowed to promote — this module
|
|
94
|
+
* does not gate and does not categorise.
|
|
95
|
+
*
|
|
96
|
+
* ## The resolution caveat (§18.3)
|
|
97
|
+
*
|
|
98
|
+
* *"At R0/R1 the tracer will lose the trail at the first cross-file call
|
|
99
|
+
* and must say 'trace stopped here' rather than presenting a truncated
|
|
100
|
+
* chain as complete."* When the traversal's own resolution floor is R0/R1,
|
|
101
|
+
* a note says so in those words — in addition to, not instead of,
|
|
102
|
+
* `impactOf`'s own disclosure, which explains *why* (cross-file calls are
|
|
103
|
+
* not reliably resolved at that level).
|
|
104
|
+
*
|
|
105
|
+
* ## Weights are starting values, not measurements
|
|
106
|
+
*
|
|
107
|
+
* `CLAUDE.md`: *"Numbers in the docs are targets or starting values, not
|
|
108
|
+
* measurements."* `DEFAULT_ROOT_CAUSE_WEIGHTS` and `INCIDENT_SATURATION`
|
|
109
|
+
* below are exactly that — picked, not measured, gathered into one named
|
|
110
|
+
* block rather than left as literals in the arithmetic so the next reader
|
|
111
|
+
* can find and tune them without reading the function body.
|
|
112
|
+
*/
|
|
113
|
+
import type { EdgeType, GraphPathHop, IRNode, ResolutionLevel } from "@descryy/ir";
|
|
114
|
+
import type { AdjacencyProvider } from "./provider.ts";
|
|
115
|
+
import { type RootCauseMatch } from "./queries.ts";
|
|
116
|
+
/** §18.3's own walk: from the failure point toward the deep implementation. */
|
|
117
|
+
export declare const ROOT_CAUSE_TRACE_EDGE_TYPES: readonly EdgeType[];
|
|
118
|
+
/**
|
|
119
|
+
* The three structural terms' multipliers. **Unmeasured starting values**
|
|
120
|
+
* (`CLAUDE.md`'s own rule) — tune from data, not intuition, once golden
|
|
121
|
+
* investigations (`ai-layer-plan.md` §9 layer 4) exist to tune against.
|
|
122
|
+
*/
|
|
123
|
+
export interface RootCauseWeights {
|
|
124
|
+
readonly graphDistance: number;
|
|
125
|
+
readonly changeCoupling: number;
|
|
126
|
+
readonly incidentCorrelation: number;
|
|
127
|
+
}
|
|
128
|
+
export declare const DEFAULT_ROOT_CAUSE_WEIGHTS: RootCauseWeights;
|
|
129
|
+
/**
|
|
130
|
+
* How many correlated incidents saturate `IncidentCorrelation`'s raw 0–1
|
|
131
|
+
* contribution. Unmeasured, same rule as the weights above: a candidate
|
|
132
|
+
* with dozens of correlated incidents should not dominate the ranking
|
|
133
|
+
* arithmetically more than one with a handful, so the raw count is capped
|
|
134
|
+
* before it is weighted.
|
|
135
|
+
*/
|
|
136
|
+
export declare const INCIDENT_SATURATION = 3;
|
|
137
|
+
/** Why a candidate's `ChangeCoupling` term is nonzero — a fact worth keeping, not just a number. */
|
|
138
|
+
export type ChangeCouplingEvidence = {
|
|
139
|
+
readonly kind: "direct";
|
|
140
|
+
} | {
|
|
141
|
+
readonly kind: "co-change";
|
|
142
|
+
readonly viaNodeId: string;
|
|
143
|
+
readonly support: number;
|
|
144
|
+
readonly confidence: number;
|
|
145
|
+
};
|
|
146
|
+
export interface RootCauseOptions {
|
|
147
|
+
readonly weights?: RootCauseWeights;
|
|
148
|
+
/** How many ranked candidates to return. Presenting every candidate is diagnostically equivalent to presenting none — §18.3a. */
|
|
149
|
+
readonly topN?: number;
|
|
150
|
+
readonly maxDepth?: number;
|
|
151
|
+
readonly budget?: number;
|
|
152
|
+
/** Node ids the current diff touches directly — `ChangeCoupling`'s "in the current diff" half. */
|
|
153
|
+
readonly changedIds?: ReadonlySet<string>;
|
|
154
|
+
/**
|
|
155
|
+
* Verbatim evidence descriptions, keyed by the exact node id they name
|
|
156
|
+
* directly — supplied by the caller, never computed here. See the module
|
|
157
|
+
* header: this is not a graph fact, and no query for it exists in this
|
|
158
|
+
* package.
|
|
159
|
+
*/
|
|
160
|
+
readonly namedByEvidence?: ReadonlyMap<string, readonly string[]>;
|
|
161
|
+
}
|
|
162
|
+
export interface RankedRootCauseCandidate {
|
|
163
|
+
readonly node: IRNode;
|
|
164
|
+
/** Hops from the failure point along the traced walk. 0 at the failure point itself. */
|
|
165
|
+
readonly depth: number;
|
|
166
|
+
/**
|
|
167
|
+
* `GraphDistance + ChangeCoupling + IncidentCorrelation`, weighted.
|
|
168
|
+
* **Never** includes `RuntimeEvidence` — see the module header.
|
|
169
|
+
*/
|
|
170
|
+
readonly structuralScore: number;
|
|
171
|
+
readonly changeCoupling: ChangeCouplingEvidence | null;
|
|
172
|
+
readonly correlatedIncidents: readonly RootCauseMatch[];
|
|
173
|
+
/**
|
|
174
|
+
* Verbatim evidence naming this exact candidate. Empty, never omitted,
|
|
175
|
+
* when none. A precondition for promotion elsewhere (NOTE-4 §5, G3,
|
|
176
|
+
* DEC-276) — never a term in `structuralScore`.
|
|
177
|
+
*/
|
|
178
|
+
readonly namedByEvidence: readonly string[];
|
|
179
|
+
/** The candidate node's own resolution — a fact stored on it directly, not re-derived. */
|
|
180
|
+
readonly resolution: ResolutionLevel;
|
|
181
|
+
/**
|
|
182
|
+
* `true` when `depth === 0` — this candidate *is* the failure point, and
|
|
183
|
+
* `resolution` above is that single node's own fact, not the minimum over
|
|
184
|
+
* any traced hop. `DEC-NEXT-empty-graph-path-resolution-floor.md` ruled
|
|
185
|
+
* that a zero-hop path caps reliability at the same class as a
|
|
186
|
+
* genuinely-but-badly-resolved one, but the two are not the same claim —
|
|
187
|
+
* this field is what lets a caller (`reliabilityNote()`'s `emptyPath`
|
|
188
|
+
* parameter, `@descryy/ir`) say which one happened, rather than
|
|
189
|
+
* inferring it from `depth === 0` at every call site. Never `true` for a
|
|
190
|
+
* deeper candidate: at least one hop was traced to reach it, however
|
|
191
|
+
* poorly that hop resolved.
|
|
192
|
+
*/
|
|
193
|
+
readonly emptyPath: boolean;
|
|
194
|
+
/**
|
|
195
|
+
* The real, walked hop-by-hop chain from the failure point to this
|
|
196
|
+
* candidate — `Finding.graphPath`'s own source. Empty exactly when
|
|
197
|
+
* `emptyPath` is `true`. Reconstructed from `weightedBfs`'s own `via`
|
|
198
|
+
* chain, not a second independent walk — see the module header.
|
|
199
|
+
*/
|
|
200
|
+
readonly path: readonly GraphPathHop[];
|
|
201
|
+
}
|
|
202
|
+
export interface RootCauseRanking {
|
|
203
|
+
/** Capped at `topN` (or fewer, if there simply weren't more candidates). */
|
|
204
|
+
readonly ranked: readonly RankedRootCauseCandidate[];
|
|
205
|
+
/** The honest denominator — every candidate the walk reached, before the `topN` cap. */
|
|
206
|
+
readonly candidateCount: number;
|
|
207
|
+
/** True when the walk's own budget stopped it short, or when `topN` dropped candidates. */
|
|
208
|
+
readonly truncated: boolean;
|
|
209
|
+
readonly notes: readonly string[];
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Rank the candidates reachable from `failurePoint` by the three structural
|
|
213
|
+
* terms of `RootCauseScore` (architecture §18.3a). See the module header
|
|
214
|
+
* for why `RuntimeEvidence` is deliberately absent from the arithmetic and
|
|
215
|
+
* present only as `namedByEvidence`, a categorical sort key.
|
|
216
|
+
*/
|
|
217
|
+
export declare function rankRootCause(provider: AdjacencyProvider, failurePoint: string, options?: RootCauseOptions): RootCauseRanking;
|
|
218
|
+
//# sourceMappingURL=root-cause-score.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-cause-score.d.ts","sourceRoot":"","sources":["../../src/query/root-cause-score.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RootCauseScore` — architecture §18.3a, the deterministic ranking half.
|
|
3
|
+
*
|
|
4
|
+
* ```
|
|
5
|
+
* RootCauseScore = RuntimeEvidence + GraphDistance + ChangeCoupling + IncidentCorrelation
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* §18.3 establishes why ranking exists at all: traversal alone returns a
|
|
9
|
+
* *path*, not a culprit. A five-hop chain has five candidate locations, and
|
|
10
|
+
* presenting all five is diagnostically equivalent to presenting none.
|
|
11
|
+
*
|
|
12
|
+
* ## Reuses three existing queries — does not re-derive them
|
|
13
|
+
*
|
|
14
|
+
* `documents/decisions-inbox/NOTE-4-correlation-graph-integration-prep.md`
|
|
15
|
+
* §1 maps three of the four terms onto queries this package already has,
|
|
16
|
+
* checked line by line against `queries.ts` rather than assumed:
|
|
17
|
+
*
|
|
18
|
+
* | Term | Query | Role here |
|
|
19
|
+
* | --- | --- | --- |
|
|
20
|
+
* | `GraphDistance` | `weightedBfs` (`impactOf`'s own underlying primitive, called directly — see below) | `direction: "out"`, walking `CALLS`/`USES_API`/`SERVES_API` from the failure point toward the deep implementation — §18.3's own worked example, and its own hop×confidence-decayed `score` is used as-is. |
|
|
21
|
+
* | `ChangeCoupling` | `changesWith` | Was this candidate, or a co-change partner of it, in the current diff. |
|
|
22
|
+
* | `IncidentCorrelation` | `rootCauseSearch` | Structural half only — inherits that query's own disclosed gap (no vector store, §8.2) unchanged. |
|
|
23
|
+
* | `RuntimeEvidence` | **none, and none should exist here** | See below. |
|
|
24
|
+
*
|
|
25
|
+
* **`weightedBfs` directly, not `impactOf`** — a deliberate, narrow deviation
|
|
26
|
+
* from "reuses, does not re-derive." `RankedRootCauseCandidate.path` (the
|
|
27
|
+
* real hop-by-hop `GraphPathHop[]` `Finding.graphPath` needs,
|
|
28
|
+
* `documents/decisions-inbox/DEC-D-NEXT-graph-path-hop-reconstruction.md`,
|
|
29
|
+
* `descry-desktop`) is reconstructed by walking `Reached.via`'s parent
|
|
30
|
+
* pointers back to the seed — the exact traversal that produced `depth`/
|
|
31
|
+
* `score`. `impactOf` discards `via` in its own `hydrate()` step before this
|
|
32
|
+
* module ever sees the result, so getting it means calling `weightedBfs`
|
|
33
|
+
* itself. The scoring math is not re-derived — `entry.score`/`entry.depth`
|
|
34
|
+
* are still `weightedBfs`'s own values, used as-is, same as before; only the
|
|
35
|
+
* thin hydrate-plus-note wrapper `impactOf` adds is now inlined here so the
|
|
36
|
+
* `via` chain survives.
|
|
37
|
+
*
|
|
38
|
+
* `RootCauseScore`'s own walk crosses the `SERVES_API` join the same way
|
|
39
|
+
* §18.3's example does: `SERVES_API` is stored route → endpoint
|
|
40
|
+
* (`queries.ts`'s own comment), so a plain `direction: "out"` walk reaches
|
|
41
|
+
* the endpoint from a caller's `USES_API` edge and stops — it never looks
|
|
42
|
+
* incoming at that one node to find the route serving it. `crossApiEndpoint`
|
|
43
|
+
* (DEC-138) is passed through for exactly this reason; without it, this
|
|
44
|
+
* module's own fixture (`InvoicePage → deleteInvoice → DELETE
|
|
45
|
+
* /invoice/:id`) would never reach `InvoiceController`/`InvoiceService` at
|
|
46
|
+
* all, and the architecture's own worked example would not walk. The join
|
|
47
|
+
* crossover means a hop's own `from`/`to` sometimes runs opposite the walk's
|
|
48
|
+
* direction (the route→endpoint edge is walked endpoint→controller) — `path`
|
|
49
|
+
* reports the edge exactly as stored, never flipped to look like a forward
|
|
50
|
+
* chain that was never actually walked that way; see `pathTo`'s own comment.
|
|
51
|
+
*
|
|
52
|
+
* ## `RuntimeEvidence` is a precondition, never a fourth addend — read this before changing anything
|
|
53
|
+
*
|
|
54
|
+
* NOTE-4 §5 settles this and `ai-layer-plan.md` §5 (G3) preserves it: a
|
|
55
|
+
* candidate with maximal `GraphDistance + ChangeCoupling + IncidentCorrelation`
|
|
56
|
+
* and zero `RuntimeEvidence` is a plausible guess ranked well, and it must
|
|
57
|
+
* **never** reach `confirmed` however the other three terms sum — because
|
|
58
|
+
* summing all four together *is* the failure mode where a well-ranked guess
|
|
59
|
+
* reads as an established fact. DEC-276 encodes the same rule one layer up
|
|
60
|
+
* as `E == 0 → unconfirmed`.
|
|
61
|
+
*
|
|
62
|
+
* So this module never computes `structuralScore + runtimeEvidence` — not
|
|
63
|
+
* as an internal step, not as a returned field, nowhere. Two design choices
|
|
64
|
+
* make this a **type constraint**, not just a documented convention (proven
|
|
65
|
+
* by a `@ts-expect-error` test in this module's test file, checked by
|
|
66
|
+
* `tsc`, not merely asserted at runtime):
|
|
67
|
+
*
|
|
68
|
+
* 1. `structuralScore: number` carries only `GraphDistance + ChangeCoupling
|
|
69
|
+
* + IncidentCorrelation`. Nothing runtime-evidence-shaped is a term in
|
|
70
|
+
* it, ever.
|
|
71
|
+
* 2. `namedByEvidence: readonly string[]` — the verbatim evidence
|
|
72
|
+
* descriptions naming this exact candidate, supplied by the caller
|
|
73
|
+
* (`descry-runtime`'s evidence-to-node resolution; NOTE-4 §2 is explicit
|
|
74
|
+
* that no graph query for this term exists or should — it is not a
|
|
75
|
+
* graph fact). A `string[]`, not a `number` or a boolean count:
|
|
76
|
+
* `structuralScore + namedByEvidence` does not typecheck, so a caller
|
|
77
|
+
* cannot casually fold it into the ranking arithmetic by accident. A
|
|
78
|
+
* caller who really wants to misuse it has to write new code to do so —
|
|
79
|
+
* this module supplies no field shaped like the thing G3 forbids
|
|
80
|
+
* summing.
|
|
81
|
+
*
|
|
82
|
+
* §18.3a's own table still calls `RuntimeEvidence` *"the strongest
|
|
83
|
+
* signal"* — a node named directly in a backend log outranks one merely on
|
|
84
|
+
* the path. That is honored **as a sort key, not a score term**:
|
|
85
|
+
* `namedByEvidence`-non-empty candidates sort before every candidate
|
|
86
|
+
* without it, and only within each of those two groups does
|
|
87
|
+
* `structuralScore` decide. Categorical partitioning, never arithmetic
|
|
88
|
+
* combination — the distinction NOTE-4 §5 draws is preserved exactly.
|
|
89
|
+
*
|
|
90
|
+
* `RootCauseScore` the architecture names is therefore not a function this
|
|
91
|
+
* module returns as one number. It ranks; a gate above this layer (G3/G4,
|
|
92
|
+
* `descry-runtime`/the pipeline) decides what `namedByEvidence` and the
|
|
93
|
+
* finding's actual evidence bundle are allowed to promote — this module
|
|
94
|
+
* does not gate and does not categorise.
|
|
95
|
+
*
|
|
96
|
+
* ## The resolution caveat (§18.3)
|
|
97
|
+
*
|
|
98
|
+
* *"At R0/R1 the tracer will lose the trail at the first cross-file call
|
|
99
|
+
* and must say 'trace stopped here' rather than presenting a truncated
|
|
100
|
+
* chain as complete."* When the traversal's own resolution floor is R0/R1,
|
|
101
|
+
* a note says so in those words — in addition to, not instead of,
|
|
102
|
+
* `impactOf`'s own disclosure, which explains *why* (cross-file calls are
|
|
103
|
+
* not reliably resolved at that level).
|
|
104
|
+
*
|
|
105
|
+
* ## Weights are starting values, not measurements
|
|
106
|
+
*
|
|
107
|
+
* `CLAUDE.md`: *"Numbers in the docs are targets or starting values, not
|
|
108
|
+
* measurements."* `DEFAULT_ROOT_CAUSE_WEIGHTS` and `INCIDENT_SATURATION`
|
|
109
|
+
* below are exactly that — picked, not measured, gathered into one named
|
|
110
|
+
* block rather than left as literals in the arithmetic so the next reader
|
|
111
|
+
* can find and tune them without reading the function body.
|
|
112
|
+
*/
|
|
113
|
+
import { changesWith, rootCauseSearch } from "./queries.js";
|
|
114
|
+
import { weightedBfs } from "./traverse.js";
|
|
115
|
+
/** §18.3's own walk: from the failure point toward the deep implementation. */
|
|
116
|
+
export const ROOT_CAUSE_TRACE_EDGE_TYPES = ["CALLS", "USES_API", "SERVES_API"];
|
|
117
|
+
export const DEFAULT_ROOT_CAUSE_WEIGHTS = {
|
|
118
|
+
graphDistance: 1,
|
|
119
|
+
changeCoupling: 0.6,
|
|
120
|
+
incidentCorrelation: 0.8,
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* How many correlated incidents saturate `IncidentCorrelation`'s raw 0–1
|
|
124
|
+
* contribution. Unmeasured, same rule as the weights above: a candidate
|
|
125
|
+
* with dozens of correlated incidents should not dominate the ranking
|
|
126
|
+
* arithmetically more than one with a handful, so the raw count is capped
|
|
127
|
+
* before it is weighted.
|
|
128
|
+
*/
|
|
129
|
+
export const INCIDENT_SATURATION = 3;
|
|
130
|
+
const DEFAULT_TOP_N = 5;
|
|
131
|
+
function changeCouplingFor(provider, candidateId, changedIds) {
|
|
132
|
+
if (changedIds.has(candidateId))
|
|
133
|
+
return { kind: "direct" };
|
|
134
|
+
let best = null;
|
|
135
|
+
for (const co of changesWith(provider, candidateId).data) {
|
|
136
|
+
if (!changedIds.has(co.node.id))
|
|
137
|
+
continue;
|
|
138
|
+
if (best === null || (best.kind === "co-change" && co.confidence > best.confidence)) {
|
|
139
|
+
best = { kind: "co-change", viaNodeId: co.node.id, support: co.support, confidence: co.confidence };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return best;
|
|
143
|
+
}
|
|
144
|
+
function rawChangeCoupling(evidence) {
|
|
145
|
+
if (evidence === null)
|
|
146
|
+
return 0;
|
|
147
|
+
return evidence.kind === "direct" ? 1 : evidence.confidence;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Walks `reachedById`'s `via` parent pointers from `targetId` back to the
|
|
151
|
+
* seed (`via === null`), returning the hops in seed-to-target order.
|
|
152
|
+
*
|
|
153
|
+
* The edge stored at each step is reported exactly as `IREdge` has it
|
|
154
|
+
* (`from`/`to` never flipped) — at a `crossApiEndpoint` join the walk can
|
|
155
|
+
* arrive at a node via an edge whose *stored* direction runs the other way
|
|
156
|
+
* (the route→endpoint `SERVES_API` edge, walked endpoint→controller), and
|
|
157
|
+
* `next`/`via` in `neighbours()` already account for that: `entry.id`
|
|
158
|
+
* (the node this hop reached) equals exactly one of `via.from`/`via.to`, and
|
|
159
|
+
* the other end is always the parent — regardless of which direction the
|
|
160
|
+
* walk actually moved. That is the rule this function uses, rather than
|
|
161
|
+
* assuming `via.from` is always the parent.
|
|
162
|
+
*/
|
|
163
|
+
function pathTo(reachedById, targetId) {
|
|
164
|
+
const hops = [];
|
|
165
|
+
let currentId = targetId;
|
|
166
|
+
for (;;) {
|
|
167
|
+
const entry = reachedById.get(currentId);
|
|
168
|
+
if (entry === undefined || entry.via === null)
|
|
169
|
+
break;
|
|
170
|
+
const edge = entry.via;
|
|
171
|
+
hops.push({ from: edge.from, to: edge.to, edgeType: edge.type, resolution: edge.resolution });
|
|
172
|
+
currentId = edge.to === currentId ? edge.from : edge.to;
|
|
173
|
+
}
|
|
174
|
+
return hops.reverse();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Rank the candidates reachable from `failurePoint` by the three structural
|
|
178
|
+
* terms of `RootCauseScore` (architecture §18.3a). See the module header
|
|
179
|
+
* for why `RuntimeEvidence` is deliberately absent from the arithmetic and
|
|
180
|
+
* present only as `namedByEvidence`, a categorical sort key.
|
|
181
|
+
*/
|
|
182
|
+
export function rankRootCause(provider, failurePoint, options = {}) {
|
|
183
|
+
const weights = options.weights ?? DEFAULT_ROOT_CAUSE_WEIGHTS;
|
|
184
|
+
const topN = Math.max(0, Math.floor(options.topN ?? DEFAULT_TOP_N));
|
|
185
|
+
const changedIds = options.changedIds ?? new Set();
|
|
186
|
+
const namedByEvidence = options.namedByEvidence ?? new Map();
|
|
187
|
+
const outcome = weightedBfs(provider, [failurePoint], {
|
|
188
|
+
direction: "out",
|
|
189
|
+
types: ROOT_CAUSE_TRACE_EDGE_TYPES,
|
|
190
|
+
crossApiEndpoint: true,
|
|
191
|
+
...(options.maxDepth === undefined ? {} : { maxDepth: options.maxDepth }),
|
|
192
|
+
...(options.budget === undefined ? {} : { budget: options.budget }),
|
|
193
|
+
});
|
|
194
|
+
const reachedById = new Map(outcome.reached.map((r) => [r.id, r]));
|
|
195
|
+
const candidates = [];
|
|
196
|
+
for (const entry of outcome.reached) {
|
|
197
|
+
const node = provider.node(entry.id);
|
|
198
|
+
if (node === undefined)
|
|
199
|
+
continue; // hydrate: the provider no longer has this node
|
|
200
|
+
const coupling = changeCouplingFor(provider, node.id, changedIds);
|
|
201
|
+
const incidents = rootCauseSearch(provider, node.id).data;
|
|
202
|
+
const incidentCorrelationRaw = Math.min(1, incidents.length / INCIDENT_SATURATION);
|
|
203
|
+
const structuralScore = weights.graphDistance * entry.score + // weightedBfs's own hop x confidence decay — not re-derived
|
|
204
|
+
weights.changeCoupling * rawChangeCoupling(coupling) +
|
|
205
|
+
weights.incidentCorrelation * incidentCorrelationRaw;
|
|
206
|
+
candidates.push({
|
|
207
|
+
node,
|
|
208
|
+
depth: entry.depth,
|
|
209
|
+
structuralScore,
|
|
210
|
+
changeCoupling: coupling,
|
|
211
|
+
correlatedIncidents: incidents,
|
|
212
|
+
namedByEvidence: namedByEvidence.get(node.id) ?? [],
|
|
213
|
+
resolution: node.resolution,
|
|
214
|
+
emptyPath: entry.depth === 0,
|
|
215
|
+
path: pathTo(reachedById, node.id),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// §18.3a: RuntimeEvidence is "the strongest signal" for which candidate to
|
|
219
|
+
// present first — honored here as a categorical partition (named, then
|
|
220
|
+
// not), never as an arithmetic combination with structuralScore.
|
|
221
|
+
const sorted = [...candidates].sort((a, b) => {
|
|
222
|
+
const evidenceRank = (b.namedByEvidence.length > 0 ? 1 : 0) - (a.namedByEvidence.length > 0 ? 1 : 0);
|
|
223
|
+
if (evidenceRank !== 0)
|
|
224
|
+
return evidenceRank;
|
|
225
|
+
if (b.structuralScore !== a.structuralScore)
|
|
226
|
+
return b.structuralScore - a.structuralScore;
|
|
227
|
+
return a.node.id.localeCompare(b.node.id);
|
|
228
|
+
});
|
|
229
|
+
const notes = [];
|
|
230
|
+
if (outcome.truncated) {
|
|
231
|
+
// Mirrors queries.ts's own `result()` helper — impactOf's equivalent
|
|
232
|
+
// note, inlined here since this module calls weightedBfs directly (see
|
|
233
|
+
// module header).
|
|
234
|
+
notes.push("The node budget was reached, so this answer is partial. Nodes beyond the budget " +
|
|
235
|
+
"were not examined and are not reported as clean.");
|
|
236
|
+
}
|
|
237
|
+
if (outcome.resolutionFloor <= 1) {
|
|
238
|
+
notes.push(`Trace stopped here: edges are resolved no better than R${outcome.resolutionFloor}, and a ` +
|
|
239
|
+
"cross-file call at that level may not appear in the graph at all. Candidates beyond an " +
|
|
240
|
+
"unresolved hop are invisible to this ranking, and their absence must not be read as " +
|
|
241
|
+
'"no root cause beyond this point" (architecture §18.3\'s resolution caveat).');
|
|
242
|
+
}
|
|
243
|
+
const ranked = sorted.slice(0, topN);
|
|
244
|
+
return {
|
|
245
|
+
ranked,
|
|
246
|
+
candidateCount: sorted.length,
|
|
247
|
+
truncated: outcome.truncated || sorted.length > ranked.length,
|
|
248
|
+
notes,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=root-cause-score.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"root-cause-score.js","sourceRoot":"","sources":["../../src/query/root-cause-score.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GG;AAKH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAuB,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,WAAW,EAAgB,MAAM,eAAe,CAAC;AAE1D,+EAA+E;AAC/E,MAAM,CAAC,MAAM,2BAA2B,GAAwB,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;AAapG,MAAM,CAAC,MAAM,0BAA0B,GAAqB;IAC1D,aAAa,EAAE,CAAC;IAChB,cAAc,EAAE,GAAG;IACnB,mBAAmB,EAAE,GAAG;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAErC,MAAM,aAAa,GAAG,CAAC,CAAC;AA2ExB,SAAS,iBAAiB,CACxB,QAA2B,EAC3B,WAAmB,EACnB,UAA+B;IAE/B,IAAI,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAE3D,IAAI,IAAI,GAAkC,IAAI,CAAC;IAC/C,KAAK,MAAM,EAAE,IAAI,WAAW,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC;QACzD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,SAAS;QAC1C,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW,IAAI,EAAE,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACpF,IAAI,GAAG,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC;QACtG,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,iBAAiB,CAAC,QAAuC;IAChE,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;AAC9D,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,MAAM,CAAC,WAAyC,EAAE,QAAgB;IACzE,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,SAAS,CAAC;QACR,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,IAAI;YAAE,MAAM;QACrD,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QAC9F,SAAS,GAAG,IAAI,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;IAC1D,CAAC;IACD,OAAO,IAAI,CAAC,OAAO,EAAE,CAAC;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,QAA2B,EAC3B,YAAoB,EACpB,UAA4B,EAAE;IAE9B,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,0BAA0B,CAAC;IAC9D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,GAAG,EAAU,CAAC;IAC3D,MAAM,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,GAAG,EAA6B,CAAC;IAExF,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE;QACpD,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,2BAA2B;QAClC,gBAAgB,EAAE,IAAI;QACtB,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzE,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;KACpE,CAAC,CAAC;IACH,MAAM,WAAW,GAAG,IAAI,GAAG,CAAkB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEpF,MAAM,UAAU,GAA+B,EAAE,CAAC;IAClD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACrC,IAAI,IAAI,KAAK,SAAS;YAAE,SAAS,CAAC,gDAAgD;QAElF,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAClE,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC;QAC1D,MAAM,sBAAsB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,MAAM,GAAG,mBAAmB,CAAC,CAAC;QAEnF,MAAM,eAAe,GACnB,OAAO,CAAC,aAAa,GAAG,KAAK,CAAC,KAAK,GAAG,4DAA4D;YAClG,OAAO,CAAC,cAAc,GAAG,iBAAiB,CAAC,QAAQ,CAAC;YACpD,OAAO,CAAC,mBAAmB,GAAG,sBAAsB,CAAC;QAEvD,UAAU,CAAC,IAAI,CAAC;YACd,IAAI;YACJ,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,eAAe;YACf,cAAc,EAAE,QAAQ;YACxB,mBAAmB,EAAE,SAAS;YAC9B,eAAe,EAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE;YACnD,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,SAAS,EAAE,KAAK,CAAC,KAAK,KAAK,CAAC;YAC5B,IAAI,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC;SACnC,CAAC,CAAC;IACL,CAAC;IAED,2EAA2E;IAC3E,uEAAuE;IACvE,iEAAiE;IACjE,MAAM,MAAM,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC3C,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrG,IAAI,YAAY,KAAK,CAAC;YAAE,OAAO,YAAY,CAAC;QAC5C,IAAI,CAAC,CAAC,eAAe,KAAK,CAAC,CAAC,eAAe;YAAE,OAAO,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC;QAC1F,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;QACtB,qEAAqE;QACrE,uEAAuE;QACvE,kBAAkB;QAClB,KAAK,CAAC,IAAI,CACR,kFAAkF;YAChF,kDAAkD,CACrD,CAAC;IACJ,CAAC;IACD,IAAI,OAAO,CAAC,eAAe,IAAI,CAAC,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CACR,0DAA0D,OAAO,CAAC,eAAe,UAAU;YACzF,yFAAyF;YACzF,sFAAsF;YACtF,8EAA8E,CACjF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACrC,OAAO;QACL,MAAM;QACN,cAAc,EAAE,MAAM,CAAC,MAAM;QAC7B,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM;QAC7D,KAAK;KACN,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DEC-244, extended: the same row can be closed by two different kinds of
|
|
3
|
+
* evidence at once, and they must never collapse into each other.
|
|
4
|
+
*
|
|
5
|
+
* `applyDeclaredValues` (`declared-value-closure.ts`) answers from `.env*`
|
|
6
|
+
* config alone. `applyConfirmedFacts` (`confirmed-facts.ts`) answers from
|
|
7
|
+
* human testimony alone, and — since `ConfirmedFact.environment` landed —
|
|
8
|
+
* testimony can now be scoped to a *named* environment, not just an unscoped
|
|
9
|
+
* blanket answer. This module is the picture that shows both kinds of
|
|
10
|
+
* evidence side by side, per environment, without ever letting one stand in
|
|
11
|
+
* for the other.
|
|
12
|
+
*
|
|
13
|
+
* ## The real case that forced this shape
|
|
14
|
+
*
|
|
15
|
+
* `sherpa-frontend`'s `import.meta.env.VITE_BACKEND_API_URL` today has, on
|
|
16
|
+
* disk, in this worktree:
|
|
17
|
+
*
|
|
18
|
+
* - `"development"` → `http://localhost:8000`, **declared** — read from
|
|
19
|
+
* `.env.development.local`, class A (DEC-244 Row 1).
|
|
20
|
+
* - `"staging"` → `https://sherpa-backend-staging.up.railway.app`,
|
|
21
|
+
* **asserted** — a human-confirmed `ConfirmedFact` with
|
|
22
|
+
* `environment: "staging"`, class B. `"staging"` never appears in any
|
|
23
|
+
* `.env*` filename here; the *only* reason this module knows it exists at
|
|
24
|
+
* all is that a person named it on the fact.
|
|
25
|
+
* - `"production"` — still entirely unaddressed, by either source, and
|
|
26
|
+
* **still unnameable from anything this module reads.**
|
|
27
|
+
*
|
|
28
|
+
* ## Why `environment` is never inferred, only ever read
|
|
29
|
+
*
|
|
30
|
+
* `ConfirmedFact.environment`'s own doc is explicit: "human-supplied only,
|
|
31
|
+
* never inferred." This module upholds that by construction — the only
|
|
32
|
+
* places an environment name enters this picture are `resolve.ts`'s
|
|
33
|
+
* `knownEnvironments` (from `.env*` **filenames**, DEC-223's own "never
|
|
34
|
+
* guessed, always refused" rule) and `fact.environment` (typed as the human
|
|
35
|
+
* literally set it). Nothing here parses `"staging"` out of a hostname; the
|
|
36
|
+
* hostname's word "staging" and the fact's `environment: "staging"` are
|
|
37
|
+
* correlated in this specific real case only because a *person* read the
|
|
38
|
+
* same signal and typed the same word — this module never makes that
|
|
39
|
+
* connection itself, and would behave identically if the two disagreed.
|
|
40
|
+
*
|
|
41
|
+
* ## Why `"closed"` is reserved for fully-declared rows
|
|
42
|
+
*
|
|
43
|
+
* A row where every named environment resolves — some from config, some from
|
|
44
|
+
* testimony — could be called "closed." It is deliberately not: `state`
|
|
45
|
+
* reaches `"closed"` only when **every** named environment's evidence is
|
|
46
|
+
* `declared` (class A). One environment resting on testimony keeps the whole
|
|
47
|
+
* row at `"partially-closed"`, even if nothing is technically unaddressed —
|
|
48
|
+
* because a caller reading the single word `"closed"` must never have to
|
|
49
|
+
* re-check `environments[].reliability` to find out whether that word is
|
|
50
|
+
* hiding an unaudited assertion. This is the concrete answer to "what each
|
|
51
|
+
* provenance earns, side by side" (DEC-244 Row 1): declared is A, asserted is
|
|
52
|
+
* B, and the top-level state can never quietly promote a B into looking like
|
|
53
|
+
* an A.
|
|
54
|
+
*/
|
|
55
|
+
import type { ConfirmedFact, ReliabilityClass, UnresolvedRef } from "@descryy/ir";
|
|
56
|
+
import type { DeclaredEnvValue } from "../ledger/resolve.ts";
|
|
57
|
+
import { type ClosureState } from "./declared-value-closure.ts";
|
|
58
|
+
/** A human-confirmed value with no `environment` on the fact — deliberately not attributed to any named one. */
|
|
59
|
+
export interface AssertedValue {
|
|
60
|
+
readonly value: string;
|
|
61
|
+
readonly confirmedBy: string;
|
|
62
|
+
readonly confirmedAt: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* One named environment — named by a `.env*` filename, a scoped
|
|
66
|
+
* `ConfirmedFact`, or both — and everything either source says about it.
|
|
67
|
+
*/
|
|
68
|
+
export interface EnvironmentValue {
|
|
69
|
+
readonly environment: string;
|
|
70
|
+
readonly declaredValue: string | null;
|
|
71
|
+
readonly declaredFile: string | null;
|
|
72
|
+
readonly declaredLine: number | null;
|
|
73
|
+
readonly assertedValue: string | null;
|
|
74
|
+
readonly assertedBy: string | null;
|
|
75
|
+
readonly assertedAt: string | null;
|
|
76
|
+
/** `"A"` when `declaredValue` is set, `"B"` when only a scoped assertion addresses it, `null` when neither. */
|
|
77
|
+
readonly reliability: ReliabilityClass | null;
|
|
78
|
+
}
|
|
79
|
+
export interface RowClosurePicture {
|
|
80
|
+
readonly ref: UnresolvedRef;
|
|
81
|
+
readonly blockedBy: string;
|
|
82
|
+
/** Every environment either source names, sorted. Never invented beyond what a filename or a fact states. */
|
|
83
|
+
readonly environments: readonly EnvironmentValue[];
|
|
84
|
+
/** Facts matching this row's `(repo, blockedBy)` with no `environment` — testimony, scope unspecified. */
|
|
85
|
+
readonly unscopedAssertions: readonly AssertedValue[];
|
|
86
|
+
/** `environments` entries with `reliability === null` — named by something, addressed by nothing. */
|
|
87
|
+
readonly stillUnaddressed: readonly string[];
|
|
88
|
+
readonly state: ClosureState | "open";
|
|
89
|
+
}
|
|
90
|
+
export interface RowClosureApplication {
|
|
91
|
+
/** One per askable row, `state` included even when `"open"` — the fuller shape is the point. */
|
|
92
|
+
readonly pictures: readonly RowClosurePicture[];
|
|
93
|
+
/** Rows `askableHandle` refused outright: wrong class, unset, no handle. Untouched by either source. */
|
|
94
|
+
readonly notAskable: readonly UnresolvedRef[];
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Describe every askable row against both sources of evidence at once.
|
|
98
|
+
*
|
|
99
|
+
* `declared` and `facts` are both pre-loaded by the caller — same
|
|
100
|
+
* read-once-apply-many discipline `resolveAgainstDeclared`/
|
|
101
|
+
* `applyConfirmedFacts` already follow. The fact-matching itself is not
|
|
102
|
+
* re-derived here: `applyConfirmedFacts` is called once and its output
|
|
103
|
+
* reused, so the `(repo, blockedBy)` scoping and the askable filter it
|
|
104
|
+
* already enforces are not re-implemented a second, potentially-diverging
|
|
105
|
+
* way.
|
|
106
|
+
*/
|
|
107
|
+
export declare function describeRowClosures(refs: readonly UnresolvedRef[], declared: readonly DeclaredEnvValue[], facts: readonly ConfirmedFact[], repo: string): RowClosureApplication;
|
|
108
|
+
//# sourceMappingURL=row-closure-picture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"row-closure-picture.d.ts","sourceRoot":"","sources":["../../src/query/row-closure-picture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIlF,OAAO,KAAK,EAAE,gBAAgB,EAA4B,MAAM,sBAAsB,CAAC;AAEvF,OAAO,EAA8B,KAAK,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAK5F,gHAAgH;AAChH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,+GAA+G;IAC/G,QAAQ,CAAC,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC/C;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,6GAA6G;IAC7G,QAAQ,CAAC,YAAY,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACnD,0GAA0G;IAC1G,QAAQ,CAAC,kBAAkB,EAAE,SAAS,aAAa,EAAE,CAAC;IACtD,qGAAqG;IACrG,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7C,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC;CACvC;AAED,MAAM,WAAW,qBAAqB;IACpC,gGAAgG;IAChG,QAAQ,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IAChD,wGAAwG;IACxG,QAAQ,CAAC,UAAU,EAAE,SAAS,aAAa,EAAE,CAAC;CAC/C;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,SAAS,aAAa,EAAE,EAC9B,QAAQ,EAAE,SAAS,gBAAgB,EAAE,EACrC,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,IAAI,EAAE,MAAM,GACX,qBAAqB,CAoEvB"}
|