@tiangong-lca/cli 0.0.24 → 0.0.26

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 (28) hide show
  1. package/README.md +66 -5
  2. package/dist/src/cli.js +484 -5
  3. package/dist/src/cli.js.map +1 -1
  4. package/dist/src/lib/dataset-maintenance-alias-request.js +99 -0
  5. package/dist/src/lib/dataset-maintenance-alias-request.js.map +1 -0
  6. package/dist/src/lib/dataset-maintenance-apply.js +12 -367
  7. package/dist/src/lib/dataset-maintenance-apply.js.map +1 -1
  8. package/dist/src/lib/dataset-maintenance-protected-artifacts.js +100 -0
  9. package/dist/src/lib/dataset-maintenance-protected-artifacts.js.map +1 -0
  10. package/dist/src/lib/dataset-maintenance-protected-before.js +284 -0
  11. package/dist/src/lib/dataset-maintenance-protected-before.js.map +1 -0
  12. package/dist/src/lib/dataset-maintenance-protected-contract.js +918 -0
  13. package/dist/src/lib/dataset-maintenance-protected-contract.js.map +1 -0
  14. package/dist/src/lib/dataset-maintenance-protected-freeze.js +230 -0
  15. package/dist/src/lib/dataset-maintenance-protected-freeze.js.map +1 -0
  16. package/dist/src/lib/dataset-maintenance-protected-preparation.js +524 -0
  17. package/dist/src/lib/dataset-maintenance-protected-preparation.js.map +1 -0
  18. package/dist/src/lib/dataset-maintenance-protected-run.js +667 -0
  19. package/dist/src/lib/dataset-maintenance-protected-run.js.map +1 -0
  20. package/dist/src/lib/dataset-maintenance-protected-seal.js +160 -0
  21. package/dist/src/lib/dataset-maintenance-protected-seal.js.map +1 -0
  22. package/dist/src/lib/dataset-maintenance-protected-toolchain.js +86 -0
  23. package/dist/src/lib/dataset-maintenance-protected-toolchain.js.map +1 -0
  24. package/dist/src/lib/dataset-maintenance-protected-verify.js +435 -0
  25. package/dist/src/lib/dataset-maintenance-protected-verify.js.map +1 -0
  26. package/dist/src/lib/dataset-maintenance-remote.js +116 -2
  27. package/dist/src/lib/dataset-maintenance-remote.js.map +1 -1
  28. package/package.json +1 -1
