@thomasminh1995/depverdict 0.6.0-alpha.1
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/README.md +518 -0
- package/bin/depverdict.js +7 -0
- package/bin/upgradelens.js +7 -0
- package/docs/GR-01-Semantic-Grounding-Failure-Analysis.md +309 -0
- package/docs/GR-02-Versioned-Action-Evaluation-Criteria.md +187 -0
- package/docs/GR-03-Extractive-Contract-Safety-Experiment.md +242 -0
- package/docs/GR-04-Versioned-Production-Extractive-Contract.md +154 -0
- package/docs/IA-01-Repository-Usage-Discovery.md +122 -0
- package/docs/IA-02-Repository-Impact-Analysis.md +118 -0
- package/docs/IA-03-Repository-Impact-Evidence.md +160 -0
- package/docs/IA-04-CLI-Orchestration.md +235 -0
- package/docs/IA-05-Real-Provider-Validation.md +235 -0
- package/docs/IA-05-VinGrade-Validation.md +339 -0
- package/docs/MVP-01.md +48 -0
- package/docs/MVP-02-Architecture.md +536 -0
- package/docs/MVP-02-CLI-HTTP-Runtime.md +50 -0
- package/docs/MVP-02-HTTP-Lifecycle.md +37 -0
- package/docs/MVP-02-Knowledge-Manifest-Generation.md +75 -0
- package/docs/MVP-02-Knowledge-Manifest.md +279 -0
- package/docs/MVP-02-Knowledge-Research-Orchestration.md +62 -0
- package/docs/MVP-02-Knowledge-Store.md +87 -0
- package/docs/MVP-02-PyPI-Registry-Adapter.md +97 -0
- package/docs/MVP-02-Research-Planning.md +196 -0
- package/docs/MVP-02-Source-Provenance.md +81 -0
- package/docs/MVP-02-npm-Registry-Adapter.md +99 -0
- package/docs/RR-01-End-to-End-and-Real-Provider-Validation.md +393 -0
- package/docs/RR-01-RERUN-Extractive-Contract-Validation.md +566 -0
- package/docs/RR-02-Full-Product-Workflow-and-Developer-CLI-UX-Review.md +35 -0
- package/docs/RR02-FIX-01-Persistent-Qualification-Resolution.md +32 -0
- package/docs/RR02-FIX-02-Stage-aware-CLI-Progress-and-Heartbeat.md +28 -0
- package/docs/RR02-FIX-03-npm-Capture-Evidence-Exclusion.md +33 -0
- package/docs/RR02-FIX-03A-Complete-Package-Exclusion-and-Evidence-Commit.md +223 -0
- package/docs/RR02-FIX-04-Event-loop-safe-Heartbeat.md +393 -0
- package/docs/RR02-FIX-05-Materialize-Persisted-Qualification.md +207 -0
- package/docs/RR02-RERUN-CLI-Qualification-Progress-UX-and-Package-Validation.md +342 -0
- package/docs/VinGrade-MVP-02-Validation.md +480 -0
- package/docs/VinGrade-RC02-Live-Validation.md +273 -0
- package/docs/ai-capability-discovery.md +573 -0
- package/docs/ai-engineering-production-readiness.md +390 -0
- package/docs/ai-engineering-review.md +251 -0
- package/docs/ai-runtime-governance-discovery.md +754 -0
- package/docs/architecture-overview.md +78 -0
- package/docs/cli-progress.md +111 -0
- package/docs/decisions/diff-01-brand-distribution-identity.md +450 -0
- package/docs/decisions/diff-02-identity-compatibility-contract.md +274 -0
- package/docs/decisions/diff-03-repository-docs-community-migration.md +165 -0
- package/docs/decisions/diff-04-release-evidence-gap-acceptance.md +110 -0
- package/docs/discovery/mvp-05-ai-migration-planning-discovery.md +350 -0
- package/docs/gateway-runtime-discovery.md +614 -0
- package/docs/live-ai-validation.md +299 -0
- package/docs/migration-planning-qualification-resolution.md +99 -0
- package/docs/migrations/upgradelens-to-depverdict.md +97 -0
- package/docs/mp-r03-deterministic-upgrade-decision-architecture.md +135 -0
- package/docs/mp-r04-evidence-bounded-migration-handoff-architecture.md +116 -0
- package/docs/mp-r05-product-completion-and-decision-first-cli-architecture.md +162 -0
- package/docs/mvp-05-deterministic-context-runtime.md +127 -0
- package/docs/mvp-05-migration-checklist-contract.md +166 -0
- package/docs/mvp-05-migration-checklist-orchestration.md +105 -0
- package/docs/mvp-05-migration-evaluation-and-qualification.md +135 -0
- package/docs/mvp-05-provider-neutral-generator.md +79 -0
- package/docs/ollama-local-smoke-validation.md +156 -0
- package/docs/openai-compatible-runtime-discovery.md +789 -0
- package/docs/openrouter-one-dependency-validation.md +205 -0
- package/docs/oss-02-package-guard-hardening-architecture.md +131 -0
- package/docs/oss-04-public-ci-package-metadata-architecture.md +142 -0
- package/docs/package-content-policy.md +98 -0
- package/docs/releases/v0.5.0-technical-preview.md +114 -0
- package/docs/releases/v0.6.0-alpha.1-depverdict-preview.md +101 -0
- package/docs/reviews/diff-02-identity-contract-compatibility.md +413 -0
- package/docs/reviews/diff-03-repository-docs-community-migration.md +364 -0
- package/docs/reviews/diff-04-depverdict-distribution-identity-readiness-rereview.md +453 -0
- package/docs/reviews/diff-04-fix-post-rename-identity-release-remediation.md +310 -0
- package/docs/reviews/diff-05-final-preview-distribution-qualification.md +561 -0
- package/docs/reviews/mvp-05-final-product-value-workflow-rereview.md +471 -0
- package/docs/reviews/mvp-05-product-workflow-review.md +605 -0
- package/docs/reviews/oss-01-duplicate-artifact-investigation-cleanup.md +411 -0
- package/docs/reviews/oss-02-package-guard-hardening.md +303 -0
- package/docs/reviews/oss-03-community-scaffolding.md +378 -0
- package/docs/reviews/oss-04-public-ci-package-metadata.md +439 -0
- package/docs/reviews/oss-05-technical-preview-qualification.md +482 -0
- package/docs/reviews/upgradelens-vs-upgradedepdetective-source-comparison.md +355 -0
- package/docs/reviews/v0.5.0-pre-release-smoke.md +263 -0
- package/docs/reviews/v0.5.0-version-bump-release-verification.md +306 -0
- package/docs/runtime-contract-discovery.md +521 -0
- package/docs/structured-output-compatibility-report.md +100 -0
- package/docs/ts-fix-01-exact-duplicate-occurrence-target-selection-architecture.md +111 -0
- package/docs/version-analysis-architecture.md +827 -0
- package/eval/README.md +86 -0
- package/eval/datasets/generic/declared-constraint.json +59 -0
- package/eval/datasets/generic/evidence-conflict.json +73 -0
- package/eval/datasets/generic/major-breaking-release.json +66 -0
- package/eval/datasets/generic/missing-evidence.json +46 -0
- package/eval/datasets/generic/patch-release-low.json +59 -0
- package/eval/datasets/node/axios-patch-low.json +59 -0
- package/eval/datasets/node/react-major-breaking.json +66 -0
- package/eval/datasets/node/react-minor-compatibility.json +66 -0
- package/eval/datasets/python/fastapi-deprecation.json +66 -0
- package/eval/datasets/python/pydantic-major-breaking.json +66 -0
- package/eval/migration-planning/golden-dataset-v2.json +306 -0
- package/eval/migration-planning/golden-dataset.json +214 -0
- package/eval/schemas/expected-result.schema.json +88 -0
- package/eval/schemas/golden-case.schema.json +181 -0
- package/package.json +57 -0
- package/schemas/ai-scorecard.schema.json +132 -0
- package/schemas/benchmark-report.schema.json +189 -0
- package/schemas/benchmark.schema.json +55 -0
- package/schemas/capability-profile.schema.json +44 -0
- package/schemas/conformance-report.schema.json +150 -0
- package/schemas/deployment-profile.schema.json +64 -0
- package/schemas/evaluation-report.schema.json +158 -0
- package/schemas/knowledge-evidence-bundle.schema.json +150 -0
- package/schemas/knowledge-manifest.schema.json +548 -0
- package/schemas/metrics.schema.json +178 -0
- package/schemas/migration-checklist-extractive-candidate.schema.json +42 -0
- package/schemas/migration-checklist.schema.json +706 -0
- package/schemas/migration-evaluation-dataset-v2.schema.json +208 -0
- package/schemas/migration-evaluation-dataset.schema.json +204 -0
- package/schemas/migration-planning-qualification-record.schema.json +234 -0
- package/schemas/project-manifest.schema.json +308 -0
- package/schemas/qualification-record.schema.json +56 -0
- package/schemas/repository-impact-evidence.schema.json +232 -0
- package/schemas/repository-impact.schema.json +202 -0
- package/schemas/upgrade-decision.schema.json +273 -0
- package/schemas/usage-index.schema.json +179 -0
- package/schemas/version-analysis.schema.json +449 -0
- package/src/ai-runtime-debug.js +325 -0
- package/src/ai-runtime-error.js +42 -0
- package/src/ai-runtime.js +174 -0
- package/src/ai-scorecard.js +204 -0
- package/src/ai-version-analysis.js +484 -0
- package/src/artifact-root-compatibility.js +91 -0
- package/src/benchmark-report.js +111 -0
- package/src/benchmark-runner.js +191 -0
- package/src/canonical-json.js +69 -0
- package/src/cli.js +1299 -0
- package/src/conformance-report.js +158 -0
- package/src/conformance-runner.js +253 -0
- package/src/constants.js +73 -0
- package/src/cooperative-scheduler.js +79 -0
- package/src/dependencies.js +44 -0
- package/src/dependency-ai-context.js +625 -0
- package/src/detectors.js +253 -0
- package/src/discovery.js +234 -0
- package/src/ecosystem-version-adapter.js +294 -0
- package/src/environment-compatibility.js +77 -0
- package/src/evaluation-comparator.js +158 -0
- package/src/evaluation-report.js +76 -0
- package/src/evaluation-runner.js +248 -0
- package/src/evidence-source-adapter.js +472 -0
- package/src/files.js +89 -0
- package/src/governance-diagnostics.js +64 -0
- package/src/governance-loader.js +63 -0
- package/src/governance-metadata.js +346 -0
- package/src/governance-validator.js +360 -0
- package/src/http/bounded-fetch.js +278 -0
- package/src/http/cli-http-runtime.js +44 -0
- package/src/impact/input-loader.js +157 -0
- package/src/impact/matcher.js +40 -0
- package/src/impact/repository-impact.js +199 -0
- package/src/impact/runtime.js +24 -0
- package/src/impact/status.js +62 -0
- package/src/impact/writer.js +30 -0
- package/src/impact-evidence/input-loader.js +202 -0
- package/src/impact-evidence/repository-impact-evidence.js +234 -0
- package/src/impact-evidence/runtime.js +16 -0
- package/src/impact-evidence/writer.js +30 -0
- package/src/index.js +563 -0
- package/src/installed-version-baseline.js +196 -0
- package/src/knowledge-cache.js +324 -0
- package/src/knowledge-evidence-bundle.js +101 -0
- package/src/knowledge-evidence-producer.js +233 -0
- package/src/knowledge-manifest-builder.js +188 -0
- package/src/knowledge-manifest-writer.js +32 -0
- package/src/knowledge-manifest.js +255 -0
- package/src/knowledge-research.js +615 -0
- package/src/metrics-engine.js +205 -0
- package/src/migration-checklist/ai-candidate.js +320 -0
- package/src/migration-checklist/assembler.js +37 -0
- package/src/migration-checklist/context-runtime.js +828 -0
- package/src/migration-checklist/evaluation/action-criteria.js +244 -0
- package/src/migration-checklist/evaluation/comparator-v2.js +332 -0
- package/src/migration-checklist/evaluation/comparator.js +279 -0
- package/src/migration-checklist/evaluation/dataset-v2.js +227 -0
- package/src/migration-checklist/evaluation/dataset.js +336 -0
- package/src/migration-checklist/evaluation/extractive-fixtures-v2.js +148 -0
- package/src/migration-checklist/evaluation/metrics-v2.js +226 -0
- package/src/migration-checklist/evaluation/metrics.js +158 -0
- package/src/migration-checklist/evaluation/qualification-v2.js +321 -0
- package/src/migration-checklist/evaluation/qualification.js +239 -0
- package/src/migration-checklist/evaluation/runner-v2.js +294 -0
- package/src/migration-checklist/evaluation/runner.js +194 -0
- package/src/migration-checklist/evaluation/scorecard-v2.js +106 -0
- package/src/migration-checklist/evaluation/scorecard.js +86 -0
- package/src/migration-checklist/extractive-candidate.js +166 -0
- package/src/migration-checklist/extractive-prompt.js +62 -0
- package/src/migration-checklist/generator.js +702 -0
- package/src/migration-checklist/grounding-policy.js +117 -0
- package/src/migration-checklist/input-loader.js +613 -0
- package/src/migration-checklist/migration-checklist.js +635 -0
- package/src/migration-checklist/presentation.js +292 -0
- package/src/migration-checklist/progress.js +96 -0
- package/src/migration-checklist/prompt.js +83 -0
- package/src/migration-checklist/qualification-guard.js +462 -0
- package/src/migration-checklist/qualification-resolution.js +122 -0
- package/src/migration-checklist/qualification-store.js +225 -0
- package/src/migration-checklist/runtime.js +205 -0
- package/src/migration-checklist/verification.js +134 -0
- package/src/migration-checklist/writer.js +35 -0
- package/src/openai-compatible-provider.js +451 -0
- package/src/orchestration/failure-log.js +32 -0
- package/src/orchestration/pipeline.js +200 -0
- package/src/orchestration/progress-events.js +337 -0
- package/src/orchestration/progress-reporter.js +131 -0
- package/src/orchestration/text-writer.js +22 -0
- package/src/portable.js +13 -0
- package/src/product-completion.js +249 -0
- package/src/project-manifest-input.js +90 -0
- package/src/project-manifest.js +141 -0
- package/src/python-requirements.js +137 -0
- package/src/registry/npm-packument.js +256 -0
- package/src/registry/npm-registry-adapter.js +262 -0
- package/src/registry/pypi-project.js +300 -0
- package/src/registry/pypi-registry-adapter.js +235 -0
- package/src/registry/sanitize-registry-body.js +51 -0
- package/src/renderers/console.js +160 -0
- package/src/renderers/impact-presentation.js +278 -0
- package/src/renderers/markdown.js +172 -0
- package/src/research-plan.js +455 -0
- package/src/runtime-conformance.js +275 -0
- package/src/source-provenance.js +393 -0
- package/src/source-url.js +62 -0
- package/src/structured-output-schema.js +66 -0
- package/src/target-selector.js +306 -0
- package/src/upgrade-decision/input-loader.js +107 -0
- package/src/upgrade-decision/presentation.js +43 -0
- package/src/upgrade-decision/runtime.js +21 -0
- package/src/upgrade-decision/upgrade-decision.js +626 -0
- package/src/upgrade-decision/writer.js +30 -0
- package/src/usage/analyzer-registry.js +63 -0
- package/src/usage/coverage.js +116 -0
- package/src/usage/input-loader.js +139 -0
- package/src/usage/js/analyzer.js +240 -0
- package/src/usage/js/parser.js +21 -0
- package/src/usage/runtime.js +187 -0
- package/src/usage/scope.js +44 -0
- package/src/usage/source-files.js +50 -0
- package/src/usage/usage-index.js +217 -0
- package/src/usage/writer.js +31 -0
- package/src/version-analysis-loader.js +203 -0
- package/src/version-analysis-manifest.js +314 -0
- package/src/version-analysis-writer.js +30 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import { canonicalJson, canonicalJsonBytes } from '../canonical-json.js';
|
|
4
|
+
import { compareText } from '../portable.js';
|
|
5
|
+
import {
|
|
6
|
+
loadMigrationEvaluationDatasetV2
|
|
7
|
+
} from './evaluation/dataset-v2.js';
|
|
8
|
+
import {
|
|
9
|
+
migrationActionEvaluationCriteriaDigest,
|
|
10
|
+
migrationActionEvaluationCriteriaIdentity
|
|
11
|
+
} from './evaluation/action-criteria.js';
|
|
12
|
+
import {
|
|
13
|
+
MIGRATION_EXTRACTIVE_GENERATOR_TRUST_SOURCE_IDENTITY,
|
|
14
|
+
MIGRATION_EXTRACTIVE_QUALIFICATION_POLICY_V2_VERSION,
|
|
15
|
+
migrationExtractiveQualificationPolicyV2Digest
|
|
16
|
+
} from './evaluation/qualification-v2.js';
|
|
17
|
+
import {
|
|
18
|
+
MIGRATION_EXTRACTIVE_PRESENTATION,
|
|
19
|
+
migrationExtractiveCandidateSchemaDigest
|
|
20
|
+
} from './extractive-candidate.js';
|
|
21
|
+
import {
|
|
22
|
+
MIGRATION_EXTRACTIVE_PLANNING_TASK,
|
|
23
|
+
MIGRATION_EXTRACTIVE_PROMPT_VERSION,
|
|
24
|
+
migrationExtractivePromptDigest
|
|
25
|
+
} from './extractive-prompt.js';
|
|
26
|
+
import {
|
|
27
|
+
buildMigrationPlanningQualificationRecord
|
|
28
|
+
} from './qualification-store.js';
|
|
29
|
+
|
|
30
|
+
export const MIGRATION_QUALIFICATION_STATES = Object.freeze([
|
|
31
|
+
'QUALIFIED',
|
|
32
|
+
'QUALIFIED_WITH_LIMITATIONS',
|
|
33
|
+
'NOT_QUALIFIED',
|
|
34
|
+
'MISSING',
|
|
35
|
+
'IDENTITY_MISMATCH',
|
|
36
|
+
'CORRUPTED',
|
|
37
|
+
'INSUFFICIENT_EVIDENCE'
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
export class MigrationQualificationError extends Error {
|
|
41
|
+
constructor(code, message, decision = null) {
|
|
42
|
+
super(message);
|
|
43
|
+
this.name = 'MigrationQualificationError';
|
|
44
|
+
this.code = code;
|
|
45
|
+
this.decision = decision;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function deepFreeze(value) {
|
|
50
|
+
if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
|
|
51
|
+
for (const child of Object.values(value)) deepFreeze(child);
|
|
52
|
+
return Object.freeze(value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function limitation(code, message) {
|
|
56
|
+
return { code, message };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function sortedUniqueLimitations(values) {
|
|
60
|
+
const unique = new Map();
|
|
61
|
+
for (const item of values) unique.set(`${item.code}\0${item.message}`, structuredClone(item));
|
|
62
|
+
return [...unique.values()].sort((left, right) => (
|
|
63
|
+
compareText(left.code, right.code) || compareText(left.message, right.message)
|
|
64
|
+
));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function normalizedMigrationRuntimeMetadata(runtimeMetadata = {}) {
|
|
68
|
+
return {
|
|
69
|
+
provider: typeof runtimeMetadata.provider === 'string' && runtimeMetadata.provider.length > 0
|
|
70
|
+
? runtimeMetadata.provider : 'unknown',
|
|
71
|
+
model: typeof runtimeMetadata.model === 'string' && runtimeMetadata.model.length > 0
|
|
72
|
+
? runtimeMetadata.model : 'unknown',
|
|
73
|
+
adapter: typeof runtimeMetadata.adapter === 'string' && runtimeMetadata.adapter.length > 0
|
|
74
|
+
? runtimeMetadata.adapter : 'unknown'
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function identityMatches(actual, expected) {
|
|
79
|
+
if (!actual) return false;
|
|
80
|
+
for (const field of [
|
|
81
|
+
'task', 'datasetId', 'datasetVersion', 'datasetDigest',
|
|
82
|
+
'evaluationCriteriaId', 'evaluationCriteriaVersion', 'evaluationCriteriaDigest',
|
|
83
|
+
'comparatorVersion', 'normalizationVersion',
|
|
84
|
+
'policyVersion', 'policyDigest', 'promptVersion', 'promptDigest',
|
|
85
|
+
'candidateSchemaDigest', 'deterministicPresentationIdentity'
|
|
86
|
+
]) {
|
|
87
|
+
if (actual[field] !== expected[field]) return false;
|
|
88
|
+
}
|
|
89
|
+
if (canonicalJson(actual.generatorTrustSourceIdentity) !== canonicalJson(expected.generatorTrustSourceIdentity)) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return actual.runtime?.mode === 'real'
|
|
93
|
+
&& actual.runtime.provider === expected.runtime.provider
|
|
94
|
+
&& actual.runtime.model === expected.runtime.model
|
|
95
|
+
&& actual.runtime.adapter === expected.runtime.adapter
|
|
96
|
+
&& canonicalJson(actual.runtime.observedProviders)
|
|
97
|
+
=== canonicalJson(expected.runtime.observedProviders)
|
|
98
|
+
&& canonicalJson(actual.runtime.observedModels)
|
|
99
|
+
=== canonicalJson(expected.runtime.observedModels);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function migrationQualificationIdentityDigest(identity) {
|
|
103
|
+
return `sha256:${createHash('sha256').update(canonicalJsonBytes(identity)).digest('hex')}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function experimentalBaseLimitations() {
|
|
107
|
+
return [
|
|
108
|
+
limitation(
|
|
109
|
+
'EXPERIMENTAL_MIGRATION_CHECKLIST',
|
|
110
|
+
'Migration Checklist is experimental and every generated instruction requires human review.'
|
|
111
|
+
),
|
|
112
|
+
limitation(
|
|
113
|
+
'EXTRACTIVE_SEMANTIC_APPLICABILITY_NOT_VERIFIED',
|
|
114
|
+
'Exact selected guidance proves provenance and structural safety, not semantic applicability to this repository.'
|
|
115
|
+
)
|
|
116
|
+
];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async function expectedQualificationIdentity(runtimeMetadata) {
|
|
120
|
+
const runtime = normalizedMigrationRuntimeMetadata(runtimeMetadata);
|
|
121
|
+
const dataset = await loadMigrationEvaluationDatasetV2();
|
|
122
|
+
const criteria = migrationActionEvaluationCriteriaIdentity();
|
|
123
|
+
return {
|
|
124
|
+
task: MIGRATION_EXTRACTIVE_PLANNING_TASK,
|
|
125
|
+
datasetId: dataset.datasetId,
|
|
126
|
+
datasetVersion: dataset.schemaVersion,
|
|
127
|
+
datasetDigest: dataset.datasetDigest,
|
|
128
|
+
evaluationCriteriaId: criteria.evaluationCriteriaId,
|
|
129
|
+
evaluationCriteriaVersion: criteria.evaluationCriteriaVersion,
|
|
130
|
+
evaluationCriteriaDigest: migrationActionEvaluationCriteriaDigest(),
|
|
131
|
+
comparatorVersion: criteria.comparatorVersion,
|
|
132
|
+
normalizationVersion: criteria.normalizationVersion,
|
|
133
|
+
policyVersion: MIGRATION_EXTRACTIVE_QUALIFICATION_POLICY_V2_VERSION,
|
|
134
|
+
policyDigest: migrationExtractiveQualificationPolicyV2Digest(),
|
|
135
|
+
promptVersion: MIGRATION_EXTRACTIVE_PROMPT_VERSION,
|
|
136
|
+
promptDigest: migrationExtractivePromptDigest(),
|
|
137
|
+
candidateSchemaDigest: migrationExtractiveCandidateSchemaDigest(),
|
|
138
|
+
generatorTrustSourceIdentity: MIGRATION_EXTRACTIVE_GENERATOR_TRUST_SOURCE_IDENTITY,
|
|
139
|
+
deterministicPresentationIdentity: MIGRATION_EXTRACTIVE_PRESENTATION,
|
|
140
|
+
runtime: {
|
|
141
|
+
mode: 'real',
|
|
142
|
+
...runtime,
|
|
143
|
+
observedProviders: [runtime.provider],
|
|
144
|
+
observedModels: [runtime.model]
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function qualificationDecision({
|
|
150
|
+
status,
|
|
151
|
+
reasonCode,
|
|
152
|
+
executionAllowed,
|
|
153
|
+
experimentalOverrideUsed,
|
|
154
|
+
qualificationId,
|
|
155
|
+
identity,
|
|
156
|
+
runtimeIdentity,
|
|
157
|
+
recordRuntimeIdentity = null,
|
|
158
|
+
sourceKind,
|
|
159
|
+
sourcePath = null,
|
|
160
|
+
limitations,
|
|
161
|
+
nextAction
|
|
162
|
+
}) {
|
|
163
|
+
return deepFreeze({
|
|
164
|
+
status,
|
|
165
|
+
state: status,
|
|
166
|
+
reasonCode,
|
|
167
|
+
executionAllowed,
|
|
168
|
+
experimentalOverrideUsed,
|
|
169
|
+
qualificationId,
|
|
170
|
+
task: MIGRATION_EXTRACTIVE_PLANNING_TASK,
|
|
171
|
+
identity,
|
|
172
|
+
runtimeIdentity: structuredClone(runtimeIdentity),
|
|
173
|
+
recordRuntimeIdentity: recordRuntimeIdentity
|
|
174
|
+
? {
|
|
175
|
+
provider: recordRuntimeIdentity.provider,
|
|
176
|
+
model: recordRuntimeIdentity.model,
|
|
177
|
+
adapter: recordRuntimeIdentity.adapter
|
|
178
|
+
}
|
|
179
|
+
: null,
|
|
180
|
+
sourceKind,
|
|
181
|
+
sourcePath,
|
|
182
|
+
limitations: sortedUniqueLimitations(limitations),
|
|
183
|
+
nextAction
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function blockedMessage(decision) {
|
|
188
|
+
if (decision.status === 'NOT_QUALIFIED') {
|
|
189
|
+
return 'The matching provider/model failed a critical migration-planning.v2 qualification gate.';
|
|
190
|
+
}
|
|
191
|
+
if (decision.status === 'IDENTITY_MISMATCH') {
|
|
192
|
+
return 'The persisted Migration Planning qualification does not match the current runtime identity.';
|
|
193
|
+
}
|
|
194
|
+
if (decision.status === 'CORRUPTED') {
|
|
195
|
+
return 'The selected Migration Planning qualification source failed schema or integrity validation.';
|
|
196
|
+
}
|
|
197
|
+
if (decision.status === 'INSUFFICIENT_EVIDENCE') {
|
|
198
|
+
return 'The matching Migration Planning qualification has insufficient evidence for execution.';
|
|
199
|
+
}
|
|
200
|
+
return 'A matching real-provider qualification is required for migration-planning.v2.';
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function migrationQualificationErrorForDecision(decision) {
|
|
204
|
+
return new MigrationQualificationError(
|
|
205
|
+
decision.reasonCode,
|
|
206
|
+
blockedMessage(decision),
|
|
207
|
+
decision
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function createMigrationQualificationSourceFailureDecision({
|
|
212
|
+
status,
|
|
213
|
+
reasonCode,
|
|
214
|
+
runtimeMetadata,
|
|
215
|
+
sourceKind,
|
|
216
|
+
sourcePath = null,
|
|
217
|
+
limitationCode,
|
|
218
|
+
limitationMessage,
|
|
219
|
+
nextAction
|
|
220
|
+
}) {
|
|
221
|
+
const identity = await expectedQualificationIdentity(runtimeMetadata);
|
|
222
|
+
return qualificationDecision({
|
|
223
|
+
status,
|
|
224
|
+
reasonCode,
|
|
225
|
+
executionAllowed: false,
|
|
226
|
+
experimentalOverrideUsed: false,
|
|
227
|
+
qualificationId: null,
|
|
228
|
+
identity,
|
|
229
|
+
runtimeIdentity: identity.runtime,
|
|
230
|
+
sourceKind,
|
|
231
|
+
sourcePath,
|
|
232
|
+
limitations: [
|
|
233
|
+
...experimentalBaseLimitations(),
|
|
234
|
+
limitation(limitationCode, limitationMessage)
|
|
235
|
+
],
|
|
236
|
+
nextAction
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Produce one immutable source-of-truth decision for the exact migration task/runtime identity.
|
|
242
|
+
* Only a missing record may use the explicit experimental execution exception.
|
|
243
|
+
*/
|
|
244
|
+
export async function decideMigrationQualification({
|
|
245
|
+
qualification = null,
|
|
246
|
+
runtimeMetadata,
|
|
247
|
+
allowExperimental = false,
|
|
248
|
+
sourceKind = qualification ? 'injected' : 'none',
|
|
249
|
+
sourcePath = null
|
|
250
|
+
} = {}) {
|
|
251
|
+
const expectedIdentity = await expectedQualificationIdentity(runtimeMetadata);
|
|
252
|
+
const runtime = expectedIdentity.runtime;
|
|
253
|
+
|
|
254
|
+
let identityDigestValid = !qualification;
|
|
255
|
+
if (qualification) {
|
|
256
|
+
try {
|
|
257
|
+
identityDigestValid = qualification.qualificationId
|
|
258
|
+
=== migrationQualificationIdentityDigest(qualification.identity);
|
|
259
|
+
} catch {
|
|
260
|
+
identityDigestValid = false;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (!identityDigestValid) {
|
|
264
|
+
return qualificationDecision({
|
|
265
|
+
status: 'CORRUPTED',
|
|
266
|
+
reasonCode: 'MIGRATION_QUALIFICATION_IDENTITY_CORRUPT',
|
|
267
|
+
executionAllowed: false,
|
|
268
|
+
experimentalOverrideUsed: false,
|
|
269
|
+
qualificationId: null,
|
|
270
|
+
identity: expectedIdentity,
|
|
271
|
+
runtimeIdentity: runtime,
|
|
272
|
+
recordRuntimeIdentity: qualification.identity?.runtime,
|
|
273
|
+
sourceKind,
|
|
274
|
+
sourcePath,
|
|
275
|
+
limitations: [
|
|
276
|
+
...experimentalBaseLimitations(),
|
|
277
|
+
limitation(
|
|
278
|
+
'MIGRATION_QUALIFICATION_IDENTITY_CORRUPT',
|
|
279
|
+
'The Migration Planning qualification identity digest is invalid.'
|
|
280
|
+
)
|
|
281
|
+
],
|
|
282
|
+
nextAction: 'REPLACE_QUALIFICATION_RECORD'
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
if (qualification) {
|
|
286
|
+
try {
|
|
287
|
+
qualification = buildMigrationPlanningQualificationRecord(qualification).qualification;
|
|
288
|
+
} catch (error) {
|
|
289
|
+
return qualificationDecision({
|
|
290
|
+
status: 'CORRUPTED',
|
|
291
|
+
reasonCode: error?.code ?? 'MIGRATION_QUALIFICATION_RECORD_INVALID',
|
|
292
|
+
executionAllowed: false,
|
|
293
|
+
experimentalOverrideUsed: false,
|
|
294
|
+
qualificationId: null,
|
|
295
|
+
identity: expectedIdentity,
|
|
296
|
+
runtimeIdentity: runtime,
|
|
297
|
+
recordRuntimeIdentity: qualification.identity?.runtime,
|
|
298
|
+
sourceKind,
|
|
299
|
+
sourcePath,
|
|
300
|
+
limitations: [
|
|
301
|
+
...experimentalBaseLimitations(),
|
|
302
|
+
limitation(
|
|
303
|
+
error?.code ?? 'MIGRATION_QUALIFICATION_RECORD_INVALID',
|
|
304
|
+
'The Migration Planning qualification failed strict schema or invariant validation.'
|
|
305
|
+
)
|
|
306
|
+
],
|
|
307
|
+
nextAction: 'REPLACE_QUALIFICATION_RECORD'
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const matches = identityMatches(qualification?.identity, expectedIdentity);
|
|
313
|
+
if (matches && qualification?.verdict === 'NOT_QUALIFIED') {
|
|
314
|
+
return qualificationDecision({
|
|
315
|
+
status: 'NOT_QUALIFIED',
|
|
316
|
+
reasonCode: 'MIGRATION_RUNTIME_NOT_QUALIFIED',
|
|
317
|
+
executionAllowed: false,
|
|
318
|
+
experimentalOverrideUsed: false,
|
|
319
|
+
qualificationId: qualification.qualificationId,
|
|
320
|
+
identity: expectedIdentity,
|
|
321
|
+
runtimeIdentity: runtime,
|
|
322
|
+
recordRuntimeIdentity: qualification.identity.runtime,
|
|
323
|
+
sourceKind,
|
|
324
|
+
sourcePath,
|
|
325
|
+
limitations: [
|
|
326
|
+
...experimentalBaseLimitations(),
|
|
327
|
+
...(qualification.limitations ?? []).map(({ code, message }) => ({ code, message })),
|
|
328
|
+
limitation(
|
|
329
|
+
'MIGRATION_RUNTIME_NOT_QUALIFIED',
|
|
330
|
+
'The matching provider/model failed a critical migration-planning.v2 qualification gate.'
|
|
331
|
+
)
|
|
332
|
+
],
|
|
333
|
+
nextAction: 'REQUALIFY_RUNTIME'
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
if (matches && qualification?.verdict === 'INSUFFICIENT_EVIDENCE') {
|
|
337
|
+
return qualificationDecision({
|
|
338
|
+
status: 'INSUFFICIENT_EVIDENCE',
|
|
339
|
+
reasonCode: 'MIGRATION_QUALIFICATION_INSUFFICIENT',
|
|
340
|
+
executionAllowed: false,
|
|
341
|
+
experimentalOverrideUsed: false,
|
|
342
|
+
qualificationId: qualification.qualificationId,
|
|
343
|
+
identity: expectedIdentity,
|
|
344
|
+
runtimeIdentity: runtime,
|
|
345
|
+
recordRuntimeIdentity: qualification.identity.runtime,
|
|
346
|
+
sourceKind,
|
|
347
|
+
sourcePath,
|
|
348
|
+
limitations: [
|
|
349
|
+
...experimentalBaseLimitations(),
|
|
350
|
+
...(qualification.limitations ?? []).map(({ code, message }) => ({ code, message })),
|
|
351
|
+
limitation(
|
|
352
|
+
'MIGRATION_QUALIFICATION_INSUFFICIENT',
|
|
353
|
+
'The matching qualification has insufficient real-provider evidence.'
|
|
354
|
+
)
|
|
355
|
+
],
|
|
356
|
+
nextAction: 'REQUALIFY_RUNTIME'
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const realQualified = matches && ['QUALIFIED', 'QUALIFIED_WITH_LIMITATIONS'].includes(
|
|
361
|
+
qualification?.verdict
|
|
362
|
+
);
|
|
363
|
+
if (realQualified) {
|
|
364
|
+
return qualificationDecision({
|
|
365
|
+
status: qualification.verdict,
|
|
366
|
+
reasonCode: 'MIGRATION_QUALIFICATION_MATCHED',
|
|
367
|
+
executionAllowed: true,
|
|
368
|
+
experimentalOverrideUsed: false,
|
|
369
|
+
qualificationId: qualification.qualificationId,
|
|
370
|
+
identity: expectedIdentity,
|
|
371
|
+
runtimeIdentity: runtime,
|
|
372
|
+
recordRuntimeIdentity: qualification.identity.runtime,
|
|
373
|
+
sourceKind,
|
|
374
|
+
sourcePath,
|
|
375
|
+
limitations: sortedUniqueLimitations([
|
|
376
|
+
...experimentalBaseLimitations(),
|
|
377
|
+
...(qualification.limitations ?? []).map(({ code, message }) => ({ code, message }))
|
|
378
|
+
]),
|
|
379
|
+
nextAction: 'NONE'
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const limitations = experimentalBaseLimitations();
|
|
384
|
+
if (!qualification) {
|
|
385
|
+
limitations.push(limitation(
|
|
386
|
+
'MIGRATION_PROVIDER_NOT_QUALIFIED',
|
|
387
|
+
'The configured provider/model has not been qualified for migration-planning.v2.'
|
|
388
|
+
));
|
|
389
|
+
if (Object.values(normalizedMigrationRuntimeMetadata(runtimeMetadata)).includes('unknown')) {
|
|
390
|
+
limitations.push(limitation(
|
|
391
|
+
'MIGRATION_RUNTIME_IDENTITY_INCOMPLETE',
|
|
392
|
+
'Provider, model, or runtime adapter metadata is incomplete; this run cannot establish qualification identity.'
|
|
393
|
+
));
|
|
394
|
+
}
|
|
395
|
+
return qualificationDecision({
|
|
396
|
+
status: 'MISSING',
|
|
397
|
+
reasonCode: allowExperimental
|
|
398
|
+
? 'MIGRATION_QUALIFICATION_MISSING_EXPERIMENTAL_OVERRIDE'
|
|
399
|
+
: 'MIGRATION_QUALIFICATION_REQUIRED',
|
|
400
|
+
executionAllowed: allowExperimental,
|
|
401
|
+
experimentalOverrideUsed: allowExperimental,
|
|
402
|
+
qualificationId: null,
|
|
403
|
+
identity: expectedIdentity,
|
|
404
|
+
runtimeIdentity: runtime,
|
|
405
|
+
sourceKind,
|
|
406
|
+
sourcePath,
|
|
407
|
+
limitations,
|
|
408
|
+
nextAction: allowExperimental
|
|
409
|
+
? 'INSTALL_QUALIFICATION_RECORD_OR_REVIEW_EXPERIMENTAL_OUTPUT'
|
|
410
|
+
: 'INSTALL_QUALIFICATION_RECORD'
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (qualification.identity?.runtime?.mode === 'fake') {
|
|
415
|
+
limitations.push(limitation(
|
|
416
|
+
'FAKE_QUALIFICATION_NOT_REAL_PROVIDER',
|
|
417
|
+
'Fake-runtime qualification does not qualify the configured real provider/model.'
|
|
418
|
+
));
|
|
419
|
+
return qualificationDecision({
|
|
420
|
+
status: 'IDENTITY_MISMATCH',
|
|
421
|
+
reasonCode: 'MIGRATION_FAKE_QUALIFICATION_FOR_REAL_RUNTIME',
|
|
422
|
+
executionAllowed: false,
|
|
423
|
+
experimentalOverrideUsed: false,
|
|
424
|
+
qualificationId: qualification.qualificationId ?? null,
|
|
425
|
+
identity: expectedIdentity,
|
|
426
|
+
runtimeIdentity: runtime,
|
|
427
|
+
recordRuntimeIdentity: qualification.identity.runtime,
|
|
428
|
+
sourceKind,
|
|
429
|
+
sourcePath,
|
|
430
|
+
limitations,
|
|
431
|
+
nextAction: 'INSTALL_MATCHING_REAL_QUALIFICATION_RECORD'
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
limitations.push(limitation(
|
|
436
|
+
'MIGRATION_QUALIFICATION_IDENTITY_MISMATCH',
|
|
437
|
+
'The qualification does not match the current task, provider, model, adapter, dataset, prompt, schema, or policy identity.'
|
|
438
|
+
));
|
|
439
|
+
return qualificationDecision({
|
|
440
|
+
status: 'IDENTITY_MISMATCH',
|
|
441
|
+
reasonCode: 'MIGRATION_QUALIFICATION_IDENTITY_MISMATCH',
|
|
442
|
+
executionAllowed: false,
|
|
443
|
+
experimentalOverrideUsed: false,
|
|
444
|
+
qualificationId: qualification.qualificationId ?? null,
|
|
445
|
+
identity: expectedIdentity,
|
|
446
|
+
runtimeIdentity: runtime,
|
|
447
|
+
recordRuntimeIdentity: qualification.identity?.runtime,
|
|
448
|
+
sourceKind,
|
|
449
|
+
sourcePath,
|
|
450
|
+
limitations,
|
|
451
|
+
nextAction: 'INSTALL_MATCHING_QUALIFICATION_RECORD'
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Backward-compatible guard API: allowed decisions are returned; blocked decisions remain errors.
|
|
457
|
+
*/
|
|
458
|
+
export async function evaluateMigrationQualification(options = {}) {
|
|
459
|
+
const decision = await decideMigrationQualification(options);
|
|
460
|
+
if (!decision.executionAllowed) throw migrationQualificationErrorForDecision(decision);
|
|
461
|
+
return decision;
|
|
462
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_MIGRATION_PLANNING_QUALIFICATION_PATH
|
|
3
|
+
} from '../constants.js';
|
|
4
|
+
import { resolveArtifactRootChain } from '../artifact-root-compatibility.js';
|
|
5
|
+
import {
|
|
6
|
+
createMigrationQualificationSourceFailureDecision,
|
|
7
|
+
decideMigrationQualification
|
|
8
|
+
} from './qualification-guard.js';
|
|
9
|
+
import {
|
|
10
|
+
buildMigrationPlanningQualificationRecord,
|
|
11
|
+
loadMigrationPlanningQualificationRecord
|
|
12
|
+
} from './qualification-store.js';
|
|
13
|
+
|
|
14
|
+
function sourceErrorDetails(error, explicitPath) {
|
|
15
|
+
if (error?.code === 'MIGRATION_QUALIFICATION_RECORD_MISSING') {
|
|
16
|
+
return {
|
|
17
|
+
status: 'MISSING',
|
|
18
|
+
reasonCode: explicitPath
|
|
19
|
+
? 'MIGRATION_QUALIFICATION_EXPLICIT_SOURCE_MISSING'
|
|
20
|
+
: 'MIGRATION_QUALIFICATION_RECORD_MISSING',
|
|
21
|
+
limitationCode: explicitPath
|
|
22
|
+
? 'MIGRATION_QUALIFICATION_EXPLICIT_SOURCE_MISSING'
|
|
23
|
+
: 'MIGRATION_PROVIDER_NOT_QUALIFIED',
|
|
24
|
+
limitationMessage: explicitPath
|
|
25
|
+
? 'The explicitly selected Migration Planning qualification record was not found.'
|
|
26
|
+
: 'The default Migration Planning qualification record was not found.',
|
|
27
|
+
nextAction: 'INSTALL_QUALIFICATION_RECORD'
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
status: 'CORRUPTED',
|
|
32
|
+
reasonCode: error?.code ?? 'MIGRATION_QUALIFICATION_RECORD_INVALID',
|
|
33
|
+
limitationCode: error?.code ?? 'MIGRATION_QUALIFICATION_RECORD_INVALID',
|
|
34
|
+
limitationMessage: 'The selected Migration Planning qualification record failed strict validation.',
|
|
35
|
+
nextAction: 'REPLACE_QUALIFICATION_RECORD'
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Deterministic precedence: injected qualification > explicit path > project-local default > missing.
|
|
41
|
+
* Exactly one source is selected and validated; invalid explicit sources never fall back.
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveMigrationQualification({
|
|
44
|
+
repositoryRoot,
|
|
45
|
+
runtimeMetadata,
|
|
46
|
+
allowExperimental = false,
|
|
47
|
+
qualification,
|
|
48
|
+
qualificationPath,
|
|
49
|
+
onCompatibilityDiagnostic,
|
|
50
|
+
loadRecord = loadMigrationPlanningQualificationRecord
|
|
51
|
+
}) {
|
|
52
|
+
if (qualification !== undefined) {
|
|
53
|
+
try {
|
|
54
|
+
const record = buildMigrationPlanningQualificationRecord(qualification);
|
|
55
|
+
return decideMigrationQualification({
|
|
56
|
+
qualification: record.qualification,
|
|
57
|
+
runtimeMetadata,
|
|
58
|
+
allowExperimental,
|
|
59
|
+
sourceKind: 'injected',
|
|
60
|
+
sourcePath: null
|
|
61
|
+
});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const details = sourceErrorDetails(error, true);
|
|
64
|
+
return createMigrationQualificationSourceFailureDecision({
|
|
65
|
+
...details,
|
|
66
|
+
runtimeMetadata,
|
|
67
|
+
sourceKind: 'injected',
|
|
68
|
+
sourcePath: null
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const explicitPath = qualificationPath !== undefined;
|
|
74
|
+
let artifactPath = qualificationPath ?? DEFAULT_MIGRATION_PLANNING_QUALIFICATION_PATH;
|
|
75
|
+
if (!explicitPath) {
|
|
76
|
+
try {
|
|
77
|
+
const selection = await resolveArtifactRootChain(
|
|
78
|
+
repositoryRoot,
|
|
79
|
+
[DEFAULT_MIGRATION_PLANNING_QUALIFICATION_PATH],
|
|
80
|
+
{ onDiagnostic: onCompatibilityDiagnostic }
|
|
81
|
+
);
|
|
82
|
+
[artifactPath] = selection.artifacts;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const details = sourceErrorDetails(error, false);
|
|
85
|
+
return createMigrationQualificationSourceFailureDecision({
|
|
86
|
+
...details,
|
|
87
|
+
runtimeMetadata,
|
|
88
|
+
sourceKind: 'defaultPath',
|
|
89
|
+
sourcePath: DEFAULT_MIGRATION_PLANNING_QUALIFICATION_PATH
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
let record;
|
|
94
|
+
try {
|
|
95
|
+
record = await loadRecord(repositoryRoot, { artifactPath });
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (!explicitPath && error?.code === 'MIGRATION_QUALIFICATION_RECORD_MISSING') {
|
|
98
|
+
return decideMigrationQualification({
|
|
99
|
+
qualification: null,
|
|
100
|
+
runtimeMetadata,
|
|
101
|
+
allowExperimental,
|
|
102
|
+
sourceKind: 'defaultPath',
|
|
103
|
+
sourcePath: artifactPath
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const details = sourceErrorDetails(error, explicitPath);
|
|
107
|
+
return createMigrationQualificationSourceFailureDecision({
|
|
108
|
+
...details,
|
|
109
|
+
runtimeMetadata,
|
|
110
|
+
sourceKind: explicitPath ? 'explicitPath' : 'defaultPath',
|
|
111
|
+
sourcePath: artifactPath
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return decideMigrationQualification({
|
|
116
|
+
qualification: record.qualification,
|
|
117
|
+
runtimeMetadata,
|
|
118
|
+
allowExperimental,
|
|
119
|
+
sourceKind: explicitPath ? 'explicitPath' : 'defaultPath',
|
|
120
|
+
sourcePath: artifactPath
|
|
121
|
+
});
|
|
122
|
+
}
|