@ductape/cli 0.2.23 → 0.3.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.
Files changed (104) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +7 -0
  3. package/dist/commands/db-migrate.js +47 -8
  4. package/dist/commands/migrate-codebase.d.ts +190 -0
  5. package/dist/commands/migrate-codebase.js +776 -0
  6. package/dist/commands/migration-artifact.d.ts +28 -0
  7. package/dist/commands/migration-artifact.js +101 -0
  8. package/dist/commands/migration-assurance.d.ts +10 -0
  9. package/dist/commands/migration-assurance.js +120 -0
  10. package/dist/commands/migration-capabilities.d.ts +53 -0
  11. package/dist/commands/migration-capabilities.js +66 -0
  12. package/dist/commands/migration-database.d.ts +26 -0
  13. package/dist/commands/migration-database.js +162 -0
  14. package/dist/commands/migration-e2e.d.ts +68 -0
  15. package/dist/commands/migration-e2e.js +189 -0
  16. package/dist/commands/migration-environments.d.ts +20 -0
  17. package/dist/commands/migration-environments.js +58 -0
  18. package/dist/commands/migration-generated.d.ts +68 -0
  19. package/dist/commands/migration-generated.js +217 -0
  20. package/dist/commands/migration-portfolio.d.ts +88 -0
  21. package/dist/commands/migration-portfolio.js +223 -0
  22. package/dist/commands/migration-products.d.ts +23 -0
  23. package/dist/commands/migration-products.js +88 -0
  24. package/dist/commands/migration-review.d.ts +248 -0
  25. package/dist/commands/migration-review.js +624 -0
  26. package/dist/commands/migration-secrets.d.ts +24 -0
  27. package/dist/commands/migration-secrets.js +93 -0
  28. package/dist/commands/migration-slice.d.ts +132 -0
  29. package/dist/commands/migration-slice.js +266 -0
  30. package/dist/commands/migration-verification.d.ts +12 -0
  31. package/dist/commands/migration-verification.js +194 -0
  32. package/dist/commands/products.d.ts +1 -0
  33. package/dist/commands/products.js +33 -1
  34. package/dist/commands/secrets.d.ts +8 -0
  35. package/dist/commands/secrets.js +44 -0
  36. package/dist/index.js +376 -4
  37. package/dist/lib/migration-artifact.d.ts +19 -0
  38. package/dist/lib/migration-artifact.js +114 -0
  39. package/package.json +8 -1
  40. package/API_PARITY.md +0 -58
  41. package/DB_MIGRATE_PLAN.md +0 -223
  42. package/ductape.example.ts +0 -18
  43. package/src/commands/apply.ts +0 -153
  44. package/src/commands/apps-import.ts +0 -43
  45. package/src/commands/apps.ts +0 -92
  46. package/src/commands/cloud.ts +0 -68
  47. package/src/commands/completion.ts +0 -119
  48. package/src/commands/db-migrate.ts +0 -209
  49. package/src/commands/db-schema.ts +0 -429
  50. package/src/commands/db.ts +0 -63
  51. package/src/commands/generate.ts +0 -74
  52. package/src/commands/graph.ts +0 -51
  53. package/src/commands/init.ts +0 -39
  54. package/src/commands/install.ts +0 -76
  55. package/src/commands/link.ts +0 -84
  56. package/src/commands/login.ts +0 -145
  57. package/src/commands/logout.ts +0 -7
  58. package/src/commands/platform.ts +0 -123
  59. package/src/commands/products.ts +0 -228
  60. package/src/commands/profiles.ts +0 -23
  61. package/src/commands/resources.ts +0 -213
  62. package/src/commands/secrets.ts +0 -46
  63. package/src/commands/unlink.ts +0 -15
  64. package/src/commands/whoami.ts +0 -27
  65. package/src/commands/workspaces.ts +0 -98
  66. package/src/index.ts +0 -523
  67. package/src/lib/api-client.ts +0 -88
  68. package/src/lib/app-import.ts +0 -82
  69. package/src/lib/apply-loaders.ts +0 -30
  70. package/src/lib/config.ts +0 -230
  71. package/src/lib/context-store.ts +0 -77
  72. package/src/lib/db-methods.ts +0 -56
  73. package/src/lib/db-types.ts +0 -104
  74. package/src/lib/encryption.ts +0 -12
  75. package/src/lib/forms/enums.ts +0 -52
  76. package/src/lib/forms/index.ts +0 -8
  77. package/src/lib/forms/prompt.ts +0 -209
  78. package/src/lib/forms/schemas.ts +0 -288
  79. package/src/lib/forms/types.ts +0 -27
  80. package/src/lib/http.ts +0 -76
  81. package/src/lib/integrations.ts +0 -110
  82. package/src/lib/interactive-opts.ts +0 -13
  83. package/src/lib/migration-files.ts +0 -58
  84. package/src/lib/output.ts +0 -39
  85. package/src/lib/platform-api.ts +0 -209
  86. package/src/lib/proxy/context.ts +0 -94
  87. package/src/lib/proxy/db-proxy.ts +0 -40
  88. package/src/lib/proxy/graph-proxy.ts +0 -64
  89. package/src/lib/proxy/sdk-proxy.ts +0 -58
  90. package/src/lib/read-body.ts +0 -57
  91. package/src/lib/resources.ts +0 -160
  92. package/src/lib/schema-loader.ts +0 -152
  93. package/src/lib/snippets.ts +0 -165
  94. package/src/lib/templates.ts +0 -157
  95. package/src/lib/workspace-context.ts +0 -43
  96. package/src/lib/workspaces.ts +0 -283
  97. package/test/apply-loaders.test.ts +0 -40
  98. package/test/encryption.test.ts +0 -20
  99. package/test/forms.test.ts +0 -25
  100. package/test/http.test.ts +0 -15
  101. package/test/platform-api.test.ts +0 -42
  102. package/test/resources.test.ts +0 -23
  103. package/test/workspace-resolve.test.ts +0 -29
  104. package/tsconfig.json +0 -15