@@ -0,0 +1,99 @@
1
+ import { CliError } from './errors.js';
2
+ import { isJsonObject, readJsonFile, resolveMaintenancePlanArtifactPath, sha256Json, } from './dataset-maintenance-contract.js';
3
+ export function loadMaintenanceDesiredPayload(planDir, action) {
4
+ if (!action.desired_payload) {
5
+ throw new CliError(`Maintenance action lacks desired payload: ${action.action_id}`, {
6
+ code: 'DATASET_MAINTENANCE_PLAN_INVALID',
7
+ exitCode: 2,
8
+ });
9
+ }
10
+ const payloadPath = resolveMaintenancePlanArtifactPath(planDir, action.desired_payload.path, 'Maintenance desired payload path');
11
+ const payload = readJsonFile(payloadPath, 'Maintenance desired payload');
12
+ if (!isJsonObject(payload) || sha256Json(payload) !== action.desired_payload.sha256) {
13
+ throw new CliError(`Desired payload hash mismatch for action ${action.action_id}.`, {
14
+ code: 'DATASET_MAINTENANCE_DESIRED_PAYLOAD_HASH_MISMATCH',
15
+ exitCode: 1,
16
+ });
17
+ }
18
+ return payload;
19
+ }
20
+ export function orderedAliasBatches(plan) {
21
+ const time = plan.alias_batches?.find((batch) => batch.dimension === 'time');
22
+ const lengthTime = plan.alias_batches?.find((batch) => batch.dimension === 'length_time');
23
+ return [time, lengthTime].filter((batch) => batch !== undefined);
24
+ }
25
+ export function buildAliasBatchRequest(options) {
26
+ if (options.plan.target_mode !== 'owner_draft') {
27
+ throw new CliError('Alias batch request requires target_mode=owner_draft.', {
28
+ code: 'DATASET_MAINTENANCE_TARGET_MODE_INVALID',
29
+ exitCode: 2,
30
+ });
31
+ }
32
+ const targetSnapshot = (snapshot) => ({
33
+ id: snapshot.id,
34
+ version: snapshot.version,
35
+ expected_modified_at: snapshot.modified_at,
36
+ expected_json_ordered: snapshot.json_ordered,
37
+ });
38
+ const actions = options.batch.action_ids.map((actionId) => {
39
+ const action = options.plan.actions.find((entry) => entry.action_id === actionId);
40
+ if (!action?.before || !action.alias_mutation) {
41
+ throw new CliError(`Alias action is incomplete: ${actionId}`, {
42
+ code: 'DATASET_MAINTENANCE_ALIAS_PLAN_INVALID',
43
+ exitCode: 2,
44
+ });
45
+ }
46
+ return {
47
+ action_id: action.action_id,
48
+ action: 'update_json_ordered',
49
+ table: action.table,
50
+ id: action.id,
51
+ version: action.version,
52
+ expected_state_code: 0,
53
+ expected_modified_at: action.before.modified_at,
54
+ expected_json_ordered: action.before.json_ordered,
55
+ desired_json_ordered: loadMaintenanceDesiredPayload(options.planDir, action),
56
+ mutation: action.alias_mutation,
57
+ };
58
+ });
59
+ return {
60
+ schema_version: 'dataset-alias-batch.v1',
61
+ target_visibility: 'owner_draft',
62
+ plan_sha256: options.plan.plan_sha256,
63
+ operation_id: options.plan.operation_id,
64
+ batch_id: options.batch.batch_id,
65
+ dimension: options.batch.dimension,
66
+ factor: options.batch.factor,
67
+ target: {
68
+ flowproperty: targetSnapshot(options.batch.target_snapshots.flowproperty),
69
+ unitgroup: targetSnapshot(options.batch.target_snapshots.unitgroup),
70
+ source_unitgroup: targetSnapshot(options.batch.target_snapshots.source_unitgroup),
71
+ },
72
+ actions,
73
+ };
74
+ }
75
+ export function buildAliasPlanRequest(options) {
76
+ if (options.plan.target_mode !== 'owner_draft') {
77
+ throw new CliError('Alias plan request requires target_mode=owner_draft.', {
78
+ code: 'DATASET_MAINTENANCE_TARGET_MODE_INVALID',
79
+ exitCode: 2,
80
+ });
81
+ }
82
+ const batches = orderedAliasBatches(options.plan);
83
+ if (batches.length !== 2 ||
84
+ batches[0]?.dimension !== 'time' ||
85
+ batches[1]?.dimension !== 'length_time') {
86
+ throw new CliError('Alias plan request requires time followed by length_time exactly once.', {
87
+ code: 'DATASET_MAINTENANCE_ALIAS_PLAN_INVALID',
88
+ exitCode: 2,
89
+ });
90
+ }
91
+ return {
92
+ schema_version: 'dataset-alias-plan.v1',
93
+ plan_sha256: options.plan.plan_sha256,
94
+ operation_id: options.plan.operation_id,
95
+ target_visibility: 'owner_draft',
96
+ batches: batches.map((batch) => buildAliasBatchRequest({ plan: options.plan, batch, planDir: options.planDir })),
97
+ };
98
+ }
99
+ //# sourceMappingURL=dataset-maintenance-alias-request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-maintenance-alias-request.js","sourceRoot":"","sources":["../../../src/lib/dataset-maintenance-alias-request.ts"],"names":[],"mappings":"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","sourcesContent":["import { CliError } from './errors.js';\nimport {\n isJsonObject,\n readJsonFile,\n resolveMaintenancePlanArtifactPath,\n sha256Json,\n type DatasetMaintenanceAliasBatchPlan,\n type DatasetMaintenancePlan,\n type DatasetMaintenancePlanAction,\n type JsonObject,\n} from './dataset-maintenance-contract.js';\n\nexport function loadMaintenanceDesiredPayload(\n planDir: string,\n action: DatasetMaintenancePlanAction,\n): JsonObject {\n if (!action.desired_payload) {\n throw new CliError(`Maintenance action lacks desired payload: ${action.action_id}`, {\n code: 'DATASET_MAINTENANCE_PLAN_INVALID',\n exitCode: 2,\n });\n }\n const payloadPath = resolveMaintenancePlanArtifactPath(\n planDir,\n action.desired_payload.path,\n 'Maintenance desired payload path',\n );\n const payload = readJsonFile(payloadPath, 'Maintenance desired payload');\n if (!isJsonObject(payload) || sha256Json(payload) !== action.desired_payload.sha256) {\n throw new CliError(`Desired payload hash mismatch for action ${action.action_id}.`, {\n code: 'DATASET_MAINTENANCE_DESIRED_PAYLOAD_HASH_MISMATCH',\n exitCode: 1,\n });\n }\n return payload;\n}\n\nexport function orderedAliasBatches(\n plan: DatasetMaintenancePlan,\n): DatasetMaintenanceAliasBatchPlan[] {\n const time = plan.alias_batches?.find((batch) => batch.dimension === 'time');\n const lengthTime = plan.alias_batches?.find((batch) => batch.dimension === 'length_time');\n return [time, lengthTime].filter(\n (batch): batch is DatasetMaintenanceAliasBatchPlan => batch !== undefined,\n );\n}\n\nexport function buildAliasBatchRequest(options: {\n plan: DatasetMaintenancePlan;\n batch: DatasetMaintenanceAliasBatchPlan;\n planDir: string;\n}): JsonObject {\n if (options.plan.target_mode !== 'owner_draft') {\n throw new CliError('Alias batch request requires target_mode=owner_draft.', {\n code: 'DATASET_MAINTENANCE_TARGET_MODE_INVALID',\n exitCode: 2,\n });\n }\n const targetSnapshot = (\n snapshot: NonNullable<DatasetMaintenanceAliasBatchPlan['target_snapshots']['unitgroup']>,\n ): JsonObject => ({\n id: snapshot.id,\n version: snapshot.version,\n expected_modified_at: snapshot.modified_at!,\n expected_json_ordered: snapshot.json_ordered!,\n });\n const actions = options.batch.action_ids.map((actionId) => {\n const action = options.plan.actions.find((entry) => entry.action_id === actionId);\n if (!action?.before || !action.alias_mutation) {\n throw new CliError(`Alias action is incomplete: ${actionId}`, {\n code: 'DATASET_MAINTENANCE_ALIAS_PLAN_INVALID',\n exitCode: 2,\n });\n }\n return {\n action_id: action.action_id,\n action: 'update_json_ordered',\n table: action.table,\n id: action.id,\n version: action.version,\n expected_state_code: 0,\n expected_modified_at: action.before.modified_at!,\n expected_json_ordered: action.before.json_ordered!,\n desired_json_ordered: loadMaintenanceDesiredPayload(options.planDir, action),\n mutation: action.alias_mutation,\n };\n });\n return {\n schema_version: 'dataset-alias-batch.v1',\n target_visibility: 'owner_draft',\n plan_sha256: options.plan.plan_sha256,\n operation_id: options.plan.operation_id,\n batch_id: options.batch.batch_id,\n dimension: options.batch.dimension,\n factor: options.batch.factor,\n target: {\n flowproperty: targetSnapshot(options.batch.target_snapshots.flowproperty!),\n unitgroup: targetSnapshot(options.batch.target_snapshots.unitgroup!),\n source_unitgroup: targetSnapshot(options.batch.target_snapshots.source_unitgroup!),\n },\n actions,\n };\n}\n\nexport function buildAliasPlanRequest(options: {\n plan: DatasetMaintenancePlan;\n planDir: string;\n}): JsonObject {\n if (options.plan.target_mode !== 'owner_draft') {\n throw new CliError('Alias plan request requires target_mode=owner_draft.', {\n code: 'DATASET_MAINTENANCE_TARGET_MODE_INVALID',\n exitCode: 2,\n });\n }\n const batches = orderedAliasBatches(options.plan);\n if (\n batches.length !== 2 ||\n batches[0]?.dimension !== 'time' ||\n batches[1]?.dimension !== 'length_time'\n ) {\n throw new CliError('Alias plan request requires time followed by length_time exactly once.', {\n code: 'DATASET_MAINTENANCE_ALIAS_PLAN_INVALID',\n exitCode: 2,\n });\n }\n return {\n schema_version: 'dataset-alias-plan.v1',\n plan_sha256: options.plan.plan_sha256,\n operation_id: options.plan.operation_id,\n target_visibility: 'owner_draft',\n batches: batches.map((batch) =>\n buildAliasBatchRequest({ plan: options.plan, batch, planDir: options.planDir }),\n ),\n };\n}\n"]}
@@ -3,7 +3,8 @@ import path from 'node:path';
3
3
  import { writeJsonArtifact } from './artifacts.js';
