@happyvertical/smrt-cli 0.40.23 → 0.40.25

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/README.md CHANGED
@@ -57,7 +57,7 @@ batch.
57
57
 
58
58
  ### Audited PostgreSQL timestamp conversion
59
59
 
60
- `timestamp without time zone` values do not carry enough information for SMRT
60
+ `timestamp without time zone` values do not carry enough information for s-m-r-t
61
61
  to infer their original instant. After auditing every historical writer,
62
62
  database default, trigger, and raw SQL path, an operator may confirm that the
63
63
  legacy values are UTC wall times and include the exact opt-in:
@@ -155,7 +155,7 @@ Custom methods on s-m-r-t objects are auto-discovered from manifests. Method par
155
155
  ## Usage
156
156
 
157
157
  ```bash
158
- # Discover what SMRT objects are available
158
+ # Discover what s-m-r-t objects are available
159
159
  smrt introspect
160
160
 
161
161
  # Generate an MCP server
@@ -691,6 +691,24 @@ function sqlShapeFingerprint(action) {
691
691
  const normalized = parts.map((s) => s.replace(/\s+/g, " ").trim()).join(";");
692
692
  return createHash("sha256").update(normalized).digest("hex").slice(0, 8);
693
693
  }
694
+ /**
695
+ * Keep type-upgrade tracker identities tied to the specific conversion, not
696
+ * merely the table and column. A column may legitimately undergo multiple
697
+ * upgrades over its lifetime; reusing the old bare id would make
698
+ * MigrationTracker compare different SQL checksums against applied history.
699
+ *
700
+ * Include the differ's source/target types as well as the normalized SQL
701
+ * shape. The type data keeps fallback identities meaningful when SQL is not
702
+ * available, while the SQL fingerprint distinguishes conversions with the
703
+ * same endpoints but different semantics (for example a UTC interpretation).
704
+ */
705
+ function typeUpgradeFingerprint(action) {
706
+ const source = action.mismatch?.actual ?? "";
707
+ const target = action.mismatch?.expected ?? action.column?.type ?? "";
708
+ const conversion = sqlShapeFingerprint(action);
709
+ if (!source && !target && !conversion) return "";
710
+ return createHash("sha256").update(`source:${source};target:${target};conversion:${conversion}`).digest("hex").slice(0, 8);
711
+ }
694
712
  function classifyTypeUpgradeSql(sql) {
695
713
  const trimmed = sql?.trim();
696
714
  if (!trimmed) return "manual";
@@ -711,7 +729,11 @@ function getSyntheticMigrationNameForAction(action) {
711
729
  const fingerprint = sqlShapeFingerprint(action);
712
730
  return fingerprint ? `drop_index_${action.indexName}_${fingerprint}` : `drop_index_${action.indexName}`;
713
731
  }
714
- case "type_upgrade": return action.column ? `type_upgrade_${action.tableName}_${action.column.name}` : null;
732
+ case "type_upgrade": {
733
+ if (!action.column) return null;
734
+ const fingerprint = typeUpgradeFingerprint(action);
735
+ return fingerprint ? `type_upgrade_${action.tableName}_${action.column.name}_${fingerprint}` : `type_upgrade_${action.tableName}_${action.column.name}`;
736
+ }
715
737
  default: return null;
716
738
  }
717
739
  }
@@ -736,10 +758,31 @@ function getSyntheticMigrationNameForChange(change) {
736
758
  });
737
759
  return fingerprint ? `drop_index_${change.name}_${fingerprint}` : `drop_index_${change.name}`;
738
760
  }
739
- case "type_upgrade": return change.name ? `type_upgrade_${change.table}_${change.name}` : null;
761
+ case "type_upgrade": {
762
+ if (!change.name) return null;
763
+ const fingerprint = typeUpgradeFingerprint(change);
764
+ return fingerprint ? `type_upgrade_${change.table}_${change.name}_${fingerprint}` : `type_upgrade_${change.table}_${change.name}`;
765
+ }
740
766
  default: return null;
741
767
  }
742
768
  }
769
+ /**
770
+ * Return every tracker name that can represent the current schema change.
771
+ *
772
+ * New type upgrades use a fingerprinted id, but releases before #2111 wrote
773
+ * failures under the bare table-and-column id. Keep that old id as a
774
+ * classification alias only: execution always uses the fingerprinted id, so
775
+ * no applied migration record is rewritten or reused for a new conversion.
776
+ */
777
+ function getSyntheticMigrationNamesForChange(change) {
778
+ const migrationName = getSyntheticMigrationNameForChange(change);
779
+ if (!migrationName) return [];
780
+ if (change.type === "type_upgrade" && change.name) {
781
+ const legacyName = `type_upgrade_${change.table}_${change.name}`;
782
+ return migrationName === legacyName ? [migrationName] : [migrationName, legacyName];
783
+ }
784
+ return [migrationName];
785
+ }
743
786
  function shouldFailDbMigrate(state) {
744
787
  return (state.tableErrorCount ?? 0) > 0 || (state.migrationErrorCount ?? 0) > 0 || (state.stiErrorCount ?? 0) > 0 || !state.dryRun && (state.manualInterventionCount ?? 0) > 0;
745
788
  }
@@ -755,8 +798,7 @@ function getUnresolvedGeneratedMigrationNames(changes) {
755
798
  for (const change of changes) {
756
799
  if (change.type !== "add_column" && change.type !== "add_index" && change.type !== "drop_index" && change.type !== "type_upgrade") continue;
757
800
  if (change.type === "type_upgrade" && classifyTypeUpgradeSql(change.sql) === "noop") continue;
758
- const migrationName = getSyntheticMigrationNameForChange(change);
759
- if (migrationName) names.add(migrationName);
801
+ for (const migrationName of getSyntheticMigrationNamesForChange(change)) names.add(migrationName);
760
802
  }
761
803
  return names;
762
804
  }
@@ -891,18 +933,15 @@ function partitionSchemaChanges(changes, getClassForTable) {
891
933
  }
892
934
  //#endregion
893
935
  //#region src/commands/migration-failure-analysis.ts
894
- function getActionableGeneratedMigrationName(change) {
936
+ function getActionableGeneratedMigrationNames(change) {
895
937
  if (change.type === "type_upgrade" && change.name) {
896
- if (classifyTypeUpgradeSql(change.sql) === "noop") return null;
938
+ if (classifyTypeUpgradeSql(change.sql) === "noop") return [];
897
939
  }
898
- return getSyntheticMigrationNameForChange(change);
940
+ return getSyntheticMigrationNamesForChange(change);
899
941
  }
900
942
  function getActionableFailedMigrationNames(diff) {
901
943
  const names = /* @__PURE__ */ new Set();
902
- for (const change of diff.changes) {
903
- const name = getActionableGeneratedMigrationName(change);
904
- if (name) names.add(name);
905
- }
944
+ for (const change of diff.changes) for (const name of getActionableGeneratedMigrationNames(change)) names.add(name);
906
945
  return names;
907
946
  }
908
947
  function classifyKind(name) {
package/dist/index.js CHANGED
@@ -48,56 +48,56 @@ var _docsCommands = null;
48
48
  var _playgroundCommands = null;
49
49
  async function getGnodeCommands() {
50
50
  if (!_gnodeCommands) {
51
- const { gnodeCommands } = await import("./commands-CR1RZCYu.js");
51
+ const { gnodeCommands } = await import("./commands-gjGWLmwD.js");
52
52
  _gnodeCommands = gnodeCommands;
53
53
  }
54
54
  return _gnodeCommands;
55
55
  }
56
56
  async function getGitCommands() {
57
57
  if (!_gitCommands) {
58
- const { gitCommands } = await import("./commands-CR1RZCYu.js");
58
+ const { gitCommands } = await import("./commands-gjGWLmwD.js");
59
59
  _gitCommands = gitCommands;
60
60
  }
61
61
  return _gitCommands;
62
62
  }
63
63
  async function getGenerateCommands() {
64
64
  if (!_generateCommands) {
65
- const { generateCommands } = await import("./commands-CR1RZCYu.js");
65
+ const { generateCommands } = await import("./commands-gjGWLmwD.js");
66
66
  _generateCommands = generateCommands;
67
67
  }
68
68
  return _generateCommands;
69
69
  }
70
70
  async function getInitCommands() {
71
71
  if (!_initCommands) {
72
- const { initCommands } = await import("./commands-CR1RZCYu.js");
72
+ const { initCommands } = await import("./commands-gjGWLmwD.js");
73
73
  _initCommands = initCommands;
74
74
  }
75
75
  return _initCommands;
76
76
  }
77
77
  async function getUtilityCommands() {
78
78
  if (!_utilityCommands) {
79
- const { utilityCommands } = await import("./commands-CR1RZCYu.js");
79
+ const { utilityCommands } = await import("./commands-gjGWLmwD.js");
80
80
  _utilityCommands = utilityCommands;
81
81
  }
82
82
  return _utilityCommands;
83
83
  }
84
84
  async function getDispatchCommands() {
85
85
  if (!_dispatchCommands) {
86
- const { dispatchCommands } = await import("./commands-CR1RZCYu.js");
86
+ const { dispatchCommands } = await import("./commands-gjGWLmwD.js");
87
87
  _dispatchCommands = dispatchCommands;
88
88
  }
89
89
  return _dispatchCommands;
90
90
  }
91
91
  async function getDocsCommands() {
92
92
  if (!_docsCommands) {
93
- const { docsCommands } = await import("./commands-CR1RZCYu.js");
93
+ const { docsCommands } = await import("./commands-gjGWLmwD.js");
94
94
  _docsCommands = docsCommands;
95
95
  }
96
96
  return _docsCommands;
97
97
  }
98
98
  async function getPlaygroundCommands() {
99
99
  if (!_playgroundCommands) {
100
- const { playgroundCommands } = await import("./commands-CR1RZCYu.js");
100
+ const { playgroundCommands } = await import("./commands-gjGWLmwD.js");
101
101
  _playgroundCommands = playgroundCommands;
102
102
  }
103
103
  return _playgroundCommands;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-cli",
3
- "version": "0.40.23",
3
+ "version": "0.40.25",
4
4
  "description": "Developer CLI for SMRT framework - introspection, testing, and project management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -32,12 +32,12 @@
32
32
  "acorn": "^8.17.0",
33
33
  "fast-glob": "3.3.3",
34
34
  "tar": "^7.5.19",
35
- "@happyvertical/smrt-agents": "0.40.23",
36
- "@happyvertical/smrt-config": "0.40.23",
37
- "@happyvertical/smrt-dev-mcp": "0.40.23",
38
- "@happyvertical/smrt-core": "0.40.23",
39
- "@happyvertical/smrt-playground": "0.40.23",
40
- "@happyvertical/smrt-types": "0.40.23"
35
+ "@happyvertical/smrt-agents": "0.40.25",
36
+ "@happyvertical/smrt-config": "0.40.25",
37
+ "@happyvertical/smrt-core": "0.40.25",
38
+ "@happyvertical/smrt-dev-mcp": "0.40.25",
39
+ "@happyvertical/smrt-types": "0.40.25",
40
+ "@happyvertical/smrt-playground": "0.40.25"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "24.13.2",