@@ -0,0 +1,223 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { printJson } from '../lib/output.js';
5
+ import { validateMigrationSlice } from './migration-slice.js';
6
+ import { readMigrationJson, writeMigrationJson } from '../lib/migration-artifact.js';
7
+ function read(file) {
8
+ return readMigrationJson(file);
9
+ }
10
+ function digest(file) {
11
+ try {
12
+ return crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex');
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
18
+ export function proposeMigrationPartitions(opts) {
19
+ const ledgerPath = path.resolve(opts.ledger);
20
+ const ledger = read(ledgerPath);
21
+ const maxBytes = opts.maxBytes ?? 128_000;
22
+ const maxTokens = opts.maxTokens ?? 32_000;
23
+ if (!Number.isInteger(maxBytes) || maxBytes <= 0 || !Number.isInteger(maxTokens) || maxTokens <= 0) {
24
+ throw new Error('Partition byte and token limits must be positive integers.');
25
+ }
26
+ const known = new Set(ledger.entries.map((entry) => entry.file));
27
+ const graph = new Map(ledger.entries.map((entry) => [
28
+ entry.file,
29
+ [
30
+ ...(entry.references ?? []).filter((reference) => !reference.external).map((reference) => reference.target),
31
+ ...(entry.follow_up_files ?? []),
32
+ ].filter((target) => known.has(target)),
33
+ ]));
34
+ let nextIndex = 0;
35
+ const stack = [];
36
+ const onStack = new Set();
37
+ const indices = new Map();
38
+ const low = new Map();
39
+ const components = [];
40
+ const visit = (node) => {
41
+ indices.set(node, nextIndex);
42
+ low.set(node, nextIndex++);
43
+ stack.push(node);
44
+ onStack.add(node);
45
+ for (const dependency of graph.get(node) ?? []) {
46
+ if (!indices.has(dependency)) {
47
+ visit(dependency);
48
+ low.set(node, Math.min(low.get(node), low.get(dependency)));
49
+ }
50
+ else if (onStack.has(dependency)) {
51
+ low.set(node, Math.min(low.get(node), indices.get(dependency)));
52
+ }
53
+ }
54
+ if (low.get(node) === indices.get(node)) {
55
+ const component = [];
56
+ let current;
57
+ do {
58
+ current = stack.pop();
59
+ onStack.delete(current);
60
+ component.push(current);
61
+ } while (current !== node);
62
+ components.push(component.sort());
63
+ }
64
+ };
65
+ for (const entry of ledger.entries)
66
+ if (!indices.has(entry.file))
67
+ visit(entry.file);
68
+ const componentFor = new Map();
69
+ components.forEach((files, component) => files.forEach((file) => componentFor.set(file, component)));
70
+ const componentDependencies = components.map(() => new Set());
71
+ for (const [file, dependencies] of graph) {
72
+ const owner = componentFor.get(file);
73
+ for (const dependencyFile of dependencies) {
74
+ const dependency = componentFor.get(dependencyFile);
75
+ if (owner !== dependency)
76
+ componentDependencies[owner].add(dependency);
77
+ }
78
+ }
79
+ const ordered = [];
80
+ const seen = new Set();
81
+ const add = (component) => {
82
+ if (seen.has(component))
83
+ return;
84
+ seen.add(component);
85
+ componentDependencies[component].forEach(add);
86
+ ordered.push(component);
87
+ };
88
+ components.forEach((_, component) => add(component));
89
+ const tags = new Map(ordered.map((component, position) => [component, `partition-${position + 1}`]));
90
+ const partitions = ordered.map((component) => {
91
+ const files = components[component];
92
+ const byteEstimate = files.reduce((sum, file) => {
93
+ try {
94
+ return sum + fs.statSync(path.join(ledger.source, file)).size;
95
+ }
96
+ catch {
97
+ return sum;
98
+ }
99
+ }, 0);
100
+ const tokenEstimate = Math.ceil(byteEstimate / 4);
101
+ return {
102
+ tag: tags.get(component),
103
+ files,
104
+ dependencies: [...componentDependencies[component]].map((dependency) => tags.get(dependency)),
105
+ byte_estimate: byteEstimate,
106
+ token_estimate: tokenEstimate,
107
+ oversized_scc: byteEstimate > maxBytes || tokenEstimate > maxTokens,
108
+ };
109
+ });
110
+ const proposal = {
111
+ version: 1,
112
+ artifact_kind: 'ductape_migration_partition_proposal',
113
+ authority: 'advisory_only',
114
+ ledger: ledgerPath,
115
+ limits: { max_bytes: maxBytes, max_tokens: maxTokens, token_estimate: 'ceil(bytes/4)' },
116
+ partitions,
117
+ };
118
+ const output = path.resolve(opts.output ?? path.join(path.dirname(ledgerPath), 'partition-proposal.json'));
119
+ writeMigrationJson(output, proposal);
120
+ return { path: output, proposal };
121
+ }
122
+ export function initializeMigrationPortfolio(opts) {
123
+ const ledgerPath = path.resolve(opts.ledger);
124
+ const definition = read(path.resolve(opts.definition));
125
+ if (!Array.isArray(definition.partitions) || !Array.isArray(definition.slices) ||
126
+ !Array.isArray(definition.cross_slice_contracts)) {
127
+ throw new Error('Portfolio definition requires partitions, slices, and cross_slice_contracts arrays.');
128
+ }
129
+ const output = path.resolve(opts.output ?? path.join(path.dirname(ledgerPath), 'portfolio.json'));
130
+ if (fs.existsSync(output))
131
+ throw new Error(`Migration portfolio already exists: ${output}`);
132
+ const portfolio = {
133
+ version: 1,
134
+ artifact_kind: 'ductape_migration_portfolio',
135
+ ledger: ledgerPath,
136
+ ...definition,
137
+ partitions: definition.partitions.map((partition) => ({
138
+ ...partition,
139
+ provenance: partition.files.map((file) => ({
140
+ file,
141
+ checksum: digest(path.join(read(ledgerPath).source, file)) ?? '',
142
+ })),
143
+ })),
144
+ slices: definition.slices.map((slice) => path.resolve(slice)),
145
+ };
146
+ writeMigrationJson(output, portfolio, { createOnly: true });
147
+ return { path: output, portfolio };
148
+ }
149
+ export function validateMigrationPortfolio(portfolioPath) {
150
+ const resolved = path.resolve(portfolioPath);
151
+ const portfolio = read(resolved);
152
+ if (portfolio.artifact_kind !== 'ductape_migration_portfolio')
153
+ throw new Error('Invalid migration portfolio.');
154
+ const ledger = read(portfolio.ledger);
155
+ const blockers = [];
156
+ const coverage = new Map();
157
+ for (const partition of portfolio.partitions) {
158
+ if (!partition.tag || partition.context_budget <= 0 || !partition.summary.trim()) {
159
+ blockers.push(`partition ${partition.tag || '<unknown>'}: tag, positive context_budget, and summary are required`);
160
+ }
161
+ const provenance = new Map(partition.provenance.map((item) => [item.file, item.checksum]));
162
+ for (const file of partition.files) {
163
+ coverage.set(file, (coverage.get(file) ?? 0) + 1);
164
+ const recorded = provenance.get(file);
165
+ if (!recorded || digest(path.join(ledger.source, file)) !== recorded) {
166
+ blockers.push(`partition ${partition.tag}: stale or missing provenance for ${file}`);
167
+ }
168
+ }
169
+ }
170
+ for (const entry of ledger.entries) {
171
+ if (!coverage.has(entry.file))
172
+ blockers.push(`portfolio coverage missing: ${entry.file}`);
173
+ }
174
+ for (const [file, count] of coverage) {
175
+ if (!ledger.entries.some((entry) => entry.file === file))
176
+ blockers.push(`partition references unknown file: ${file}`);
177
+ if (count > 1)
178
+ blockers.push(`file appears in multiple partitions: ${file}`);
179
+ }
180
+ const sliceTags = new Set();
181
+ if (portfolio.slices.length === 0)
182
+ blockers.push('portfolio must contain at least one migration slice');
183
+ for (const slicePath of portfolio.slices) {
184
+ const result = validateMigrationSlice(slicePath);
185
+ sliceTags.add(result.tag);
186
+ if (!result.ready)
187
+ blockers.push(`slice ${result.tag}: ${result.blockers.join('; ')}`);
188
+ }
189
+ for (const contract of portfolio.cross_slice_contracts) {
190
+ if (!sliceTags.has(contract.provider) || !sliceTags.has(contract.consumer) ||
191
+ !contract.contract || !contract.evidence) {
192
+ blockers.push(`cross-slice contract ${contract.provider} -> ${contract.consumer} is incomplete`);
193
+ }
194
+ }
195
+ return {
196
+ portfolio: resolved,
197
+ ready: blockers.length === 0,
198
+ partitions: portfolio.partitions.length,
199
+ slices: portfolio.slices.length,
200
+ coverage: { reviewed_files: ledger.entries.length, partitioned_files: coverage.size },
201
+ blockers,
202
+ };
203
+ }
204
+ export function runMigrationPortfolioInit(opts) {
205
+ const result = initializeMigrationPortfolio(opts);
206
+ printJson({ path: result.path, partitions: result.portfolio.partitions.length }, Boolean(opts.json));
207
+ }
208
+ export function runMigrationPortfolioValidate(opts) {
209
+ const result = validateMigrationPortfolio(opts.portfolio);
210
+ printJson(result, Boolean(opts.json));
211
+ if (opts.strict && !result.ready) {
212
+ throw new Error('Migration portfolio is not ready: resolve every partition, slice, and contract blocker.');
213
+ }
214
+ }
215
+ export function runMigrationPortfolioPropose(opts) {
216
+ const result = proposeMigrationPartitions(opts);
217
+ printJson({
218
+ path: result.path,
219
+ authority: result.proposal.authority,
220
+ partitions: result.proposal.partitions.length,
221
+ oversized_sccs: result.proposal.partitions.filter((partition) => partition.oversized_scc).length,
222
+ }, Boolean(opts.json));
223
+ }
@@ -0,0 +1,23 @@
1
+ export declare function initializeProductMap(opts: {
2
+ analysis: string;
3
+ definition: string;
4
+ output: string;
5
+ }): {
6
+ path: string;
7
+ };
8
+ export declare function validateProductMap(file: string): {
9
+ artifact: string;
10
+ ready: boolean;
11
+ blockers: string[];
12
+ };
13
+ export declare function runMigrationProductsInit(opts: {
14
+ analysis: string;
15
+ definition: string;
16
+ output: string;
17
+ json?: boolean;
18
+ }): void;
19
+ export declare function runMigrationProductsValidate(opts: {
20
+ file: string;
21
+ strict?: boolean;
22
+ json?: boolean;
23
+ }): void;
@@ -0,0 +1,88 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { printJson } from '../lib/output.js';
4
+ import { readMigrationJson, writeMigrationJson } from '../lib/migration-artifact.js';
5
+ function read(file) {
6
+ return readMigrationJson(file);
7
+ }
8
+ export function initializeProductMap(opts) {
9
+ const analysisPath = path.resolve(opts.analysis);
10
+ const definition = read(opts.definition);
11
+ if (!Array.isArray(definition.mappings) || !Array.isArray(definition.assets) ||
12
+ !Array.isArray(definition.promotions)) {
13
+ throw new Error('Definition requires mappings, assets, and promotions arrays.');
14
+ }
15
+ const artifact = {
16
+ version: 1,
17
+ artifact_kind: 'ductape_service_product_map',
18
+ analysis: analysisPath,
19
+ ...definition,
20
+ };
21
+ const output = path.resolve(opts.output);
22
+ if (fs.existsSync(output))
23
+ throw new Error(`Service-to-product map already exists: ${output}`);
24
+ fs.mkdirSync(path.dirname(output), { recursive: true });
25
+ writeMigrationJson(output, artifact, { createOnly: true });
26
+ return { path: output };
27
+ }
28
+ export function validateProductMap(file) {
29
+ const resolved = path.resolve(file);
30
+ const artifact = read(resolved);
31
+ if (artifact.artifact_kind !== 'ductape_service_product_map')
32
+ throw new Error('Invalid service-to-product map.');
33
+ const analysis = read(artifact.analysis);
34
+ const blockers = [];
35
+ const assignment = new Map();
36
+ for (const mapping of artifact.mappings) {
37
+ if (!mapping.service || !mapping.product_tag || !mapping.environments.length || !mapping.evidence) {
38
+ blockers.push(`mapping ${mapping.service || '<unknown>'}: service, product, environments, and evidence are required`);
39
+ }
40
+ for (const manifest of mapping.manifests) {
41
+ assignment.set(manifest, [...(assignment.get(manifest) ?? []), mapping.service]);
42
+ }
43
+ }
44
+ for (const unit of analysis.workspace_units ?? []) {
45
+ const owners = assignment.get(unit.manifest) ?? [];
46
+ if (owners.length === 0)
47
+ blockers.push(`workspace unit is unmapped: ${unit.manifest}`);
48
+ if (owners.length > 1)
49
+ blockers.push(`workspace unit has multiple service owners: ${unit.manifest}`);
50
+ }
51
+ for (const manifest of assignment.keys()) {
52
+ if (!(analysis.workspace_units ?? []).some((unit) => unit.manifest === manifest)) {
53
+ blockers.push(`mapping references unknown workspace unit: ${manifest}`);
54
+ }
55
+ }
56
+ for (const asset of artifact.assets) {
57
+ if (!asset.kind || !asset.tag || !asset.product_tag || !asset.inventory_evidence) {
58
+ blockers.push(`asset ${asset.tag || '<unknown>'}: identity and inventory evidence are required`);
59
+ }
60
+ for (const environment of asset.required_environments) {
61
+ if (!asset.configured_environments.includes(environment)) {
62
+ blockers.push(`asset ${asset.kind}:${asset.tag} missing environment configuration: ${environment}`);
63
+ }
64
+ }
65
+ }
66
+ for (const promotion of artifact.promotions) {
67
+ if (!promotion.product_tag || !promotion.from || !promotion.to ||
68
+ !promotion.entry_conditions.length || !promotion.validation.length || !promotion.rollback.length) {
69
+ blockers.push(`promotion ${promotion.from || '?'} -> ${promotion.to || '?'} is incomplete`);
70
+ }
71
+ }
72
+ const mappedProducts = new Set(artifact.mappings.map((mapping) => mapping.product_tag));
73
+ for (const product of mappedProducts) {
74
+ if (!artifact.promotions.some((promotion) => promotion.product_tag === product)) {
75
+ blockers.push(`product has no promotion plan: ${product}`);
76
+ }
77
+ }
78
+ return { artifact: resolved, ready: blockers.length === 0, blockers };
79
+ }
80
+ export function runMigrationProductsInit(opts) {
81
+ printJson(initializeProductMap(opts), Boolean(opts.json));
82
+ }
83
+ export function runMigrationProductsValidate(opts) {
84
+ const result = validateProductMap(opts.file);
85
+ printJson(result, Boolean(opts.json));
86
+ if (opts.strict && !result.ready)
87
+ throw new Error('Service-to-product mapping is not ready.');
88
+ }
@@ -0,0 +1,248 @@
1
+ type ReviewStatus = 'pending' | 'reviewed' | 'excluded';
2
+ type ParityStatus = 'pending' | 'verified' | 'not_applicable';
3
+ type ReferenceKind = 'import' | 'caller' | 'callee' | 'implements' | 'event' | 'test' | 'config' | 'schema';
4
+ interface ParityEvidence {
5
+ status: ParityStatus;
6
+ evidence: string[];
7
+ reason?: string;
8
+ }
9
+ interface ReviewEntry {
10
+ file: string;
11
+ status: ReviewStatus;
12
+ safety: 'names_only' | 'normal';
13
+ baseline_checksum: string | null;
14
+ reviewed_checksum: string | null;
15
+ purpose: string;
16
+ dependencies: string[];
17
+ follow_up_files: string[];
18
+ references: Array<{
19
+ kind: ReferenceKind;
20
+ target: string;
21
+ external: boolean;
22
+ evidence: string;
23
+ }>;
24
+ interfaces: string[];
25
+ findings: Array<{
26
+ kind: string;
27
+ evidence: string;
28
+ location: {
29
+ start_line: number;
30
+ end_line: number;
31
+ };
32
+ symbol: {
33
+ kind: 'module' | 'class' | 'interface' | 'function' | 'method' | 'field' | 'route' | 'event' | 'schema' | 'other';
34
+ name: string;
35
+ qualified_name?: string;
36
+ };
37
+ classification: 'observed' | 'inferred' | 'proposed' | 'confirmed';
38
+ confidence: 'low' | 'medium' | 'high';
39
+ requires_followup: string[];
40
+ }>;
41
+ exclusion_reason?: string;
42
+ exclusion?: {
43
+ classification: 'generated' | 'vendor' | 'build' | 'snapshot' | 'other';
44
+ reason: string;
45
+ owner?: string;
46
+ generator?: string;
47
+ regeneration_test?: string;
48
+ };
49
+ interface_parity: ParityEvidence;
50
+ functional_parity: ParityEvidence;
51
+ operational_parity: ParityEvidence;
52
+ }
53
+ interface ReviewLedger {
54
+ version: 1;
55
+ artifact_kind: 'ai_migration_review_ledger';
56
+ source: string;
57
+ created_at: string;
58
+ revision: number;
59
+ events: Array<{
60
+ revision: number;
61
+ timestamp: string;
62
+ actor: string;
63
+ tool: '@ductape/cli';
64
+ tool_version: string;
65
+ operation: string;
66
+ state_hash: string;
67
+ previous_hash: string | null;
68
+ hash: string;
69
+ }>;
70
+ entries: ReviewEntry[];
71
+ }
72
+ interface LedgerSignature {
73
+ version: 1;
74
+ artifact_kind: 'ductape_review_ledger_signature';
75
+ ledger: string;
76
+ ledger_checksum: string;
77
+ key_id: string;
78
+ algorithm: 'Ed25519';
79
+ signed_at: string;
80
+ signature: string;
81
+ }
82
+ export declare function initializeReviewLedger(analysisPath: string, ledgerPath?: string): {
83
+ ledger: ReviewLedger;
84
+ path: string;
85
+ };
86
+ export declare function validateReviewLedger(ledgerPath: string): {
87
+ ledger: string;
88
+ source: string;
89
+ ready: boolean;
90
+ coverage: {
91
+ total: number;
92
+ reviewed: number;
93
+ excluded: number;
94
+ pending: number;
95
+ percent: number;
96
+ };
97
+ blockers: {
98
+ pending: string[];
99
+ stale: string[];
100
+ invalid_exclusions: string[];
101
+ invalid_generated_exclusions: string[];
102
+ missing_follow_ups: {
103
+ file: string;
104
+ follow_up: string;
105
+ }[];
106
+ uncovered_references: {
107
+ file: string;
108
+ kind: ReferenceKind;
109
+ target: string;
110
+ }[];
111
+ missing_finding_provenance: {
112
+ file: string;
113
+ finding: number;
114
+ }[];
115
+ missing_symbol_provenance: {
116
+ file: string;
117
+ finding: number;
118
+ }[];
119
+ probable_moves: {
120
+ from: string;
121
+ candidates: string[];
122
+ }[];
123
+ invalidated_dependents: string[];
124
+ audit_errors: string[];
125
+ missing_context: string[];
126
+ incomplete_parity: {
127
+ file: string;
128
+ concern: string;
129
+ }[];
130
+ };
131
+ };
132
+ export declare function snapshotReviewedFile(ledgerPath: string, file: string): ReviewEntry;
133
+ export declare function recordReviewContext(ledgerPath: string, file: string, dataPath: string): ReviewEntry;
134
+ export declare function addFollowUpFile(ledgerPath: string, fromFile: string, followUpFile: string): ReviewEntry;
135
+ export declare function excludeReviewFile(ledgerPath: string, file: string, exclusion: NonNullable<ReviewEntry['exclusion']>): ReviewEntry;
136
+ export declare function setParityEvidence(ledgerPath: string, file: string, concern: 'interface' | 'functional' | 'operational', status: Exclude<ParityStatus, 'pending'>, evidence?: string, reason?: string): ReviewEntry;
137
+ export declare function reopenReviewFile(ledgerPath: string, file: string): ReviewEntry;
138
+ export declare function moveReviewedFile(ledgerPath: string, from: string, to: string): ReviewEntry;
139
+ export declare function signReviewLedger(opts: {
140
+ ledger: string;
141
+ privateKey: string;
142
+ keyId: string;
143
+ output?: string;
144
+ }): {
145
+ path: string;
146
+ signature: LedgerSignature;
147
+ };
148
+ export declare function verifyReviewLedgerSignature(opts: {
149
+ ledger: string;
150
+ signature: string;
151
+ publicKey: string;
152
+ }): {
153
+ ledger: string;
154
+ key_id: string;
155
+ valid: boolean;
156
+ };
157
+ export declare function mergeReviewLedgers(opts: {
158
+ base: string;
159
+ current: string;
160
+ incoming: string;
161
+ output: string;
162
+ }): {
163
+ merged: boolean;
164
+ conflicts: string[];
165
+ output: null;
166
+ } | {
167
+ merged: boolean;
168
+ conflicts: never[];
169
+ output: string;
170
+ };
171
+ export declare function runMigrationReviewInit(opts: {
172
+ analysis: string;
173
+ output?: string;
174
+ json?: boolean;
175
+ }): void;
176
+ export declare function runMigrationReviewValidate(opts: {
177
+ ledger: string;
178
+ strict?: boolean;
179
+ json?: boolean;
180
+ }): void;
181
+ export declare function runMigrationReviewSnapshot(opts: {
182
+ ledger: string;
183
+ file: string;
184
+ json?: boolean;
185
+ }): void;
186
+ export declare function runMigrationReviewRecord(opts: {
187
+ ledger: string;
188
+ file: string;
189
+ data: string;
190
+ json?: boolean;
191
+ }): void;
192
+ export declare function runMigrationReviewFollowUp(opts: {
193
+ ledger: string;
194
+ file: string;
195
+ followUp: string;
196
+ json?: boolean;
197
+ }): void;
198
+ export declare function runMigrationReviewExclude(opts: {
199
+ ledger: string;
200
+ file: string;
201
+ classification: NonNullable<ReviewEntry['exclusion']>['classification'];
202
+ reason: string;
203
+ owner?: string;
204
+ generator?: string;
205
+ regenerationTest?: string;
206
+ json?: boolean;
207
+ }): void;
208
+ export declare function runMigrationReviewParity(opts: {
209
+ ledger: string;
210
+ file: string;
211
+ concern: 'interface' | 'functional' | 'operational';
212
+ status: 'verified' | 'not_applicable';
213
+ evidence?: string;
214
+ reason?: string;
215
+ json?: boolean;
216
+ }): void;
217
+ export declare function runMigrationReviewReopen(opts: {
218
+ ledger: string;
219
+ file: string;
220
+ json?: boolean;
221
+ }): void;
222
+ export declare function runMigrationReviewMove(opts: {
223
+ ledger: string;
224
+ from: string;
225
+ to: string;
226
+ json?: boolean;
227
+ }): void;
228
+ export declare function runMigrationReviewSign(opts: {
229
+ ledger: string;
230
+ privateKey: string;
231
+ keyId: string;
232
+ output?: string;
233
+ json?: boolean;
234
+ }): void;
235
+ export declare function runMigrationReviewVerifySignature(opts: {
236
+ ledger: string;
237
+ signature: string;
238
+ publicKey: string;
239
+ json?: boolean;
240
+ }): void;
241
+ export declare function runMigrationReviewMerge(opts: {
242
+ base: string;
243
+ current: string;
244
+ incoming: string;
245
+ output: string;
246
+ json?: boolean;
247
+ }): void;
248
+ export {};