4
4
  import { CliError } from './errors.js';
5
5
  import { withStateFileLock } from './state-lock.js';
6
- import { MAINTENANCE_SCAN_TABLES, appendStableJsonLine, isJsonObject, maintenanceRowKey, parseMaintenancePlan, readJsonFile, readJsonLinesIfPresent, resolveMaintenancePlanArtifactPath, sha256Json, snapshotRemoteRow, writeImmutableJson, } from './dataset-maintenance-contract.js';
6
+ import { buildAliasBatchRequest, buildAliasPlanRequest, loadMaintenanceDesiredPayload, orderedAliasBatches, } from './dataset-maintenance-alias-request.js';
7
+ import { MAINTENANCE_SCAN_TABLES, appendStableJsonLine, isJsonObject, maintenanceRowKey, parseMaintenancePlan, readJsonFile, readJsonLinesIfPresent, sha256Json, snapshotRemoteRow, writeImmutableJson, } from './dataset-maintenance-contract.js';
7
8
  import { buildDerivativePlanRequest, derivativePlanAction, parseDerivativeSnapshotResponse, parseDerivativeSubmitResponse, } from './dataset-maintenance-derivatives.js';
8
9
  import { maintenanceProjectedReferenceFingerprint } from './dataset-maintenance-plan.js';
