@descryy/core 0.3.0 → 0.5.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/dist/budget/reply.d.ts +11 -46
- package/dist/budget/reply.d.ts.map +1 -1
- package/dist/budget/reply.js +10 -39
- package/dist/budget/reply.js.map +1 -1
- package/dist/budget/size.d.ts +2 -21
- package/dist/budget/size.d.ts.map +1 -1
- package/dist/budget/size.js +3 -24
- package/dist/budget/size.js.map +1 -1
- package/dist/capabilities/preconditions.d.ts +7 -68
- package/dist/capabilities/preconditions.d.ts.map +1 -1
- package/dist/capabilities/preconditions.js +6 -57
- package/dist/capabilities/preconditions.js.map +1 -1
- package/dist/contracts/engine.d.ts +26 -12
- package/dist/contracts/engine.d.ts.map +1 -1
- package/dist/contracts/engine.js +28 -11
- package/dist/contracts/engine.js.map +1 -1
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/orphans.d.ts +28 -77
- package/dist/contracts/orphans.d.ts.map +1 -1
- package/dist/contracts/orphans.js +124 -94
- package/dist/contracts/orphans.js.map +1 -1
- package/dist/contracts/paths.d.ts +4 -114
- package/dist/contracts/paths.d.ts.map +1 -1
- package/dist/contracts/paths.js +7 -140
- package/dist/contracts/paths.js.map +1 -1
- package/dist/contracts/shapes.d.ts +13 -66
- package/dist/contracts/shapes.d.ts.map +1 -1
- package/dist/contracts/shapes.js +20 -91
- package/dist/contracts/shapes.js.map +1 -1
- package/dist/governance/budget.d.ts +12 -48
- package/dist/governance/budget.d.ts.map +1 -1
- package/dist/governance/budget.js +12 -48
- package/dist/governance/budget.js.map +1 -1
- package/dist/governance/candidate-boundary.d.ts +4 -32
- package/dist/governance/candidate-boundary.d.ts.map +1 -1
- package/dist/governance/candidate-boundary.js +4 -32
- package/dist/governance/candidate-boundary.js.map +1 -1
- package/dist/governance/escalation.d.ts +7 -45
- package/dist/governance/escalation.d.ts.map +1 -1
- package/dist/governance/escalation.js +7 -45
- package/dist/governance/escalation.js.map +1 -1
- package/dist/governance/fact-boundary.d.ts +7 -49
- package/dist/governance/fact-boundary.d.ts.map +1 -1
- package/dist/governance/fact-boundary.js +5 -43
- package/dist/governance/fact-boundary.js.map +1 -1
- package/dist/governance/finding-funnel.d.ts +11 -72
- package/dist/governance/finding-funnel.d.ts.map +1 -1
- package/dist/governance/finding-funnel.js +9 -68
- package/dist/governance/finding-funnel.js.map +1 -1
- package/dist/graph/build.d.ts +13 -75
- package/dist/graph/build.d.ts.map +1 -1
- package/dist/graph/build.js +0 -0
- package/dist/graph/build.js.map +1 -1
- package/dist/graph/cross-language.d.ts +4 -43
- package/dist/graph/cross-language.d.ts.map +1 -1
- package/dist/graph/cross-language.js +9 -77
- package/dist/graph/cross-language.js.map +1 -1
- package/dist/graph/index.d.ts +1 -1
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/merge.d.ts +8 -76
- package/dist/graph/merge.d.ts.map +1 -1
- package/dist/graph/merge.js +27 -77
- package/dist/graph/merge.js.map +1 -1
- package/dist/graph/observed-tests.d.ts +7 -68
- package/dist/graph/observed-tests.d.ts.map +1 -1
- package/dist/graph/observed-tests.js +5 -60
- package/dist/graph/observed-tests.js.map +1 -1
- package/dist/graph/persist.d.ts +2 -25
- package/dist/graph/persist.d.ts.map +1 -1
- package/dist/graph/persist.js +2 -25
- package/dist/graph/persist.js.map +1 -1
- package/dist/graph/runtime-confirmation.d.ts +14 -91
- package/dist/graph/runtime-confirmation.d.ts.map +1 -1
- package/dist/graph/runtime-confirmation.js +19 -103
- package/dist/graph/runtime-confirmation.js.map +1 -1
- package/dist/ledger/mint.d.ts +9 -43
- package/dist/ledger/mint.d.ts.map +1 -1
- package/dist/ledger/mint.js +15 -65
- package/dist/ledger/mint.js.map +1 -1
- package/dist/ledger/resolve.d.ts +13 -81
- package/dist/ledger/resolve.d.ts.map +1 -1
- package/dist/ledger/resolve.js +13 -76
- package/dist/ledger/resolve.js.map +1 -1
- package/dist/multipr/fingerprint.d.ts +5 -46
- package/dist/multipr/fingerprint.d.ts.map +1 -1
- package/dist/multipr/fingerprint.js +22 -74
- package/dist/multipr/fingerprint.js.map +1 -1
- package/dist/multipr/hidden-dependency.d.ts +13 -73
- package/dist/multipr/hidden-dependency.d.ts.map +1 -1
- package/dist/multipr/hidden-dependency.js +12 -63
- package/dist/multipr/hidden-dependency.js.map +1 -1
- package/dist/multipr/mechanical.d.ts +25 -160
- package/dist/multipr/mechanical.d.ts.map +1 -1
- package/dist/multipr/mechanical.js +21 -137
- package/dist/multipr/mechanical.js.map +1 -1
- package/dist/multipr/migration-heads.d.ts +18 -105
- package/dist/multipr/migration-heads.d.ts.map +1 -1
- package/dist/multipr/migration-heads.js +15 -84
- package/dist/multipr/migration-heads.js.map +1 -1
- package/dist/multipr/overlap.d.ts +16 -94
- package/dist/multipr/overlap.d.ts.map +1 -1
- package/dist/multipr/overlap.js +25 -105
- package/dist/multipr/overlap.js.map +1 -1
- package/dist/multipr/scope-store.d.ts +15 -80
- package/dist/multipr/scope-store.d.ts.map +1 -1
- package/dist/multipr/scope-store.js +17 -87
- package/dist/multipr/scope-store.js.map +1 -1
- package/dist/multipr/superseded.d.ts +10 -95
- package/dist/multipr/superseded.d.ts.map +1 -1
- package/dist/multipr/superseded.js +7 -60
- package/dist/multipr/superseded.js.map +1 -1
- package/dist/query/confirmed-facts.d.ts +12 -58
- package/dist/query/confirmed-facts.d.ts.map +1 -1
- package/dist/query/confirmed-facts.js +8 -47
- package/dist/query/confirmed-facts.js.map +1 -1
- package/dist/query/declared-value-closure.d.ts +13 -104
- package/dist/query/declared-value-closure.d.ts.map +1 -1
- package/dist/query/declared-value-closure.js +15 -104
- package/dist/query/declared-value-closure.js.map +1 -1
- package/dist/query/memory.d.ts +5 -9
- package/dist/query/memory.d.ts.map +1 -1
- package/dist/query/memory.js +31 -15
- package/dist/query/memory.js.map +1 -1
- package/dist/query/prominence.d.ts +6 -55
- package/dist/query/prominence.d.ts.map +1 -1
- package/dist/query/prominence.js +6 -55
- package/dist/query/prominence.js.map +1 -1
- package/dist/query/provider.d.ts +28 -81
- package/dist/query/provider.d.ts.map +1 -1
- package/dist/query/provider.js +8 -29
- package/dist/query/provider.js.map +1 -1
- package/dist/query/queries.d.ts +15 -65
- package/dist/query/queries.d.ts.map +1 -1
- package/dist/query/queries.js +43 -100
- package/dist/query/queries.js.map +1 -1
- package/dist/query/refusal-fetch.d.ts +8 -85
- package/dist/query/refusal-fetch.d.ts.map +1 -1
- package/dist/query/refusal-fetch.js +11 -93
- package/dist/query/refusal-fetch.js.map +1 -1
- package/dist/query/refusal-questions.d.ts +17 -92
- package/dist/query/refusal-questions.d.ts.map +1 -1
- package/dist/query/refusal-questions.js +14 -78
- package/dist/query/refusal-questions.js.map +1 -1
- package/dist/query/resolution-floor.d.ts +3 -23
- package/dist/query/resolution-floor.d.ts.map +1 -1
- package/dist/query/resolution-floor.js +3 -23
- package/dist/query/resolution-floor.js.map +1 -1
- package/dist/query/root-cause-score.d.ts +11 -161
- package/dist/query/root-cause-score.d.ts.map +1 -1
- package/dist/query/root-cause-score.js +6 -137
- package/dist/query/root-cause-score.js.map +1 -1
- package/dist/query/row-closure-picture.d.ts +5 -67
- package/dist/query/row-closure-picture.d.ts.map +1 -1
- package/dist/query/row-closure-picture.js +6 -67
- package/dist/query/row-closure-picture.js.map +1 -1
- package/dist/query/similar-incidents.d.ts +6 -41
- package/dist/query/similar-incidents.d.ts.map +1 -1
- package/dist/query/similar-incidents.js +15 -69
- package/dist/query/similar-incidents.js.map +1 -1
- package/dist/query/sqlite.d.ts +3 -7
- package/dist/query/sqlite.d.ts.map +1 -1
- package/dist/query/sqlite.js +13 -16
- package/dist/query/sqlite.js.map +1 -1
- package/dist/query/test-coverage.d.ts +5 -45
- package/dist/query/test-coverage.d.ts.map +1 -1
- package/dist/query/test-coverage.js +10 -53
- package/dist/query/test-coverage.js.map +1 -1
- package/dist/query/traverse.d.ts +22 -126
- package/dist/query/traverse.d.ts.map +1 -1
- package/dist/query/traverse.js +0 -0
- package/dist/query/traverse.js.map +1 -1
- package/dist/query/unresolved.d.ts +19 -164
- package/dist/query/unresolved.d.ts.map +1 -1
- package/dist/query/unresolved.js +11 -138
- package/dist/query/unresolved.js.map +1 -1
- package/dist/query/verification-status.d.ts +12 -109
- package/dist/query/verification-status.d.ts.map +1 -1
- package/dist/query/verification-status.js +9 -99
- package/dist/query/verification-status.js.map +1 -1
- package/dist/recording/migrate.d.ts +4 -10
- package/dist/recording/migrate.d.ts.map +1 -1
- package/dist/recording/migrate.js +4 -10
- package/dist/recording/migrate.js.map +1 -1
- package/dist/recording/reader.d.ts +2 -7
- package/dist/recording/reader.d.ts.map +1 -1
- package/dist/recording/reader.js +2 -7
- package/dist/recording/reader.js.map +1 -1
- package/dist/recording/redact.d.ts +6 -57
- package/dist/recording/redact.d.ts.map +1 -1
- package/dist/recording/redact.js +8 -64
- package/dist/recording/redact.js.map +1 -1
- package/dist/recording/schema.d.ts +3 -38
- package/dist/recording/schema.d.ts.map +1 -1
- package/dist/recording/schema.js +3 -38
- package/dist/recording/schema.js.map +1 -1
- package/dist/recording/writer.d.ts +11 -74
- package/dist/recording/writer.d.ts.map +1 -1
- package/dist/recording/writer.js +3 -43
- package/dist/recording/writer.js.map +1 -1
- package/dist/scoping/fanout.d.ts +6 -68
- package/dist/scoping/fanout.d.ts.map +1 -1
- package/dist/scoping/fanout.js +6 -68
- package/dist/scoping/fanout.js.map +1 -1
- package/dist/scoping/score.d.ts +20 -70
- package/dist/scoping/score.d.ts.map +1 -1
- package/dist/scoping/score.js +20 -70
- package/dist/scoping/score.js.map +1 -1
- package/dist/scoping/tiers.d.ts +4 -16
- package/dist/scoping/tiers.d.ts.map +1 -1
- package/dist/scoping/tiers.js +4 -16
- package/dist/scoping/tiers.js.map +1 -1
- package/dist/scoping/traverse.d.ts +3 -25
- package/dist/scoping/traverse.d.ts.map +1 -1
- package/dist/scoping/traverse.js +14 -49
- package/dist/scoping/traverse.js.map +1 -1
- package/dist/scoping/weights.d.ts +10 -74
- package/dist/scoping/weights.d.ts.map +1 -1
- package/dist/scoping/weights.js +14 -86
- package/dist/scoping/weights.js.map +1 -1
- package/dist/sources/confirmed/incidents.d.ts +5 -35
- package/dist/sources/confirmed/incidents.d.ts.map +1 -1
- package/dist/sources/confirmed/incidents.js +14 -48
- package/dist/sources/confirmed/incidents.js.map +1 -1
- package/dist/sources/git/diff.d.ts +71 -65
- package/dist/sources/git/diff.d.ts.map +1 -1
- package/dist/sources/git/diff.js +136 -83
- package/dist/sources/git/diff.js.map +1 -1
- package/dist/sources/git/env.d.ts +7 -0
- package/dist/sources/git/env.d.ts.map +1 -1
- package/dist/sources/git/env.js +10 -11
- package/dist/sources/git/env.js.map +1 -1
- package/dist/sources/git/history.d.ts +64 -84
- package/dist/sources/git/history.d.ts.map +1 -1
- package/dist/sources/git/history.js +141 -112
- package/dist/sources/git/history.js.map +1 -1
- package/dist/sources/git/incidents.d.ts +13 -75
- package/dist/sources/git/incidents.d.ts.map +1 -1
- package/dist/sources/git/incidents.js +21 -91
- package/dist/sources/git/incidents.js.map +1 -1
- package/dist/sources/git/index.d.ts +5 -3
- package/dist/sources/git/index.d.ts.map +1 -1
- package/dist/sources/git/index.js +3 -2
- package/dist/sources/git/index.js.map +1 -1
- package/dist/sources/git/source.d.ts +9 -79
- package/dist/sources/git/source.d.ts.map +1 -1
- package/dist/sources/git/source.js +0 -0
- package/dist/sources/git/source.js.map +1 -1
- package/dist/sources/migrations/across-change.d.ts +94 -0
- package/dist/sources/migrations/across-change.d.ts.map +1 -0
- package/dist/sources/migrations/across-change.js +278 -0
- package/dist/sources/migrations/across-change.js.map +1 -0
- package/dist/sources/migrations/dialects.d.ts +20 -36
- package/dist/sources/migrations/dialects.d.ts.map +1 -1
- package/dist/sources/migrations/dialects.js +11 -36
- package/dist/sources/migrations/dialects.js.map +1 -1
- package/dist/sources/migrations/index.d.ts +3 -1
- package/dist/sources/migrations/index.d.ts.map +1 -1
- package/dist/sources/migrations/index.js +2 -1
- package/dist/sources/migrations/index.js.map +1 -1
- package/dist/sources/migrations/read.d.ts +14 -79
- package/dist/sources/migrations/read.d.ts.map +1 -1
- package/dist/sources/migrations/read.js +14 -89
- package/dist/sources/migrations/read.js.map +1 -1
- package/dist/sources/workspace/workspace.d.ts +6 -42
- package/dist/sources/workspace/workspace.d.ts.map +1 -1
- package/dist/sources/workspace/workspace.js +10 -53
- package/dist/sources/workspace/workspace.js.map +1 -1
- package/dist/store/driver/driver.d.ts +9 -38
- package/dist/store/driver/driver.d.ts.map +1 -1
- package/dist/store/driver/driver.js +4 -22
- package/dist/store/driver/driver.js.map +1 -1
- package/dist/store/driver/node-sqlite.d.ts +3 -12
- package/dist/store/driver/node-sqlite.d.ts.map +1 -1
- package/dist/store/driver/node-sqlite.js +4 -18
- package/dist/store/driver/node-sqlite.js.map +1 -1
- package/dist/store/index.d.ts +2 -7
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +4 -10
- package/dist/store/index.js.map +1 -1
- package/dist/store/migrate.d.ts +14 -50
- package/dist/store/migrate.d.ts.map +1 -1
- package/dist/store/migrate.js +15 -52
- package/dist/store/migrate.js.map +1 -1
- package/dist/store/reader.d.ts +19 -84
- package/dist/store/reader.d.ts.map +1 -1
- package/dist/store/reader.js +25 -92
- package/dist/store/reader.js.map +1 -1
- package/dist/store/schema.d.ts +14 -76
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +14 -76
- package/dist/store/schema.js.map +1 -1
- package/dist/store/writer.d.ts +14 -88
- package/dist/store/writer.d.ts.map +1 -1
- package/dist/store/writer.js +29 -123
- package/dist/store/writer.js.map +1 -1
- package/dist/tiers/certify.d.ts +46 -281
- package/dist/tiers/certify.d.ts.map +1 -1
- package/dist/tiers/certify.js +46 -226
- package/dist/tiers/certify.js.map +1 -1
- package/dist/tiers/ladder.d.ts +7 -51
- package/dist/tiers/ladder.d.ts.map +1 -1
- package/dist/tiers/ladder.js +13 -89
- package/dist/tiers/ladder.js.map +1 -1
- package/dist/validation/attributes.d.ts +47 -0
- package/dist/validation/attributes.d.ts.map +1 -0
- package/dist/validation/attributes.js +130 -0
- package/dist/validation/attributes.js.map +1 -0
- package/dist/validation/config-graph.d.ts +7 -56
- package/dist/validation/config-graph.d.ts.map +1 -1
- package/dist/validation/config-graph.js +15 -61
- package/dist/validation/config-graph.js.map +1 -1
- package/dist/validation/env.d.ts +53 -2
- package/dist/validation/env.d.ts.map +1 -1
- package/dist/validation/env.js +99 -4
- package/dist/validation/env.js.map +1 -1
- package/dist/validation/index.d.ts +5 -1
- package/dist/validation/index.d.ts.map +1 -1
- package/dist/validation/index.js +3 -1
- package/dist/validation/index.js.map +1 -1
- package/dist/validation/join-substitution.d.ts +22 -120
- package/dist/validation/join-substitution.d.ts.map +1 -1
- package/dist/validation/join-substitution.js +13 -90
- package/dist/validation/join-substitution.js.map +1 -1
- package/dist/validation/migration-integrity.d.ts +72 -0
- package/dist/validation/migration-integrity.d.ts.map +1 -0
- package/dist/validation/migration-integrity.js +238 -0
- package/dist/validation/migration-integrity.js.map +1 -0
- package/dist/validation/migrations.d.ts +23 -88
- package/dist/validation/migrations.d.ts.map +1 -1
- package/dist/validation/migrations.js +20 -79
- package/dist/validation/migrations.js.map +1 -1
- package/dist/validation/nodes.d.ts +3 -1
- package/dist/validation/nodes.d.ts.map +1 -1
- package/dist/validation/nodes.js +3 -1
- package/dist/validation/nodes.js.map +1 -1
- package/dist/validation/rollback.d.ts +10 -73
- package/dist/validation/rollback.d.ts.map +1 -1
- package/dist/validation/rollback.js +23 -81
- package/dist/validation/rollback.js.map +1 -1
- package/dist/validation/route-drift.d.ts +9 -62
- package/dist/validation/route-drift.d.ts.map +1 -1
- package/dist/validation/route-drift.js +7 -52
- package/dist/validation/route-drift.js.map +1 -1
- package/dist/validation/sources.d.ts +16 -94
- package/dist/validation/sources.d.ts.map +1 -1
- package/dist/validation/sources.js +51 -147
- package/dist/validation/sources.js.map +1 -1
- package/dist/worker/pool.d.ts +6 -45
- package/dist/worker/pool.d.ts.map +1 -1
- package/dist/worker/pool.js +5 -42
- package/dist/worker/pool.js.map +1 -1
- package/dist/worker/protocol.d.ts +7 -20
- package/dist/worker/protocol.d.ts.map +1 -1
- package/dist/worker/protocol.js +3 -10
- package/dist/worker/protocol.js.map +1 -1
- package/dist/worker/traversal.worker.d.ts +2 -21
- package/dist/worker/traversal.worker.d.ts.map +1 -1
- package/dist/worker/traversal.worker.js +4 -23
- package/dist/worker/traversal.worker.js.map +1 -1
- package/package.json +11 -2
|
@@ -1,86 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* DEC-166
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* them is why the row looked unreachable. **Source fidelity** — does a call
|
|
6
|
-
* site's text really support the edge's claim — is
|
|
7
|
-
* `bench/precision-adjudicate.mjs`'s job and is almost entirely
|
|
8
|
-
* `null`-producing (NOTE-C). **Join / identity fidelity** — does the endpoint
|
|
9
|
-
* id a caller's edge targets correspond, and *only* correspond, to the route
|
|
10
|
-
* an independent producer says is served there — is this file's job, and it
|
|
11
|
-
* is the layer the numbers this project publishes are actually made of.
|
|
12
|
-
*
|
|
13
|
-
* ## The instrument
|
|
14
|
-
*
|
|
15
|
-
* For every joined `USES_API` edge:
|
|
16
|
-
*
|
|
17
|
-
* 1. Take the **population** — every `SERVES_API`-linked route in the same
|
|
18
|
-
* repository, deduplicated by (method, normalised template). A fact
|
|
19
|
-
* produced by a *different pass* than the one under test.
|
|
20
|
-
* 2. **Exclude** any route identical in (method, template) to the edge's true
|
|
21
|
-
* target. A group of one substitutes onto itself, which is not a wrong
|
|
22
|
-
* target. This exclusion is load-bearing: without it every draw would match
|
|
23
|
-
* by construction and the control would manufacture false acceptances.
|
|
24
|
-
* 3. Substitute a different route from that population, holding the caller's
|
|
25
|
-
* raw evidence fixed.
|
|
26
|
-
* 4. Re-run the same identity computation against the substituted target.
|
|
27
|
-
* 5. **Expect refusal, always.** The substituted target's identity did not
|
|
28
|
-
* come from the rule under test and did not come from the caller — it came
|
|
29
|
-
* from an independent producer, so the harness can say why the seeded value
|
|
30
|
-
* is wrong without consulting the rule being tested. That is precisely what
|
|
31
|
-
* NOTE-C limit (a) requires and address-keyed source rules could not give.
|
|
32
|
-
*
|
|
33
|
-
* **Threshold 0%, and it is not a dial.** Wrongness here is established by an
|
|
34
|
-
* independent producer, so 0% is the honest floor rather than a number picked
|
|
35
|
-
* to accommodate anything. A non-zero acceptance is a row to read, never a
|
|
36
|
-
* ceiling to raise.
|
|
37
|
-
*
|
|
38
|
-
* ## What this control is, honestly, and what it is not
|
|
39
|
-
*
|
|
40
|
-
* On a correct implementation this control is **structurally** 0%: the
|
|
41
|
-
* caller's evidence normalises onto its own target, the population is
|
|
42
|
-
* deduplicated by that same identity, and every other group is by definition
|
|
43
|
-
* a different string. That is not a weakness dressed up as a floor — it is
|
|
44
|
-
* what a 0% floor *means*, and it is why the number is not a dial. Its value
|
|
45
|
-
* is as a **regression detector over the normalisation layer**: change
|
|
46
|
-
* `normaliseEndpointPath` (or the DEC-097 base composition, or the method
|
|
47
|
-
* half of the identity) so that it discards discriminating information, and
|
|
48
|
-
* two genuinely distinct routes collapse onto one string — at which point the
|
|
49
|
-
* caller's evidence supports a route it never called and the acceptance rate
|
|
50
|
-
* leaves zero. It is a detector for the *class* of suspect join, not a rule
|
|
51
|
-
* that says which flagged rows are wrong; a flagged row still needs a
|
|
52
|
-
* decision, the same way DEC-163's own open question does.
|
|
53
|
-
*
|
|
54
|
-
* ## Why the exclusion key is fixed while the identity under test is not
|
|
55
|
-
*
|
|
56
|
-
* `options.identity` exists so a deliberately degraded identity can be run
|
|
57
|
-
* through the same instrument and the control observed to fire — this repo's
|
|
58
|
-
* standing bar for an instrument (DEC-162's seeded-grader work): a control
|
|
59
|
-
* that has never been seen to fire proves nothing. The population's dedup and
|
|
60
|
-
* step 2's exclusion deliberately do **not** use it. They use the engine's
|
|
61
|
-
* fixed `endpointQsp`, because they are arithmetic over an independently
|
|
62
|
-
* produced route table, not the thing under test. If the exclusion used the
|
|
63
|
-
* identity under test, a degraded identity would collapse the whole
|
|
64
|
-
* population into the target's own group, every candidate would be excluded,
|
|
65
|
-
* and the run would report `UNCONTROLLABLE` — the defect hiding inside the
|
|
66
|
-
* one state that is explicitly not a pass. `join-substitution.test.ts` pins
|
|
67
|
-
* exactly that.
|
|
2
|
+
* DEC-166's join/identity control for `USES_API` (distinct from source fidelity in `bench/precision-adjudicate.mjs`, NOTE-C).
|
|
3
|
+
* Substitutes an independent route, keyed by the fixed engine identity — never `options.identity` under test — and expects refusal always.
|
|
4
|
+
* Threshold 0%: a DEC-097 normalisation regression detector, not a dial.
|
|
68
5
|
*/
|
|
69
6
|
import { endpointQsp } from "@descryy/ir";
|
|
70
7
|
/** The fixed key: the engine's identity, never the one under test. */
|
|
71
8
|
const groupKey = (method, path) => endpointQsp(method, path);
|
|
72
|
-
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* Deterministic and exhaustive: every candidate in the population is drawn for
|
|
76
|
-
* every caller, never a fixed-N sample. Route tables are frequently far below
|
|
77
|
-
* any fixed draw size — axum shipped 18/18, full population — and a fixed-N
|
|
78
|
-
* bar would make this layer structurally uncontrollable for exactly the
|
|
79
|
-
* repositories with the smallest, most auditable route tables.
|
|
80
|
-
*/
|
|
9
|
+
/** Runs the control over one repository's joined edges and route table.
|
|
10
|
+
* Deterministic and exhaustive — every candidate is drawn for every caller,
|
|
11
|
+
* never a fixed-N sample (axum shipped 18/18, full population). */
|
|
81
12
|
export function runJoinSubstitutionControl(callers, routes, options = {}) {
|
|
82
13
|
const identity = options.identity ?? endpointQsp;
|
|
83
|
-
//
|
|
14
|
+
// Population, deduplicated by the engine's own identity.
|
|
84
15
|
const population = new Map();
|
|
85
16
|
for (const route of routes) {
|
|
86
17
|
const key = groupKey(route.method, route.path);
|
|
@@ -96,9 +27,7 @@ export function runJoinSubstitutionControl(callers, routes, options = {}) {
|
|
|
96
27
|
flagged: [],
|
|
97
28
|
unreproducible: [],
|
|
98
29
|
};
|
|
99
|
-
//
|
|
100
|
-
// names them: a repository with neither routes nor callers is first of all a
|
|
101
|
-
// repository that serves nothing.
|
|
30
|
+
// Route legs checked before caller leg, per DEC-166's order.
|
|
102
31
|
if (population.size === 0) {
|
|
103
32
|
return {
|
|
104
33
|
controllable: false,
|
|
@@ -137,12 +66,10 @@ export function runJoinSubstitutionControl(callers, routes, options = {}) {
|
|
|
137
66
|
let substitutions = 0;
|
|
138
67
|
let accepted = 0;
|
|
139
68
|
for (const caller of callers) {
|
|
140
|
-
//
|
|
141
|
-
// composition is `basePath + written`; DEC-170's query strip and DEC-163's
|
|
142
|
-
// method+template identity are inside `identity` itself.
|
|
69
|
+
// Caller's raw evidence, recomposed per DEC-097; DEC-170/DEC-163 are inside `identity`.
|
|
143
70
|
const derived = identity(caller.method, `${caller.basePath}${caller.written}`);
|
|
144
71
|
const trueIdentity = identity(caller.targetMethod, caller.targetPath);
|
|
145
|
-
//
|
|
72
|
+
// Exclude the true target's own group, under the fixed key.
|
|
146
73
|
const excluded = groupKey(caller.targetMethod, caller.targetPath);
|
|
147
74
|
if (derived !== trueIdentity) {
|
|
148
75
|
unreproducible.push({ at: caller.at, derived, trueTarget: excluded });
|
|
@@ -151,8 +78,7 @@ export function runJoinSubstitutionControl(callers, routes, options = {}) {
|
|
|
151
78
|
if (key === excluded)
|
|
152
79
|
continue;
|
|
153
80
|
substitutions += 1;
|
|
154
|
-
//
|
|
155
|
-
// against the substituted target, expect refusal.
|
|
81
|
+
// Hold the caller's evidence fixed, re-run against the substituted target.
|
|
156
82
|
if (identity(route.method, route.path) === derived) {
|
|
157
83
|
accepted += 1;
|
|
158
84
|
flagged.push({
|
|
@@ -165,11 +91,8 @@ export function runJoinSubstitutionControl(callers, routes, options = {}) {
|
|
|
165
91
|
}
|
|
166
92
|
}
|
|
167
93
|
return {
|
|
168
|
-
//
|
|
169
|
-
// the
|
|
170
|
-
// is structural rather than hoped for. Asserted through the returned flag
|
|
171
|
-
// anyway, so a future change to the population arithmetic cannot turn an
|
|
172
|
-
// empty draw into a silent pass.
|
|
94
|
+
// population >= 2 guarantees substitutions > 0 structurally; asserted via
|
|
95
|
+
// the returned flag so a future arithmetic change can't silently pass.
|
|
173
96
|
controllable: substitutions > 0,
|
|
174
97
|
...(substitutions > 0
|
|
175
98
|
? {}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"join-substitution.js","sourceRoot":"","sources":["../../src/validation/join-substitution.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"join-substitution.js","sourceRoot":"","sources":["../../src/validation/join-substitution.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Migration-integrity: location + revision-chain findings from migrationRevision/
|
|
2
|
+
* migrationDownRevision/migrationDir (UAT round 3's 3rd of 4 missed defects). Migrations
|
|
3
|
+
* directory is inferred by graph consensus, never hardcoded; `"unknown"` downRevision is refused, never null. */
|
|
4
|
+
import type { ResolutionLevel } from "@descryy/ir";
|
|
5
|
+
import type { AdjacencyProvider, QueryResult } from "../query/provider.ts";
|
|
6
|
+
/** null = declared root. string = single parent. array = merge revision. "unknown" = unreadable
|
|
7
|
+
* as any of the three — refused, never a synonym for null. */
|
|
8
|
+
export type MigrationDownRevision = string | readonly string[] | null | "unknown";
|
|
9
|
+
export interface MigrationRevisionInfo {
|
|
10
|
+
readonly revision: string;
|
|
11
|
+
readonly downRevision: MigrationDownRevision;
|
|
12
|
+
/** This file's own containing directory. `""` for the repository root. */
|
|
13
|
+
readonly dir: string;
|
|
14
|
+
readonly file: string;
|
|
15
|
+
readonly resolution: ResolutionLevel;
|
|
16
|
+
}
|
|
17
|
+
export type MigrationIntegrityFindingKind = "MIGRATION_OUTSIDE_DIRECTORY" | "BROKEN_REVISION_CHAIN";
|
|
18
|
+
export interface MigrationIntegrityFinding {
|
|
19
|
+
readonly kind: MigrationIntegrityFindingKind;
|
|
20
|
+
readonly file: string;
|
|
21
|
+
readonly revision: string;
|
|
22
|
+
/** The sentence a developer reads. */
|
|
23
|
+
readonly detail: string;
|
|
24
|
+
readonly resolution: ResolutionLevel;
|
|
25
|
+
}
|
|
26
|
+
export interface MigrationIntegrityReport {
|
|
27
|
+
readonly findings: readonly MigrationIntegrityFinding[];
|
|
28
|
+
/** The count before `maxFindings` capped it — P27's disclosed remainder: a structured field, not only a prose note. */
|
|
29
|
+
readonly findingsTotal: number;
|
|
30
|
+
readonly examined: {
|
|
31
|
+
/** Modules carrying a well-formed migrationRevision/migrationDownRevision/migrationDir triple. */
|
|
32
|
+
readonly modulesWithRevision: number;
|
|
33
|
+
};
|
|
34
|
+
/** The directory this run inferred as the project's migrations directory. Null when no consensus could be established. */
|
|
35
|
+
readonly migrationsDirectory: string | null;
|
|
36
|
+
/** True when at least one module in the graph carried migration revision attrs. */
|
|
37
|
+
readonly ran: boolean;
|
|
38
|
+
readonly withheld: {
|
|
39
|
+
/** Declared roots — never a finding. Counted for the same reason `rollback.ts` counts its refusals. */
|
|
40
|
+
readonly root: number;
|
|
41
|
+
/** downRevision recorded as "unknown" — refused, never reported as broken. */
|
|
42
|
+
readonly unreadable: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface MigrationIntegrityOptions {
|
|
46
|
+
/** Restrict findings to these repo-relative files. The revision registry itself always spans the whole graph — see the header. */
|
|
47
|
+
readonly files?: readonly string[];
|
|
48
|
+
readonly maxFindings?: number;
|
|
49
|
+
/** Cap on file-scoped nodes enumerated. Truncation is reported. */
|
|
50
|
+
readonly limit?: number;
|
|
51
|
+
}
|
|
52
|
+
/** Pulls migrationRevision/migrationDownRevision/migrationDir off a node, refusing anything
|
|
53
|
+
* malformed. A downRevision that isn't null/"unknown"/a non-empty string or string array is
|
|
54
|
+
* dropped, not guessed at — defaulting it would manufacture a chain claim never made. */
|
|
55
|
+
export declare function readMigrationRevision(attrs: Readonly<Record<string, unknown>> | undefined, file: string, resolution: ResolutionLevel): MigrationRevisionInfo | null;
|
|
56
|
+
export declare function checkMigrationIntegrity(provider: AdjacencyProvider, options?: MigrationIntegrityOptions): QueryResult<MigrationIntegrityReport>;
|
|
57
|
+
/** `cross_pr`'s migration-heads check: would merging these leave more than one head? Computed
|
|
58
|
+
* over the graph as built, not base-vs-head separately. An unreadable ("unknown") down-revision
|
|
59
|
+
* is counted and disclosed, never treated as absent — that could wrongly promote a revision with an unread child. */
|
|
60
|
+
export interface MigrationHeadsReport {
|
|
61
|
+
/** Revisions with no other migration in this graph declaring them as a parent. More than one means a fork. */
|
|
62
|
+
readonly heads: readonly string[];
|
|
63
|
+
readonly examined: {
|
|
64
|
+
readonly modulesWithRevision: number;
|
|
65
|
+
};
|
|
66
|
+
/** True when at least one module in the graph carried a migration revision. */
|
|
67
|
+
readonly ran: boolean;
|
|
68
|
+
/** Down-revisions recorded as "unknown" — a head count computed alongside these is a lower bound. */
|
|
69
|
+
readonly unreadableChains: number;
|
|
70
|
+
}
|
|
71
|
+
export declare function checkMigrationHeads(provider: AdjacencyProvider, options?: Pick<MigrationIntegrityOptions, "limit">): QueryResult<MigrationHeadsReport>;
|
|
72
|
+
//# sourceMappingURL=migration-integrity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-integrity.d.ts","sourceRoot":"","sources":["../../src/validation/migration-integrity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/** Migration-integrity: location + revision-chain findings from migrationRevision/
|
|
2
|
+
* migrationDownRevision/migrationDir (UAT round 3's 3rd of 4 missed defects). Migrations
|
|
3
|
+
* directory is inferred by graph consensus, never hardcoded; `"unknown"` downRevision is refused, never null. */
|
|
4
|
+
import { fileScopedNodes } from "./nodes.js";
|
|
5
|
+
import { MIGRATION_DIRECTORY } from "./migrations.js";
|
|
6
|
+
const DEFAULT_MAX_FINDINGS = 25;
|
|
7
|
+
/** Below this many modules, a directory count isn't a consensus — one migration can't disagree
|
|
8
|
+
* with itself, and two tied at one apiece isn't a majority either way. */
|
|
9
|
+
const MIN_MODULES_FOR_CONSENSUS = 2;
|
|
10
|
+
function isNonEmptyStringArray(value) {
|
|
11
|
+
return Array.isArray(value) && value.length > 0 && value.every((v) => typeof v === "string" && v !== "");
|
|
12
|
+
}
|
|
13
|
+
/** Pulls migrationRevision/migrationDownRevision/migrationDir off a node, refusing anything
|
|
14
|
+
* malformed. A downRevision that isn't null/"unknown"/a non-empty string or string array is
|
|
15
|
+
* dropped, not guessed at — defaulting it would manufacture a chain claim never made. */
|
|
16
|
+
export function readMigrationRevision(attrs, file, resolution) {
|
|
17
|
+
if (attrs === undefined)
|
|
18
|
+
return null;
|
|
19
|
+
const revision = attrs["migrationRevision"];
|
|
20
|
+
const downRevision = attrs["migrationDownRevision"];
|
|
21
|
+
const dir = attrs["migrationDir"];
|
|
22
|
+
if (typeof revision !== "string" || revision === "")
|
|
23
|
+
return null;
|
|
24
|
+
if (typeof dir !== "string")
|
|
25
|
+
return null;
|
|
26
|
+
let parsedDown;
|
|
27
|
+
if (downRevision === null)
|
|
28
|
+
parsedDown = null;
|
|
29
|
+
else if (downRevision === "unknown")
|
|
30
|
+
parsedDown = "unknown";
|
|
31
|
+
else if (typeof downRevision === "string" && downRevision !== "")
|
|
32
|
+
parsedDown = downRevision;
|
|
33
|
+
else if (isNonEmptyStringArray(downRevision))
|
|
34
|
+
parsedDown = downRevision;
|
|
35
|
+
else
|
|
36
|
+
return null;
|
|
37
|
+
return { revision, downRevision: parsedDown, dir, file, resolution };
|
|
38
|
+
}
|
|
39
|
+
/** Human-readable name for a directory, the root included. */
|
|
40
|
+
function nameDir(dir) {
|
|
41
|
+
return dir === "" ? "the repository root" : dir;
|
|
42
|
+
}
|
|
43
|
+
/** The directory most modules agree on. A tie is broken toward one matching migrations.ts's
|
|
44
|
+
* generic directory shape (a shape check, not a guess). A tie surviving that is genuinely
|
|
45
|
+
* ambiguous — refused and disclosed, never guessed. */
|
|
46
|
+
function consensusDirectory(infos) {
|
|
47
|
+
if (infos.length < MIN_MODULES_FOR_CONSENSUS) {
|
|
48
|
+
return {
|
|
49
|
+
dir: null,
|
|
50
|
+
note: `Only ${infos.length} module(s) in the graph carry a migration revision, which is not ` +
|
|
51
|
+
"enough to establish which directory this project keeps its migrations in. " +
|
|
52
|
+
"MIGRATION_OUTSIDE_DIRECTORY did not run.",
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const counts = new Map();
|
|
56
|
+
for (const info of infos)
|
|
57
|
+
counts.set(info.dir, (counts.get(info.dir) ?? 0) + 1);
|
|
58
|
+
const sorted = [...counts.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
|
|
59
|
+
const top = sorted[0];
|
|
60
|
+
const tied = sorted.filter(([, count]) => count === top[1]);
|
|
61
|
+
if (tied.length === 1)
|
|
62
|
+
return { dir: top[0], note: null };
|
|
63
|
+
const matching = tied.filter(([dir]) => MIGRATION_DIRECTORY.test(dir));
|
|
64
|
+
if (matching.length === 1)
|
|
65
|
+
return { dir: matching[0][0], note: null };
|
|
66
|
+
return {
|
|
67
|
+
dir: null,
|
|
68
|
+
note: `${tied.length} director(ies) are equally common among the migration-revision-carrying ` +
|
|
69
|
+
`module(s) this run saw (${tied.map(([dir]) => nameDir(dir)).join(", ")}), so no single ` +
|
|
70
|
+
"directory could be established as the project's migrations directory. " +
|
|
71
|
+
"MIGRATION_OUTSIDE_DIRECTORY did not run.",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** Every well-formed migration triple in the graph — shared by `checkMigrationIntegrity` and
|
|
75
|
+
* `checkMigrationHeads` so the two checks can't disagree on what counts as a migration node. */
|
|
76
|
+
function collectMigrationRevisions(provider, limit) {
|
|
77
|
+
const nodes = fileScopedNodes(provider, limit);
|
|
78
|
+
const infos = [];
|
|
79
|
+
for (const node of nodes.nodes) {
|
|
80
|
+
const path = node.file;
|
|
81
|
+
if (path === null)
|
|
82
|
+
continue;
|
|
83
|
+
const info = readMigrationRevision(node.attrs, path, node.resolution);
|
|
84
|
+
if (info !== null)
|
|
85
|
+
infos.push(info);
|
|
86
|
+
}
|
|
87
|
+
infos.sort((a, b) => a.file.localeCompare(b.file));
|
|
88
|
+
return { infos, truncated: nodes.truncated };
|
|
89
|
+
}
|
|
90
|
+
export function checkMigrationIntegrity(provider, options = {}) {
|
|
91
|
+
const maxFindings = options.maxFindings ?? DEFAULT_MAX_FINDINGS;
|
|
92
|
+
const scoped = options.files === undefined ? null : new Set(options.files);
|
|
93
|
+
const notes = [];
|
|
94
|
+
// The revision registry spans the whole graph even when scoped to a diff — a down_revision
|
|
95
|
+
// often names a migration outside the diff, and directory consensus is a whole-repo fact.
|
|
96
|
+
const { infos, truncated: nodesTruncated } = collectMigrationRevisions(provider, options.limit);
|
|
97
|
+
const ran = infos.length > 0;
|
|
98
|
+
const revisionProvidedBy = new Map();
|
|
99
|
+
for (const info of infos) {
|
|
100
|
+
// First file wins for a stable answer between runs — same precedent as env.ts's requiredReads.
|
|
101
|
+
const existing = revisionProvidedBy.get(info.revision);
|
|
102
|
+
if (existing === undefined || info.file < existing.file)
|
|
103
|
+
revisionProvidedBy.set(info.revision, info);
|
|
104
|
+
}
|
|
105
|
+
const { dir: migrationsDirectory, note: consensusNote } = consensusDirectory(infos);
|
|
106
|
+
if (consensusNote !== null)
|
|
107
|
+
notes.push(consensusNote);
|
|
108
|
+
const findings = [];
|
|
109
|
+
const withheld = { root: 0, unreadable: 0 };
|
|
110
|
+
for (const info of infos) {
|
|
111
|
+
if (scoped !== null && !scoped.has(info.file))
|
|
112
|
+
continue;
|
|
113
|
+
if (migrationsDirectory !== null && info.dir !== migrationsDirectory) {
|
|
114
|
+
const peers = infos.filter((i) => i.dir === migrationsDirectory).length;
|
|
115
|
+
findings.push({
|
|
116
|
+
kind: "MIGRATION_OUTSIDE_DIRECTORY",
|
|
117
|
+
file: info.file,
|
|
118
|
+
revision: info.revision,
|
|
119
|
+
detail: `${info.file} carries a migration revision (${info.revision}) but sits in ` +
|
|
120
|
+
`${nameDir(info.dir)}, not ${nameDir(migrationsDirectory)} — where ${peers} of the ` +
|
|
121
|
+
`${infos.length} migration(s) this run saw live. A migration tool that only looks in ` +
|
|
122
|
+
"one directory never reaches this file.",
|
|
123
|
+
resolution: info.resolution,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (info.downRevision === null) {
|
|
127
|
+
withheld.root += 1;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (info.downRevision === "unknown") {
|
|
131
|
+
withheld.unreadable += 1;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const parents = typeof info.downRevision === "string" ? [info.downRevision] : info.downRevision;
|
|
135
|
+
for (const parent of parents) {
|
|
136
|
+
if (revisionProvidedBy.has(parent))
|
|
137
|
+
continue;
|
|
138
|
+
findings.push({
|
|
139
|
+
kind: "BROKEN_REVISION_CHAIN",
|
|
140
|
+
file: info.file,
|
|
141
|
+
revision: info.revision,
|
|
142
|
+
detail: `${info.file}'s revision ${info.revision} names ${parent} as its parent, and no module ` +
|
|
143
|
+
"in this graph provides a migration with that revision. Either the parent was never " +
|
|
144
|
+
"read into this graph, or the chain is genuinely broken.",
|
|
145
|
+
resolution: info.resolution,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
findings.sort((a, b) => a.file.localeCompare(b.file) || a.kind.localeCompare(b.kind) || a.revision.localeCompare(b.revision));
|
|
150
|
+
const kept = findings.slice(0, maxFindings);
|
|
151
|
+
if (!ran) {
|
|
152
|
+
notes.push("No module in the graph carries a migration revision. This check reads " +
|
|
153
|
+
"attrs.migrationRevision, attrs.migrationDownRevision and attrs.migrationDir on a " +
|
|
154
|
+
"file-scoped node, and no adapter supplied them on this run. This is not a result of the " +
|
|
155
|
+
"migration chain being intact.");
|
|
156
|
+
}
|
|
157
|
+
if (withheld.unreadable > 0) {
|
|
158
|
+
notes.push(`${withheld.unreadable} migration(s)' down-revision could not be read as a single parent, a ` +
|
|
159
|
+
'merge tuple or a declared root — recorded as "unknown" by the producer. Not reported: a ' +
|
|
160
|
+
"chain this run cannot read is not evidence that it is broken.");
|
|
161
|
+
}
|
|
162
|
+
if (scoped !== null) {
|
|
163
|
+
notes.push(`Findings were restricted to ${scoped.size} scoped file(s). The revision registry itself was ` +
|
|
164
|
+
"built from every migration-revision-carrying module in the graph, because a down_revision " +
|
|
165
|
+
"commonly names a parent outside the diff, and the migrations-directory consensus is a " +
|
|
166
|
+
"fact about the whole repository.");
|
|
167
|
+
}
|
|
168
|
+
if (kept.length < findings.length) {
|
|
169
|
+
notes.push(`${findings.length - kept.length} further finding(s) were withheld: a report is capped at ` +
|
|
170
|
+
`${maxFindings} so a short list stays worth reading. They were found, not missed.`);
|
|
171
|
+
}
|
|
172
|
+
if (nodesTruncated) {
|
|
173
|
+
notes.push("More files exist in the graph than were enumerated, so this check covers a prefix of the " +
|
|
174
|
+
"repository rather than all of it.");
|
|
175
|
+
}
|
|
176
|
+
let resolutionFloor = 4;
|
|
177
|
+
for (const finding of kept)
|
|
178
|
+
if (finding.resolution < resolutionFloor)
|
|
179
|
+
resolutionFloor = finding.resolution;
|
|
180
|
+
const stamp = { ...provider.baseStamp(), resolutionFloor };
|
|
181
|
+
return {
|
|
182
|
+
data: {
|
|
183
|
+
findings: kept,
|
|
184
|
+
findingsTotal: findings.length,
|
|
185
|
+
examined: { modulesWithRevision: infos.length },
|
|
186
|
+
migrationsDirectory,
|
|
187
|
+
ran,
|
|
188
|
+
withheld,
|
|
189
|
+
},
|
|
190
|
+
stamp,
|
|
191
|
+
truncated: nodesTruncated || kept.length < findings.length,
|
|
192
|
+
notes,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export function checkMigrationHeads(provider, options = {}) {
|
|
196
|
+
const { infos, truncated } = collectMigrationRevisions(provider, options.limit);
|
|
197
|
+
const notes = [];
|
|
198
|
+
const ran = infos.length > 0;
|
|
199
|
+
const namedAsParent = new Set();
|
|
200
|
+
let unreadableChains = 0;
|
|
201
|
+
for (const info of infos) {
|
|
202
|
+
if (info.downRevision === null)
|
|
203
|
+
continue;
|
|
204
|
+
if (info.downRevision === "unknown") {
|
|
205
|
+
unreadableChains += 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const parents = typeof info.downRevision === "string" ? [info.downRevision] : info.downRevision;
|
|
209
|
+
for (const parent of parents)
|
|
210
|
+
namedAsParent.add(parent);
|
|
211
|
+
}
|
|
212
|
+
const heads = infos.map((i) => i.revision).filter((r) => !namedAsParent.has(r)).sort();
|
|
213
|
+
if (!ran) {
|
|
214
|
+
notes.push("No module in the graph carries a migration revision, so migration heads could not be " +
|
|
215
|
+
"counted. This is not a result of the chain having exactly one head.");
|
|
216
|
+
}
|
|
217
|
+
if (unreadableChains > 0) {
|
|
218
|
+
notes.push(`${unreadableChains} migration(s)' parent could not be read as any of the four recognised ` +
|
|
219
|
+
'shapes ("unknown"), so this head count is a lower bound: one of the listed heads may in ' +
|
|
220
|
+
"fact have a child this run could not identify.");
|
|
221
|
+
}
|
|
222
|
+
if (truncated) {
|
|
223
|
+
notes.push("More files exist in the graph than were enumerated, so this head count covers a prefix of " +
|
|
224
|
+
"the repository rather than all of it.");
|
|
225
|
+
}
|
|
226
|
+
let resolutionFloor = 4;
|
|
227
|
+
for (const info of infos)
|
|
228
|
+
if (info.resolution < resolutionFloor)
|
|
229
|
+
resolutionFloor = info.resolution;
|
|
230
|
+
const stamp = { ...provider.baseStamp(), resolutionFloor };
|
|
231
|
+
return {
|
|
232
|
+
data: { heads, examined: { modulesWithRevision: infos.length }, ran, unreadableChains },
|
|
233
|
+
stamp,
|
|
234
|
+
truncated,
|
|
235
|
+
notes,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
//# sourceMappingURL=migration-integrity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migration-integrity.js","sourceRoot":"","sources":["../../src/validation/migration-integrity.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,61 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* is: **zero `.sql` files inside a migration directory across fifteen reference
|
|
9
|
-
* repositories**, against 3,301 migrations written as program code. The code half
|
|
10
|
-
* arrives through `attrs.migrationOps` and is an adapter's to produce.
|
|
11
|
-
*
|
|
12
|
-
* ## The scope fence, which is what makes this Class A
|
|
13
|
-
*
|
|
14
|
-
* Appendix B.3 settles rollback readiness as Class A against two sources that
|
|
15
|
-
* called it B, and the resolution turns entirely on what is claimed:
|
|
16
|
-
*
|
|
17
|
-
* > **"this migration contains a destructive operation"** — a fact, and a
|
|
18
|
-
* > deterministic pattern match establishes it.
|
|
19
|
-
* >
|
|
20
|
-
* > **"this migration is unsafe to roll back"** — an inference, requiring the
|
|
21
|
-
* > consumer-impact analysis §15.3 explicitly separates this check from.
|
|
22
|
-
*
|
|
23
|
-
* Every sentence produced here is written to stay behind that fence. The finding
|
|
24
|
-
* names the operation, the object and the line; it does not characterise risk.
|
|
25
|
-
*
|
|
26
|
-
* ## What counts as destructive, and the things deliberately left out
|
|
27
|
-
*
|
|
28
|
-
* Only operations that **destroy data**. `DROP INDEX`, `DROP CONSTRAINT`,
|
|
29
|
-
* `DROP VIEW`, `DROP TRIGGER` and `DROP FUNCTION` are all recoverable by
|
|
30
|
-
* re-running the definition and none of them loses a row, so including them
|
|
31
|
-
* would triple the finding count with statements nobody needs to see. A rename
|
|
32
|
-
* is carried separately: the data survives, so it is not the same claim as a
|
|
33
|
-
* drop, but every reader of the old name breaks.
|
|
34
|
-
*
|
|
35
|
-
* ## Section detection is tool-blind on purpose
|
|
36
|
-
*
|
|
37
|
-
* Directional migrations mark their halves with a comment directive. Matching a
|
|
38
|
-
* specific tool's spelling would put that tool's name in engine code — rule 1,
|
|
39
|
-
* the violation the boundary lint already caught once in this directory. So the
|
|
40
|
-
* **shape** is matched instead: a comment carrying a directive word followed by
|
|
41
|
-
* an up/down keyword, in either the `+name Up` form or a bare `-- Up`. Any tool
|
|
42
|
-
* using that shape works; none of them is named.
|
|
43
|
-
*/
|
|
44
|
-
/**
|
|
45
|
-
* What an operation does to data.
|
|
46
|
-
*
|
|
47
|
-
* `RENAME` is separate because it is a different claim: the rows survive, and
|
|
48
|
-
* what breaks is every reader of the old name.
|
|
49
|
-
*/
|
|
1
|
+
/** §15.3's rollback-readiness check, format half — DDL is a format, not a
|
|
2
|
+
* language, so it stays below the IR boundary. DEC-122: zero `.sql` files
|
|
3
|
+
* across 15 repos vs 3,301 migrations as code (adapter's territory).
|
|
4
|
+
* Class A (Appx B.3): reports only "this drops data", never the inference
|
|
5
|
+
* "unsafe to roll back". Section markers matched by shape, not tool name. */
|
|
6
|
+
/** What an operation does to data. `RENAME` is separate — rows survive,
|
|
7
|
+
* but every reader of the old name breaks. */
|
|
50
8
|
export type DestructiveKind = "DROP_TABLE" | "DROP_COLUMN" | "DROP_SCHEMA" | "TRUNCATE" | "RENAME";
|
|
51
|
-
/**
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* `reverse` is the whole reason this field exists — a drop there undoes the
|
|
55
|
-
* forward migration and is supposed to be present. `unknown` is not a synonym
|
|
56
|
-
* for `forward`: it means the file declares no direction, and DEC-122 refuses to
|
|
57
|
-
* report on it.
|
|
58
|
-
*/
|
|
9
|
+
/** Which half of a directional migration a statement sits in. `reverse` is
|
|
10
|
+
* supposed to contain drops (undoing forward). `unknown` ≠ `forward` — no
|
|
11
|
+
* declared direction, and DEC-122 refuses to report on it. */
|
|
59
12
|
export type MigrationSection = "forward" | "reverse" | "unknown";
|
|
60
13
|
export interface MigrationOp {
|
|
61
14
|
readonly kind: DestructiveKind;
|
|
@@ -69,12 +22,8 @@ export interface MigrationOp {
|
|
|
69
22
|
export interface MigrationFile {
|
|
70
23
|
readonly file: string;
|
|
71
24
|
readonly ops: readonly MigrationOp[];
|
|
72
|
-
/**
|
|
73
|
-
*
|
|
74
|
-
* discounted — and so the *order* is known. Create-then-drop is a temporary
|
|
75
|
-
* object; drop-then-create is an idempotent setup script. Different reasons,
|
|
76
|
-
* and without the line they are indistinguishable.
|
|
77
|
-
*/
|
|
25
|
+
/** Objects this file creates, with line — so a later drop of one can be
|
|
26
|
+
* discounted. Create-then-drop is temporary; drop-then-create is idempotent setup. */
|
|
78
27
|
readonly creates: readonly {
|
|
79
28
|
readonly name: string;
|
|
80
29
|
readonly line: number;
|
|
@@ -92,38 +41,24 @@ export interface MigrationScan {
|
|
|
92
41
|
export interface MigrationScanOptions {
|
|
93
42
|
readonly maxDepth?: number;
|
|
94
43
|
readonly maxFiles?: number;
|
|
95
|
-
/**
|
|
96
|
-
*
|
|
97
|
-
* config-surface walk through `skipDirectory`, so the two cannot disagree about
|
|
98
|
-
* what counts as vendored.
|
|
99
|
-
*/
|
|
44
|
+
/** Adapters' declared noise dirs (DEC-110); shared with the config-surface
|
|
45
|
+
* walk via `skipDirectory` so the two can't disagree. */
|
|
100
46
|
readonly noiseDirectories?: readonly string[];
|
|
101
47
|
}
|
|
48
|
+
/** A directory holding migrations — path-based, not content-based, so a
|
|
49
|
+
* `.sql` file in `schema/` (a definition, not a change) isn't misread.
|
|
50
|
+
* Exported for `migration-integrity.ts`'s tie-break: a generic shape, no tool/framework name. */
|
|
51
|
+
export declare const MIGRATION_DIRECTORY: RegExp;
|
|
102
52
|
interface Statement {
|
|
103
53
|
readonly text: string;
|
|
104
54
|
readonly line: number;
|
|
105
55
|
}
|
|
106
|
-
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* A plain split would cut a statement in half at the first semicolon inside a
|
|
111
|
-
* default value, which is a real thing to find in a migration.
|
|
112
|
-
*
|
|
113
|
-
* **This duplicates the splitter in `adapter-sql`, deliberately and with the
|
|
114
|
-
* cost recorded** (DEC-122). `@descryy/core` cannot depend on an adapter —
|
|
115
|
-
* adapters depend on core — so the alternative to a second copy is inverting the
|
|
116
|
-
* boundary the repository split exists to hold. If a third caller appears, the
|
|
117
|
-
* right home is `@descryy/ir`.
|
|
118
|
-
*/
|
|
56
|
+
/** Splits DDL on `;`, skipping separators inside strings/identifiers/comments.
|
|
57
|
+
* Duplicates `adapter-sql`'s splitter (core can't depend on an adapter,
|
|
58
|
+
* DEC-122) — a third caller should move this to `@descryy/ir`. */
|
|
119
59
|
export declare function statements(sql: string): readonly Statement[];
|
|
120
|
-
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* Returns `null` for everything unrecognised, which is the overwhelming
|
|
124
|
-
* majority and is correct: an unclassified statement is not a silent pass, it is
|
|
125
|
-
* counted by the caller and disclosed.
|
|
126
|
-
*/
|
|
60
|
+
/** Classifies one statement; `null` for unrecognised (the majority) is
|
|
61
|
+
* correct — the caller counts and discloses it, not a silent pass. */
|
|
127
62
|
export declare function classifyStatement(text: string, line: number, section: MigrationSection): MigrationOp | null;
|
|
128
63
|
/** Read one migration file's operations, tracking which section each sits in. */
|
|
129
64
|
export declare function readMigration(text: string): Omit<MigrationFile, "file">;
|