@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,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §14.4 — is this overlap two people adding lines, or two people disagreeing?
|
|
3
|
+
*
|
|
4
|
+
* `overlap.ts` surfaces a shared node and stops, and says why it stops: *"§14.4's
|
|
5
|
+
* mechanical-versus-semantic classification needs the actual diff lines at the
|
|
6
|
+
* shared node, which this module never sees … Guessing 'mechanical' without
|
|
7
|
+
* reading the lines would be the one place in this system where a wrong guess
|
|
8
|
+
* edits somebody's branch."* This is that module, and it reads the lines.
|
|
9
|
+
*
|
|
10
|
+
* ## The bar here is higher than anywhere else in §14, not lower
|
|
11
|
+
*
|
|
12
|
+
* §14.4 is the **single documented exception** to approval-before-code-change
|
|
13
|
+
* (§9, rule 8). Every other check in this package tells a human to look at
|
|
14
|
+
* something. A `MECHANICAL` verdict may be **auto-applied** — it edits a branch
|
|
15
|
+
* without asking.
|
|
16
|
+
*
|
|
17
|
+
* So rule 2 inverts in consequence, and the asymmetry is the whole design:
|
|
18
|
+
*
|
|
19
|
+
* > A missed mechanical resolution costs a human thirty seconds of `git
|
|
20
|
+
* > mergetool`. A false one silently rewrites two people's work into something
|
|
21
|
+
* > neither wrote and reports success.
|
|
22
|
+
*
|
|
23
|
+
* `MECHANICAL` is therefore claimed **only when every condition is affirmatively
|
|
24
|
+
* established**, never when nothing contradicts it. Absent hunks, a hunk whose
|
|
25
|
+
* shape cannot be read, a file this module cannot reason about — each produces
|
|
26
|
+
* `UNCLASSIFIABLE` with a reason, and `UNCLASSIFIABLE` never auto-applies.
|
|
27
|
+
* `SEMANTIC` is the answer when the lines were read and disagree; that is a
|
|
28
|
+
* different sentence from "the lines were not read", and the two are kept apart
|
|
29
|
+
* for the reason they are kept apart everywhere else in this package.
|
|
30
|
+
*
|
|
31
|
+
* ## What "only add new, non-overlapping lines" has to mean to be safe
|
|
32
|
+
*
|
|
33
|
+
* §14.4's diamond asks whether both changes *"only add new, non-overlapping
|
|
34
|
+
* lines"*. Read literally that is a test on the post-image, and the post-images
|
|
35
|
+
* of two branches are not comparable — they are two different files. The test
|
|
36
|
+
* that means anything is on the **base**:
|
|
37
|
+
*
|
|
38
|
+
* 1. **Every hunk on both sides is a pure insertion** — it consumes zero base
|
|
39
|
+
* lines (`baseLines === 0`). One modified or deleted base line anywhere at
|
|
40
|
+
* this node makes the whole overlap `SEMANTIC`. Not "the modified hunks are
|
|
41
|
+
* semantic and the rest are mechanical": a partially auto-applied overlap is
|
|
42
|
+
* a merge nobody reviewed.
|
|
43
|
+
*
|
|
44
|
+
* 2. **No two insertion points from opposite sides fall within the context
|
|
45
|
+
* window.** Two insertions at base lines 10 and 12 do not touch the same
|
|
46
|
+
* line and git still refuses them, because their three-line contexts
|
|
47
|
+
* overlap. Matching git's own margin rather than testing for exact equality
|
|
48
|
+
* is the difference between a rule about text and a rule about what the
|
|
49
|
+
* merge will actually do.
|
|
50
|
+
*
|
|
51
|
+
* The margin is `CONTEXT_LINES`, and it is deliberately the *conservative*
|
|
52
|
+
* direction: a real mechanical case inside the margin is reported `SEMANTIC` and
|
|
53
|
+
* a human resolves it in half a minute.
|
|
54
|
+
*
|
|
55
|
+
* ## Two claims, two different sources of authority — and only one of them
|
|
56
|
+
* gates the apply
|
|
57
|
+
*
|
|
58
|
+
* This finding rests on two separate things and it is worth being exact about
|
|
59
|
+
* which does what, because conflating them would either block safe applies or
|
|
60
|
+
* permit unsafe ones:
|
|
61
|
+
*
|
|
62
|
+
* | claim | rests on | governs |
|
|
63
|
+
* | --- | --- | --- |
|
|
64
|
+
* | *both changes touch this node* | the graph | whether this is **surfaced**, and the reliability class |
|
|
65
|
+
* | *these hunks merge without conflict* | the diff text | whether this may be **applied** |
|
|
66
|
+
*
|
|
67
|
+
* The reliability class follows `overlap.ts` — the weaker of the two sides'
|
|
68
|
+
* resolutions, per DEC-058 and §11A.5 — and it describes how much to trust that
|
|
69
|
+
* this overlap *matters*. It does not gate the apply, and that is not a
|
|
70
|
+
* loosening: if the graph were wrong about the node, "keep both non-adjacent
|
|
71
|
+
* pure insertions" is still exactly as safe, because the textual claim never
|
|
72
|
+
* depended on the graph being right. Gating the apply on the graph's resolution
|
|
73
|
+
* would be borrowing authority from the wrong evidence.
|
|
74
|
+
*
|
|
75
|
+
* A caller wanting the stricter rule can require class A itself. The point is
|
|
76
|
+
* that the module states which evidence carries which claim rather than folding
|
|
77
|
+
* both into one number.
|
|
78
|
+
*
|
|
79
|
+
* ## Verify-after is not a recommendation and is not expressible as one
|
|
80
|
+
*
|
|
81
|
+
* §14.4 makes the post-apply verification **mandatory** (§19). A `verifyAfter:
|
|
82
|
+
* true` field on a result would be advisory, and an advisory obligation is one
|
|
83
|
+
* `if` away from being skipped by a caller in a hurry.
|
|
84
|
+
*
|
|
85
|
+
* So the apply path is `applyMechanical(plan, { apply, verify })` and **both
|
|
86
|
+
* callbacks are required by the type**. There is no overload that applies
|
|
87
|
+
* without verifying. A `verify` returning false produces `"reverted"`, and a
|
|
88
|
+
* `verify` that throws produces `"verification-failed"` — which is *not* the
|
|
89
|
+
* same outcome and is not collapsed into it, because "the change broke
|
|
90
|
+
* something" and "we could not tell whether the change broke something" are the
|
|
91
|
+
* two categories §20.2 exists to keep apart.
|
|
92
|
+
*/
|
|
93
|
+
import { reliabilityCap, reliabilityNote } from "@descryy/ir";
|
|
94
|
+
/**
|
|
95
|
+
* git's default diff context. Two insertions closer than this on the base
|
|
96
|
+
* produce overlapping context and git declines to merge them, so this module
|
|
97
|
+
* declines too.
|
|
98
|
+
*/
|
|
99
|
+
export const CONTEXT_LINES = 3;
|
|
100
|
+
/** Every reason this module declines, as data — a refusal is a row, never a silence. */
|
|
101
|
+
const REASONS = {
|
|
102
|
+
noHunksA: "no hunks supplied for side A at this node, so nothing was read",
|
|
103
|
+
noHunksB: "no hunks supplied for side B at this node, so nothing was read",
|
|
104
|
+
noHunksEither: "no hunks supplied for either side at this node, so nothing was read",
|
|
105
|
+
negativeLines: "a hunk reports a negative line count, which this module cannot interpret; " +
|
|
106
|
+
"an unreadable hunk is not an absent one",
|
|
107
|
+
};
|
|
108
|
+
function hunksFor(hunks, node) {
|
|
109
|
+
// A shared node without a file cannot be matched to a hunk. That is a refusal
|
|
110
|
+
// below, not a filter that silently returns nothing here.
|
|
111
|
+
if (node.file === undefined)
|
|
112
|
+
return [];
|
|
113
|
+
return hunks.filter((hunk) => hunk.file === node.file);
|
|
114
|
+
}
|
|
115
|
+
function classifyOne(node, aHunks, bHunks, contextLines) {
|
|
116
|
+
const reliability = node.reliability;
|
|
117
|
+
const disclosure = `${reliabilityNote(node.resolution, false)} This classification's textual half — whether ` +
|
|
118
|
+
"the hunks merge — rests on the diff and not on the graph; the class above describes how " +
|
|
119
|
+
"far to rely on this overlap mattering at all.";
|
|
120
|
+
const base = {
|
|
121
|
+
nodeId: node.nodeId,
|
|
122
|
+
reliability,
|
|
123
|
+
resolution_level: node.resolution,
|
|
124
|
+
hunksRead: [aHunks.length, bHunks.length],
|
|
125
|
+
disclosure,
|
|
126
|
+
};
|
|
127
|
+
if (node.file === undefined) {
|
|
128
|
+
return {
|
|
129
|
+
...base,
|
|
130
|
+
verdict: "UNCLASSIFIABLE",
|
|
131
|
+
reason: "this shared node carries no file, so its hunks cannot be located. Node identity " +
|
|
132
|
+
"deliberately excludes the path (DEC-004), so a node without a reported file is a " +
|
|
133
|
+
"normal graph state and not a defect — it is simply not classifiable here.",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (aHunks.length === 0 && bHunks.length === 0) {
|
|
137
|
+
return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.noHunksEither };
|
|
138
|
+
}
|
|
139
|
+
if (aHunks.length === 0)
|
|
140
|
+
return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.noHunksA };
|
|
141
|
+
if (bHunks.length === 0)
|
|
142
|
+
return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.noHunksB };
|
|
143
|
+
const all = [...aHunks, ...bHunks];
|
|
144
|
+
if (all.some((hunk) => hunk.baseLines < 0 || hunk.headLines < 0 || hunk.baseStart < 0)) {
|
|
145
|
+
return { ...base, verdict: "UNCLASSIFIABLE", reason: REASONS.negativeLines };
|
|
146
|
+
}
|
|
147
|
+
// Condition 1 — every hunk on BOTH sides is a pure insertion. One modified
|
|
148
|
+
// base line anywhere at this node makes the whole overlap semantic; there is
|
|
149
|
+
// no partial auto-apply, because a partially applied overlap is a merge
|
|
150
|
+
// nobody reviewed.
|
|
151
|
+
const modifying = all.filter((hunk) => hunk.baseLines > 0);
|
|
152
|
+
if (modifying.length > 0) {
|
|
153
|
+
const where = modifying
|
|
154
|
+
.slice(0, 3)
|
|
155
|
+
.map((hunk) => `${hunk.file}:${hunk.baseStart}(+${hunk.baseLines} base lines)`)
|
|
156
|
+
.join(", ");
|
|
157
|
+
return {
|
|
158
|
+
...base,
|
|
159
|
+
verdict: "SEMANTIC",
|
|
160
|
+
reason: `${modifying.length} of ${all.length} hunk(s) modify or delete existing base lines ` +
|
|
161
|
+
`(${where}${modifying.length > 3 ? ", …" : ""}). §14.4 admits only pure additions, and ` +
|
|
162
|
+
"admits them only when every hunk at the node is one — a partially auto-applied " +
|
|
163
|
+
"overlap is a merge nobody reviewed.",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// Condition 2 — no insertion point from one side within the context window of
|
|
167
|
+
// one from the other. Exact equality is not the test: git conflicts on
|
|
168
|
+
// overlapping context, so this uses git's own margin.
|
|
169
|
+
const collisions = [];
|
|
170
|
+
for (const a of aHunks) {
|
|
171
|
+
for (const b of bHunks) {
|
|
172
|
+
const gap = Math.abs(a.baseStart - b.baseStart);
|
|
173
|
+
if (gap <= contextLines) {
|
|
174
|
+
collisions.push(`${a.file}:${a.baseStart} vs :${b.baseStart} (${gap} line(s) apart)`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (collisions.length > 0) {
|
|
179
|
+
return {
|
|
180
|
+
...base,
|
|
181
|
+
verdict: "SEMANTIC",
|
|
182
|
+
reason: `both sides insert within ${contextLines} line(s) of each other on the base ` +
|
|
183
|
+
`(${collisions.slice(0, 3).join("; ")}${collisions.length > 3 ? "; …" : ""}). ` +
|
|
184
|
+
"The insertions do not share a line and git still declines them, because their " +
|
|
185
|
+
"contexts overlap — so this module declines too rather than proposing a merge git " +
|
|
186
|
+
"would refuse.",
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
const order = [
|
|
190
|
+
...aHunks.map((hunk) => ({ side: "a", hunk })),
|
|
191
|
+
...bHunks.map((hunk) => ({ side: "b", hunk })),
|
|
192
|
+
].sort((x, y) => x.hunk.baseStart - y.hunk.baseStart || x.side.localeCompare(y.side));
|
|
193
|
+
return {
|
|
194
|
+
...base,
|
|
195
|
+
verdict: "MECHANICAL",
|
|
196
|
+
reason: `all ${all.length} hunk(s) at this node are pure insertions and the two sides' ` +
|
|
197
|
+
`insertion points are more than ${contextLines} line(s) apart on the base, so "keep ` +
|
|
198
|
+
'both" is a merge git would produce unaided. Auto-applying it is §14.4\'s documented ' +
|
|
199
|
+
"exception to approval-before-code-change, and the verification after it is mandatory.",
|
|
200
|
+
resolution: { kind: "keep-both", order },
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Classify every surfaced overlap between two changes.
|
|
205
|
+
*
|
|
206
|
+
* @param shared the nodes `findOverlaps` surfaced — this module does not decide
|
|
207
|
+
* what is worth looking at, only what may be done about it
|
|
208
|
+
* @param aHunks side A's hunks, against the base both sides share
|
|
209
|
+
* @param bHunks side B's hunks, against the same base
|
|
210
|
+
*/
|
|
211
|
+
export function classifyOverlaps(shared, aHunks, bHunks, options = {}) {
|
|
212
|
+
const contextLines = options.contextLines ?? CONTEXT_LINES;
|
|
213
|
+
const classifications = shared.map((node) => classifyOne(node, hunksFor(aHunks, node), hunksFor(bHunks, node), contextLines));
|
|
214
|
+
const counts = {
|
|
215
|
+
shared: shared.length,
|
|
216
|
+
withHunksBothSides: classifications.filter((c) => c.hunksRead[0] > 0 && c.hunksRead[1] > 0).length,
|
|
217
|
+
mechanical: classifications.filter((c) => c.verdict === "MECHANICAL").length,
|
|
218
|
+
semantic: classifications.filter((c) => c.verdict === "SEMANTIC").length,
|
|
219
|
+
unclassifiable: classifications.filter((c) => c.verdict === "UNCLASSIFIABLE").length,
|
|
220
|
+
};
|
|
221
|
+
// The baseline precondition, same shape as `hidden-dependency.ts`: nothing was
|
|
222
|
+
// read, so nothing can be concluded — and "nothing was mechanical" is a
|
|
223
|
+
// conclusion.
|
|
224
|
+
const analysed = counts.withHunksBothSides > 0;
|
|
225
|
+
const notes = [];
|
|
226
|
+
if (!analysed && shared.length > 0) {
|
|
227
|
+
notes.push("No shared node had hunks on both sides, so this check did not run. That is not a " +
|
|
228
|
+
"finding that nothing is mechanically resolvable: a caller that did not supply diffs, " +
|
|
229
|
+
"hunks whose paths do not match the nodes' reported files, and an overlap that is " +
|
|
230
|
+
"genuinely all semantic are three different situations and this answer cannot tell " +
|
|
231
|
+
"them apart.");
|
|
232
|
+
}
|
|
233
|
+
if (counts.unclassifiable > 0) {
|
|
234
|
+
notes.push(`${counts.unclassifiable} overlap(s) could not be classified and are NOT semantic — ` +
|
|
235
|
+
"nothing read them. They never auto-apply.");
|
|
236
|
+
}
|
|
237
|
+
if (counts.mechanical > 0) {
|
|
238
|
+
notes.push(`${counts.mechanical} overlap(s) are mechanically resolvable. Applying one is the single ` +
|
|
239
|
+
"documented exception to approval-before-code-change (§9, §14.4) and the verification " +
|
|
240
|
+
"after it is mandatory, not advisory — see applyMechanical().");
|
|
241
|
+
}
|
|
242
|
+
const floor = classifications.reduce((lowest, c) => (c.resolution_level < lowest ? c.resolution_level : lowest), 4);
|
|
243
|
+
const stamp = options.stamp ?? {
|
|
244
|
+
commitSha: null,
|
|
245
|
+
graphBuiltAt: null,
|
|
246
|
+
irSchemaVersion: 1,
|
|
247
|
+
resolutionFloor: shared.length === 0 ? 0 : floor,
|
|
248
|
+
};
|
|
249
|
+
return { data: { classifications, analysed, checked: counts }, stamp, truncated: false, notes };
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Apply one mechanical resolution, then verify — in that order, always both.
|
|
253
|
+
*
|
|
254
|
+
* The mandatory verification of §14.4/§19 is enforced by the *type*: `verify` is
|
|
255
|
+
* a required member of `ApplyHooks`, so a caller that wants to skip it has to
|
|
256
|
+
* write a function that lies, which is a different and more visible act than
|
|
257
|
+
* omitting an optional field.
|
|
258
|
+
*
|
|
259
|
+
* Refuses outright, before touching anything, when the verdict is not
|
|
260
|
+
* `MECHANICAL` or when no `revert` was supplied — auto-applying a change that
|
|
261
|
+
* cannot be undone if verification fails is the exception's precondition, not a
|
|
262
|
+
* detail of it.
|
|
263
|
+
*/
|
|
264
|
+
export async function applyMechanical(classification, hooks) {
|
|
265
|
+
if (classification.verdict !== "MECHANICAL" || classification.resolution === undefined) {
|
|
266
|
+
return {
|
|
267
|
+
status: "refused",
|
|
268
|
+
why: `verdict is ${classification.verdict}, and only MECHANICAL may auto-apply. ` +
|
|
269
|
+
`Reason given: ${classification.reason}`,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
if (hooks.revert === undefined) {
|
|
273
|
+
return {
|
|
274
|
+
status: "refused",
|
|
275
|
+
why: "no revert hook was supplied. §14.4's exception is auto-apply PLUS mandatory " +
|
|
276
|
+
"verification, and a verification whose failure cannot undo the change is a report, " +
|
|
277
|
+
"not a safeguard.",
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
await hooks.apply(classification.resolution);
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
return { status: "refused", why: `apply threw and nothing was verified: ${String(error)}` };
|
|
285
|
+
}
|
|
286
|
+
let ok;
|
|
287
|
+
try {
|
|
288
|
+
ok = await hooks.verify();
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
// Verification could not answer. Revert anyway — the change is live and
|
|
292
|
+
// unverified, which is the state §14.4 exists to prevent — but report that
|
|
293
|
+
// we do not know rather than that it was bad.
|
|
294
|
+
try {
|
|
295
|
+
await hooks.revert();
|
|
296
|
+
}
|
|
297
|
+
catch (revertError) {
|
|
298
|
+
return {
|
|
299
|
+
status: "verification-failed",
|
|
300
|
+
why: `verification threw (${String(error)}) AND the revert threw (${String(revertError)}). ` +
|
|
301
|
+
"The branch is in an applied, unverified state and needs a human.",
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
status: "verification-failed",
|
|
306
|
+
why: `verification threw and the change was reverted: ${String(error)}. This is "we cannot tell", not "it broke".`,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
if (!ok) {
|
|
310
|
+
try {
|
|
311
|
+
await hooks.revert();
|
|
312
|
+
}
|
|
313
|
+
catch (revertError) {
|
|
314
|
+
return {
|
|
315
|
+
status: "verification-failed",
|
|
316
|
+
why: `verification failed and the revert threw (${String(revertError)}). The branch is in ` +
|
|
317
|
+
"an applied, failing state and needs a human.",
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
return { status: "reverted", why: "verification failed and the change was reverted." };
|
|
321
|
+
}
|
|
322
|
+
return { status: "applied", verified: true };
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The reliability class this module would report for a pair of resolutions.
|
|
326
|
+
*
|
|
327
|
+
* Exported so `overlap.ts`'s rule and this one cannot drift apart silently: both
|
|
328
|
+
* take the weaker of the two sides, per DEC-058.
|
|
329
|
+
*/
|
|
330
|
+
export function pairReliability(a, b) {
|
|
331
|
+
return reliabilityCap(a < b ? a : b, false);
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=mechanical.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mechanical.js","sourceRoot":"","sources":["../../src/multipr/mechanical.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* §14.6 — two open changes each adding a migration from the same parent, caught
|
|
3
|
+
* before the merge that turns it into a broken deploy.
|
|
4
|
+
*
|
|
5
|
+
* ## What this computes, and why it is the real computation rather than a proxy
|
|
6
|
+
*
|
|
7
|
+
* Migrations form a DAG: each revision names its parent(s), and a **head** is a
|
|
8
|
+
* revision nothing else descends from. Every migration runner refuses to run
|
|
9
|
+
* with more than one — Alembic's *"Multiple head revisions are present"* is the
|
|
10
|
+
* canonical spelling, and the equivalent exists in every tool that versions
|
|
11
|
+
* migrations at all.
|
|
12
|
+
*
|
|
13
|
+
* The obvious shortcut is to group migrations by declared parent and flag any
|
|
14
|
+
* parent claimed by two different changes. **That is wrong**, and wrong in the
|
|
15
|
+
* direction that produces false findings: a merge migration declaring two
|
|
16
|
+
* parents exists precisely to resolve such a fork, and the shortcut cannot see
|
|
17
|
+
* it. So this computes heads over the union the way the runner does. The check
|
|
18
|
+
* is only worth its Class A ceiling if it computes what the tool computes.
|
|
19
|
+
*
|
|
20
|
+
* ## Nothing here names a framework, and that is §14.6's own argument
|
|
21
|
+
*
|
|
22
|
+
* §14.6 says the SQL adapter makes this work identically across ORMs and raw-SQL
|
|
23
|
+
* tooling "rather than being written once per framework". So the input is a
|
|
24
|
+
* revision, its declared parents and where it came from — the shape every
|
|
25
|
+
* migration tool has, whatever it calls the fields. Reading `down_revision` out
|
|
26
|
+
* of an Alembic file, `dependencies` out of a Django one or a timestamp chain
|
|
27
|
+
* out of a Rails one is producer work. Rule 1: none of those words appear below
|
|
28
|
+
* this sentence.
|
|
29
|
+
*
|
|
30
|
+
* ## The class, and a disagreement with §15.2 that is already settled
|
|
31
|
+
*
|
|
32
|
+
* §15.2 lists "migration head conflict" as **Class A — deterministically
|
|
33
|
+
* verifiable, a structural fact, not an inference**. That is right about the
|
|
34
|
+
* *inference*: given both parent lists, the conclusion follows with no
|
|
35
|
+
* judgement at all. It is not right unconditionally, because the parent lists
|
|
36
|
+
* still have to be obtained, and reading `down_revision` with a regex is R0
|
|
37
|
+
* evidence however certain the arithmetic on top of it.
|
|
38
|
+
*
|
|
39
|
+
* This is the same disagreement the env/config check hit, and §15.3 already
|
|
40
|
+
* records the resolution: **the Class column is a ceiling, not a floor** —
|
|
41
|
+
* "corrected after the env/config check was built and the two disagreed". So A
|
|
42
|
+
* is what this check reaches when the records come from the framework's own
|
|
43
|
+
* migration history (DEC-115's promotion path, R3), and a source-read set lands
|
|
44
|
+
* where its evidence lands. DEC-058 at the finding level: the claim carries the
|
|
45
|
+
* resolution of its own evidence, not of the run.
|
|
46
|
+
*
|
|
47
|
+
* That is now the second check to disagree with §15.2's column in the same
|
|
48
|
+
* direction, which is worth someone's attention as a pattern rather than as two
|
|
49
|
+
* incidents.
|
|
50
|
+
*
|
|
51
|
+
* ## An unread migration is not a migration with no parents
|
|
52
|
+
*
|
|
53
|
+
* An initial migration genuinely has zero parents. A migration whose parent line
|
|
54
|
+
* could not be read has *unknown* parents. Both are "no parents in the record",
|
|
55
|
+
* and collapsing them silently corrupts the head computation — an unread
|
|
56
|
+
* migration might be exactly the merge that resolves the fork being reported.
|
|
57
|
+
*
|
|
58
|
+
* So `parents` is `readonly string[] | "unknown"` and the empty array cannot be
|
|
59
|
+
* used to mean the second thing. The distinction is made unrepresentable at the
|
|
60
|
+
* type rather than handled downstream, which is the repair
|
|
61
|
+
* `bench/lib/README.md` asks for at construction time rather than afterwards.
|
|
62
|
+
*/
|
|
63
|
+
import type { ReliabilityClass, ResolutionLevel } from "@descryy/ir";
|
|
64
|
+
/**
|
|
65
|
+
* One migration a change adds.
|
|
66
|
+
*
|
|
67
|
+
* `parents` is the declared parent revision(s): empty for an initial migration,
|
|
68
|
+
* more than one for a merge migration, and `"unknown"` when the producer could
|
|
69
|
+
* not read them. **Never `[]` for the last case.**
|
|
70
|
+
*/
|
|
71
|
+
export interface MigrationRecord {
|
|
72
|
+
readonly prId: string;
|
|
73
|
+
/** The revision identifier this migration declares for itself. */
|
|
74
|
+
readonly revision: string;
|
|
75
|
+
readonly parents: readonly string[] | "unknown";
|
|
76
|
+
readonly file: string;
|
|
77
|
+
/** The level at which this record's parent list was obtained. */
|
|
78
|
+
readonly resolution: ResolutionLevel;
|
|
79
|
+
}
|
|
80
|
+
export interface MigrationHead {
|
|
81
|
+
readonly revision: string;
|
|
82
|
+
readonly prId: string;
|
|
83
|
+
readonly file: string;
|
|
84
|
+
}
|
|
85
|
+
export interface HeadConflict {
|
|
86
|
+
/** The changes whose migrations would each be left as a head. */
|
|
87
|
+
readonly prIds: readonly string[];
|
|
88
|
+
readonly heads: readonly MigrationHead[];
|
|
89
|
+
readonly reliability: ReliabilityClass;
|
|
90
|
+
readonly disclosure: string;
|
|
91
|
+
}
|
|
92
|
+
/** A change whose migration descends from one only another open change adds. */
|
|
93
|
+
export interface CrossPrDependency {
|
|
94
|
+
readonly prId: string;
|
|
95
|
+
readonly revision: string;
|
|
96
|
+
/** The parent revision, and the change that introduces it. */
|
|
97
|
+
readonly dependsOnRevision: string;
|
|
98
|
+
readonly dependsOnPrId: string;
|
|
99
|
+
readonly reliability: ReliabilityClass;
|
|
100
|
+
readonly disclosure: string;
|
|
101
|
+
}
|
|
102
|
+
export interface MigrationHeadReport {
|
|
103
|
+
/**
|
|
104
|
+
* At most one: either the merged set has a single head or it does not. The
|
|
105
|
+
* conflict names every change involved rather than being reported pairwise,
|
|
106
|
+
* because three changes forking from one parent is one problem, not three.
|
|
107
|
+
*/
|
|
108
|
+
readonly conflict: HeadConflict | null;
|
|
109
|
+
/**
|
|
110
|
+
* Ordering constraints between changes. Not conflicts — merging in the stated
|
|
111
|
+
* order is fine, and merging in the other order is not.
|
|
112
|
+
*/
|
|
113
|
+
readonly dependencies: readonly CrossPrDependency[];
|
|
114
|
+
/** Two changes declaring the same revision identifier. Always a defect. */
|
|
115
|
+
readonly duplicateRevisions: readonly {
|
|
116
|
+
revision: string;
|
|
117
|
+
prIds: readonly string[];
|
|
118
|
+
}[];
|
|
119
|
+
/**
|
|
120
|
+
* False when no change in the set adds a migration at all.
|
|
121
|
+
*
|
|
122
|
+
* **Not the same as "no conflict".** A set with no migrations and a set whose
|
|
123
|
+
* migrations form a clean chain both report `conflict: null`.
|
|
124
|
+
*/
|
|
125
|
+
readonly analysed: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* False when any record's parents were unknown, so the DAG read is partial.
|
|
128
|
+
*
|
|
129
|
+
* A finding computed over a partial DAG is conditional on what was not read —
|
|
130
|
+
* an unread migration may be the merge that resolves it — and its class is
|
|
131
|
+
* capped at C for that reason alone, regardless of the resolution of the
|
|
132
|
+
* records that *were* read.
|
|
133
|
+
*/
|
|
134
|
+
readonly complete: boolean;
|
|
135
|
+
readonly checked: {
|
|
136
|
+
readonly changes: number;
|
|
137
|
+
readonly migrations: number;
|
|
138
|
+
readonly withUnknownParents: number;
|
|
139
|
+
readonly heads: number;
|
|
140
|
+
};
|
|
141
|
+
readonly notes: readonly string[];
|
|
142
|
+
}
|
|
143
|
+
export interface MigrationHeadOptions {
|
|
144
|
+
/**
|
|
145
|
+
* The head revision of the base branch, when the caller knows it.
|
|
146
|
+
*
|
|
147
|
+
* Supplying it enables one extra check — a migration branching from a
|
|
148
|
+
* revision that is *not* the current head is building on stale history — and
|
|
149
|
+
* its absence is disclosed rather than assumed away.
|
|
150
|
+
*/
|
|
151
|
+
readonly baseHead?: string;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Head conflicts across a set of in-flight changes.
|
|
155
|
+
*
|
|
156
|
+
* Pure over records: no store, no graph, no framework. The producer that reads
|
|
157
|
+
* migration files decides `resolution`; this function propagates it.
|
|
158
|
+
*/
|
|
159
|
+
export declare function findMigrationHeadConflicts(records: readonly MigrationRecord[], options?: MigrationHeadOptions): MigrationHeadReport;
|
|
160
|
+
//# sourceMappingURL=migration-heads.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-heads.d.ts","sourceRoot":"","sources":["../../src/multipr/migration-heads.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AAGH,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;CACtC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAAC;IACvC;;;OAGG;IACH,QAAQ,CAAC,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACpD,2EAA2E;IAC3E,QAAQ,CAAC,kBAAkB,EAAE,SAAS;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACvF;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B;;;;;;;OAOG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;QACpC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CAqKrB"}
|