9
10
  import { isSnapshotCompletenessCompatible } from './dataset-maintenance-pagination.js';
@@ -16,21 +17,7 @@ function errorMessage(error) {
16
17
  return error instanceof Error ? error.message : String(error);
17
18
  }
18
19
  function loadDesiredPayload(planDir, action) {
19
- if (!action.desired_payload) {
20
- throw new CliError(`save_draft action lacks desired payload: ${action.action_id}`, {
21
- code: 'DATASET_MAINTENANCE_PLAN_INVALID',
22
- exitCode: 2,
23
- });
24
- }
25
- const payloadPath = resolveMaintenancePlanArtifactPath(planDir, action.desired_payload.path, 'Maintenance desired payload path');
26
- const payload = readJsonFile(payloadPath, 'Maintenance desired payload');
27
- if (!isJsonObject(payload) || sha256Json(payload) !== action.desired_payload.sha256) {
28
- throw new CliError(`Desired payload hash mismatch for action ${action.action_id}.`, {
29
- code: 'DATASET_MAINTENANCE_DESIRED_PAYLOAD_HASH_MISMATCH',
30
- exitCode: 1,
31
- });
32
- }
33
- return payload;
20
+ return loadMaintenanceDesiredPayload(planDir, action);
34
21
  }
35
22
  function parseProgress(plan, progressPath) {
36
23
  const rawEntries = readJsonLinesIfPresent(progressPath);
@@ -526,81 +513,6 @@ async function assertAliasSupportSnapshots(options) {
526
513
  }
527
514
  }
528
515
  }
529
- function buildAliasBatchRequest(options) {
530
- if (options.plan.target_mode !== 'owner_draft') {
531
- throw new CliError('Alias batch request requires target_mode=owner_draft.', {
532
- code: 'DATASET_MAINTENANCE_TARGET_MODE_INVALID',
533
- exitCode: 2,
534
- });
535
- }
536
- const targetSnapshot = (snapshot) => {
537
- return {
538
- id: snapshot.id,
539
- version: snapshot.version,
540
- expected_modified_at: snapshot.modified_at,
541
- expected_json_ordered: snapshot.json_ordered,
542
- };
543
- };
544
- const actions = options.batch.action_ids.map((actionId) => {
545
- const action = options.plan.actions.find((entry) => entry.action_id === actionId);
546
- return {
547
- action_id: action.action_id,
548
- action: 'update_json_ordered',
549
- table: action.table,
550
- id: action.id,
551
- version: action.version,
552
- expected_state_code: 0,
553
- expected_modified_at: action.before.modified_at,
554
- expected_json_ordered: action.before.json_ordered,
555
- desired_json_ordered: loadDesiredPayload(options.planDir, action),
556
- mutation: action.alias_mutation,
557
- };
558
- });
559
- return {
560
- schema_version: 'dataset-alias-batch.v1',
561
- target_visibility: 'owner_draft',
562
- plan_sha256: options.plan.plan_sha256,
563
- operation_id: options.plan.operation_id,
564
- batch_id: options.batch.batch_id,
565
- dimension: options.batch.dimension,
566
- factor: options.batch.factor,
567
- target: {
568
- flowproperty: targetSnapshot(options.batch.target_snapshots.flowproperty),
569
- unitgroup: targetSnapshot(options.batch.target_snapshots.unitgroup),
570
- source_unitgroup: targetSnapshot(options.batch.target_snapshots.source_unitgroup),
571
- },
572
- actions,
573
- };
574
- }
575
- function orderedAliasBatches(plan) {
576
- const time = plan.alias_batches.find((batch) => batch.dimension === 'time');
577
- const lengthTime = plan.alias_batches.find((batch) => batch.dimension === 'length_time');
578
- return [time, lengthTime].filter((batch) => batch !== undefined);
579
- }
580
- function buildAliasPlanRequest(options) {
581
- if (options.plan.target_mode !== 'owner_draft') {
582
- throw new CliError('Alias plan request requires target_mode=owner_draft.', {
583
- code: 'DATASET_MAINTENANCE_TARGET_MODE_INVALID',
584
- exitCode: 2,
585
- });
586
- }
587
- const batches = orderedAliasBatches(options.plan);
588
- if (batches.length !== 2 ||
589
- batches[0]?.dimension !== 'time' ||
590
- batches[1]?.dimension !== 'length_time') {
591
- throw new CliError('Alias plan request requires time followed by length_time exactly once.', {
592
- code: 'DATASET_MAINTENANCE_ALIAS_PLAN_INVALID',
593
- exitCode: 2,
594
- });
595
- }
596
- return {
597
- schema_version: 'dataset-alias-plan.v1',
598
- plan_sha256: options.plan.plan_sha256,
599
- operation_id: options.plan.operation_id,
600
- target_visibility: 'owner_draft',
601
- batches: batches.map((batch) => buildAliasBatchRequest({ plan: options.plan, batch, planDir: options.planDir })),
602
- };
603
- }
604
516
  function validateAliasRpcResult(value, batch, plan) {
605
517
  const audit = Array.isArray(value.audit) ? value.audit : [];
606
518
  const proofs = audit.filter(isJsonObject).map((entry) => ({
@@ -751,106 +663,6 @@ async function executeAliasPlan(options) {
751
663
  function aliasExchangeProgressKey(value) {
752
664
  return `${value.batch_id}\u0000${value.action_id}\u0000${value.exchange_index}\u0000${value.data_set_internal_id}`;
753
665
  }
754
- function aliasBatchDerivedLogsComplete(options) {
755
- const planBatchProof = options.planSuccess.batches.find((proof) => proof.batch_id === options.batch.batch_id);
756
- if (!planBatchProof ||
757
- planBatchProof.dimension !== options.batch.dimension ||
758
- planBatchProof.batch_request_sha256 !== options.batchSuccess.batch_request_sha256 ||
759
- planBatchProof.summary_audit_id !== options.batchSuccess.summary_audit_id ||
760
- options.batchSuccess.plan_request_sha256 !== options.planSuccess.plan_request_sha256 ||
761
- options.batchSuccess.plan_summary_audit_id !== options.planSuccess.summary_audit_id ||
762
- !options.batch.action_ids.every((actionId) => options.progress.successes.get(actionId)?.batch_request_sha256 ===
763
- options.batchSuccess.batch_request_sha256 &&
764
- options.progress.successes.get(actionId)?.summary_audit_id ===
765
- options.batchSuccess.summary_audit_id &&
766
- options.progress.successes.get(actionId)?.plan_request_sha256 ===
767
- options.planSuccess.plan_request_sha256 &&
768
- options.progress.successes.get(actionId)?.plan_summary_audit_id ===
769
- options.planSuccess.summary_audit_id)) {
770
- return false;
771
- }
772
- const expected = new Map(options.batch.exchange_rewrites.map((rewrite) => [
773
- aliasExchangeProgressKey({ batch_id: options.batch.batch_id, ...rewrite }),
774
- rewrite,
775
- ]));
776
- const exchangeKeys = new Set();
777
- for (const value of readJsonLinesIfPresent(options.exchangeProgressPath)) {
778
- if (!isJsonObject(value) || value.batch_id !== options.batch.batch_id)
779
- continue;
780
- const key = typeof value.action_id === 'string' &&
781
- typeof value.exchange_index === 'number' &&
782
- typeof value.data_set_internal_id === 'string'
783
- ? aliasExchangeProgressKey({
784
- batch_id: options.batch.batch_id,
785
- action_id: value.action_id,
786
- exchange_index: value.exchange_index,
787
- data_set_internal_id: value.data_set_internal_id,
788
- })
789
- : '';
790
- const rewrite = expected.get(key);
791
- const rowProof = rewrite ? options.progress.successes.get(rewrite.action_id) : null;
792
- if (!rewrite ||
793
- !rowProof ||
794
- value.schema_version !== 1 ||
795
- value.plan_sha256 !== options.plan.plan_sha256 ||
796
- value.operation_id !== options.plan.operation_id ||
797
- value.target_mode !== 'owner_draft' ||
798
- value.batch_request_sha256 !== options.batchSuccess.batch_request_sha256 ||
799
- value.batch_request_sha256 !== rowProof.batch_request_sha256 ||
800
- value.summary_audit_id !== options.batchSuccess.summary_audit_id ||
801
- value.summary_audit_id !== rowProof.summary_audit_id ||
802
- value.plan_request_sha256 !== options.planSuccess.plan_request_sha256 ||
803
- value.plan_request_sha256 !== rowProof.plan_request_sha256 ||
804
- value.plan_summary_audit_id !== options.planSuccess.summary_audit_id ||
805
- value.plan_summary_audit_id !== rowProof.plan_summary_audit_id ||
806
- value.factor !== options.batch.factor ||
807
- value.result !== 'success' ||
808
- !isJsonObject(value.actor) ||
809
- value.actor.user_id !== options.plan.account.user_id ||
810
- value.actor.email !== options.plan.account.email ||
811
- typeof value.logged_at_utc !== 'string' ||
812
- typeof value.database_audit_id !== 'string' ||
813
- !POSITIVE_INTEGER_TEXT.test(value.database_audit_id) ||
814
- value.database_audit_id !== rowProof.database_audit_id ||
815
- typeof value.summary_audit_id !== 'string' ||
816
- !POSITIVE_INTEGER_TEXT.test(value.summary_audit_id) ||
817
- sha256Json({
818
- action_id: value.action_id,
819
- process_id: value.process_id,
820
- process_version: value.process_version,
821
- exchange_index: value.exchange_index,
822
- data_set_internal_id: value.data_set_internal_id,
823
- flow_id: value.flow_id,
824
- flow_version: value.flow_version,
825
- direction: value.direction,
826
- before_exchange_sha256: value.before_exchange_sha256,
827
- before_mean_amount: value.before_mean_amount,
828
- before_resulting_amount: value.before_resulting_amount,
829
- after_mean_amount: value.after_mean_amount,
830
- after_resulting_amount: value.after_resulting_amount,
831
- after_exchange_sha256: value.after_exchange_sha256,
832
- }) !== sha256Json(rewrite) ||
833
- exchangeKeys.has(key)) {
834
- return false;
835
- }
836
- exchangeKeys.add(key);
837
- }
838
- return options.batch.exchange_rewrites.every((rewrite) => exchangeKeys.has(aliasExchangeProgressKey({ batch_id: options.batch.batch_id, ...rewrite })));
839
- }
840
- function aliasPlanDerivedLogsComplete(options) {
841
- return orderedAliasBatches(options.plan).every((batch) => {
842
- const batchSuccess = options.batchProgress.successes.get(batch.batch_id);
843
- return Boolean(batchSuccess &&
844
- aliasBatchDerivedLogsComplete({
845
- plan: options.plan,
846
- batch,
847
- planSuccess: options.planSuccess,
848
- batchSuccess,
849
- progress: options.progress,
850
- exchangeProgressPath: options.exchangeProgressPath,
851
- }));
852
- });
853
- }
854
666
  function appendAliasSuccessLogs(options) {
855
667
  const batchRpc = options.execution.rpc.batches.get(options.batch.dimension);
856
668
  for (const actionId of options.batch.action_ids) {
@@ -1105,32 +917,6 @@ function appendAliasProofProgress(options) {
1105
917
  options.planProgress.latestFailure = null;
1106
918
  return entry;
1107
919
  }
1108
- function appendAliasPlanFailure(options) {
1109
- const entry = {
1110
- schema_version: 1,
1111
- plan_sha256: options.plan.plan_sha256,
1112
- operation_id: options.plan.operation_id,
1113
- target_mode: 'owner_draft',
1114
- actor: { user_id: options.context.account.user_id, email: options.context.account.email },
1115
- started_at_utc: options.startedAt,
1116
- ended_at_utc: options.endedAt,
1117
- plan_request_sha256: null,
1118
- idempotent_replay: null,
1119
- batch_count: 2,
1120
- row_count: 52,
1121
- exchange_count: 59,
1122
- summary_audit_id: null,
1123
- batches: [],
1124
- result: 'failed',
1125
- error: errorMessage(options.error),
1126
- };
1127
- appendStableJsonLine(options.progressPath, entry);
1128
- options.planProgress.entries.push(entry);
1129
- if (!options.planProgress.success) {
1130
- options.planProgress.latestFailure = entry;
1131
- }
1132
- return entry;
1133
- }
1134
920
  async function executeAction(options) {
1135
921
  if (options.action.action === 'rebuild_derivatives') {
1136
922
  throw new CliError('Derivative rebuild actions may only execute through the guarded whole-plan RPC.', {
@@ -1324,6 +1110,12 @@ export async function runDatasetMaintenanceApply(options) {
1324
1110
  details: plan.blockers,
1325
1111
  });
1326
1112
  }
1113
+ if (plan.operation === 'merge-support-aliases') {
1114
+ throw new CliError('merge-support-aliases is sealed for dataset maintenance run-protected and cannot use ordinary apply.', {
1115
+ code: 'DATASET_MAINTENANCE_PROTECTED_RUN_REQUIRED',
1116
+ exitCode: 1,
1117
+ });
1118
+ }
1327
1119
  if (plan.operation === 'redo-import' &&
1328
1120
  !plan.source_import_run_id &&
1329
1121
  plan.source_lineage === null) {
@@ -1335,9 +1127,6 @@ export async function runDatasetMaintenanceApply(options) {
1335
1127
  const progressPath = path.join(planDir, plan.operation === 'rebuild-derivatives'
1336
1128
  ? 'derivative-submit-progress.jsonl'
1337
1129
  : 'apply-progress.jsonl');
1338
- const aliasPlanProgressPath = path.join(planDir, 'alias-plan-progress.jsonl');
1339
- const aliasBatchProgressPath = path.join(planDir, 'alias-batch-progress.jsonl');
1340
- const aliasExchangeProgressPath = path.join(planDir, 'alias-exchange-progress.jsonl');
1341
1130
  return withStateFileLock(progressPath, { reason: `dataset_maintenance_apply_${plan.operation_id}` }, async () => {
1342
1131
  const context = await resolveMaintenanceRemoteContext({
1343
1132
  env: options.env,
@@ -1364,13 +1153,7 @@ export async function runDatasetMaintenanceApply(options) {
1364
1153
  const progress = plan.operation === 'rebuild-derivatives'
1365
1154
  ? { entries: [], successes: new Map(), latestFailures: new Map() }
1366
1155
  : parseProgress(plan, progressPath);
1367
- let resumedSuccesses = progress.successes.size;
1368
- const aliasPlanProgress = plan.operation === 'merge-support-aliases'
1369
- ? parseAliasPlanProgress(plan, aliasPlanProgressPath)
1370
- : { entries: [], success: null, latestFailure: null };
1371
- const aliasBatchProgress = plan.operation === 'merge-support-aliases'
1372
- ? parseAliasBatchProgress(plan, aliasBatchProgressPath)
1373
- : { entries: [], successes: new Map() };
1156
+ const resumedSuccesses = progress.successes.size;
1374
1157
  const current = await fetchMaintenanceAccountRows({
1375
1158
  context,
1376
1159
  userId: plan.account.user_id,
@@ -1380,11 +1163,8 @@ export async function runDatasetMaintenanceApply(options) {
1380
1163
  planDir,
1381
1164
  currentRows: current.rows,
1382
1165
  progress,
1383
- aliasPlanProgress,
1166
+ aliasPlanProgress: { entries: [], success: null, latestFailure: null },
1384
1167
  });
1385
- if (plan.operation === 'merge-support-aliases') {
1386
- await assertAliasSupportSnapshots({ plan, context });
1387
- }
1388
1168
  const approvalPath = path.join(planDir, 'approval-record.json');
1389
1169
  const approvalAlreadyExisted = existsSync(approvalPath);
1390
1170
  validateApprovalRecord({ path: approvalPath, plan, context });
@@ -1504,140 +1284,6 @@ export async function runDatasetMaintenanceApply(options) {
1504
1284
  writeJsonArtifact(report.artifacts.commit_report, report);
1505
1285
  return report;
1506
1286
  }
1507
- if (plan.operation === 'merge-support-aliases') {
1508
- const alreadyComplete = Boolean(aliasPlanProgress.success &&
1509
- aliasPlanDerivedLogsComplete({
1510
- plan,
1511
- planSuccess: aliasPlanProgress.success,
1512
- batchProgress: aliasBatchProgress,
1513
- progress,
1514
- exchangeProgressPath: aliasExchangeProgressPath,
1515
- }));
1516
- resumedSuccesses = alreadyComplete ? plan.actions.length : 0;
1517
- let planSuccess = alreadyComplete ? aliasPlanProgress.success : null;
1518
- let planFailure = null;
1519
- if (!alreadyComplete) {
1520
- const startedAt = clock(options);
1521
- try {
1522
- const execution = await executeAliasPlan({ plan, planDir, context });
1523
- const endedAt = clock(options);
1524
- for (const batch of orderedAliasBatches(plan)) {
1525
- appendAliasSuccessLogs({
1526
- plan,
1527
- batch,
1528
- execution,
1529
- progress,
1530
- progressPath,
1531
- exchangeProgressPath: aliasExchangeProgressPath,
1532
- context,
1533
- startedAt,
1534
- endedAt,
1535
- });
1536
- }
1537
- planSuccess = appendAliasProofProgress({
1538
- plan,
1539
- execution,
1540
- planProgress: aliasPlanProgress,
1541
- batchProgress: aliasBatchProgress,
1542
- planProgressPath: aliasPlanProgressPath,
1543
- batchProgressPath: aliasBatchProgressPath,
1544
- context,
1545
- startedAt,
1546
- endedAt,
1547
- });
1548
- }
1549
- catch (error) {
1550
- planFailure = appendAliasPlanFailure({
1551
- plan,
1552
- planProgress: aliasPlanProgress,
1553
- progressPath: aliasPlanProgressPath,
1554
- context,
1555
- startedAt,
1556
- endedAt: clock(options),
1557
- error,
1558
- });
1559
- }
1560
- }
1561
- const fullyProven = Boolean(planSuccess &&
1562
- aliasPlanDerivedLogsComplete({
1563
- plan,
1564
- planSuccess,
1565
- batchProgress: aliasBatchProgress,
1566
- progress,
1567
- exchangeProgressPath: aliasExchangeProgressPath,
1568
- }));
1569
- const failureError = planFailure?.error ?? 'Whole-plan proof is incomplete.';
1570
- const actions = plan.actions.map((action) => {
1571
- return {
1572
- action_id: action.action_id,
1573
- action: action.action,
1574
- table: action.table,
1575
- id: action.id,
1576
- version: action.version,
1577
- status: fullyProven ? 'success' : 'failed',
1578
- error: fullyProven ? null : failureError,
1579
- };
1580
- });
1581
- const successCount = fullyProven ? actions.length : 0;
1582
- const failureCount = fullyProven ? 0 : actions.length;
1583
- const attemptPath = nextAttemptPath(planDir);
1584
- const report = {
1585
- schema_version: 1,
1586
- generated_at_utc: clock(options),
1587
- status: successCount === actions.length ? 'completed' : 'completed_with_failures',
1588
- task_id: plan.task_id,
1589
- operation: plan.operation,
1590
- operation_id: plan.operation_id,
1591
- target_mode: plan.target_mode,
1592
- plan_sha256: plan.plan_sha256,
1593
- actor: { user_id: context.account.user_id, email: context.account.email },
1594
- summary: {
1595
- actions: actions.length,
1596
- success: successCount,
1597
- failed: failureCount,
1598
- pending: actions.length - successCount - failureCount,
1599
- resumed_successes: resumedSuccesses,
1600
- },
1601
- actions,
1602
- artifacts: {
1603
- approval_record: approvalPath,
1604
- apply_progress: progressPath,
1605
- commit_report: path.join(planDir, 'commit-report.json'),
1606
- attempt_report: attemptPath,
1607
- alias_plan_progress: aliasPlanProgressPath,
1608
- alias_batch_progress: aliasBatchProgressPath,
1609
- alias_exchange_progress: aliasExchangeProgressPath,
1610
- },
1611
- database_audit: {
1612
- rpc_transaction_log: 'public.command_audit_log',
1613
- source: 'tiangong-lca dataset maintenance apply',
1614
- correlation_fields: [
1615
- 'plan_sha256',
1616
- 'operation_id',
1617
- 'target_visibility',
1618
- 'plan_request_sha256',
1619
- 'batch_id',
1620
- 'action_id',
1621
- 'batch_request_sha256',
1622
- ],
1623
- },
1624
- ...(fullyProven && planSuccess
1625
- ? {
1626
- alias_plan_proof: {
1627
- plan_request_sha256: planSuccess.plan_request_sha256,
1628
- summary_audit_id: planSuccess.summary_audit_id,
1629
- batch_count: 2,
1630
- row_count: 52,
1631
- exchange_count: 59,
1632
- idempotent_replay: planSuccess.idempotent_replay,
1633
- },
1634
- }
1635
- : {}),
1636
- };
1637
- writeImmutableJson(attemptPath, report);
1638
- writeJsonArtifact(report.artifacts.commit_report, report);
1639
- return report;
1640
- }
1641
1287
  const ordered = [...plan.actions].sort((left, right) => {
1642
1288
  const rank = {
1643
1289
  save_draft: 0,
@@ -1777,10 +1423,9 @@ export async function runDatasetMaintenanceApply(options) {
1777
1423
  });
1778
1424
  }
1779
1425
  export const __testInternals = {
1780
- aliasBatchDerivedLogsComplete,
1781
- aliasPlanDerivedLogsComplete,
1782
1426
  aliasExchangeProgressKey,
1783
1427
  appendAliasSuccessLogs,
1428
+ appendAliasProofProgress,
1784
1429
  assertApplyPreconditions,
1785
1430
  assertAliasSupportSnapshots,
1786
1431
  buildAliasBatchRequest,