@happyvertical/smrt-cli 0.40.68 → 0.40.70

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/AGENTS.md CHANGED
@@ -7,12 +7,16 @@ Developer CLI with lazy-loaded commands, manifest discovery, and class introspec
7
7
  ```
8
8
  smrt introspect # Discover SMRT objects in project
9
9
  smrt doctor # Umbrella diagnostics; can verify a generation snapshot
10
+ smrt doctor --db # Add the live-schema parity section (see below)
10
11
  smrt db:status # Pending schema changes + failed migration classification
12
+ smrt db:status --parity # Same, plus live-schema parity (see below)
11
13
  smrt db:migrate # Apply migrations
14
+ smrt db:migrate --postgres-safe # PostgreSQL concurrent-index mode (see below)
12
15
  smrt db:migrate --force-migration <exact-id> [--force-migration <exact-id>...] # Force exact generated migrations in one atomic batch
13
16
  smrt db:migrate-uuid # Convert schema-declared UUID text columns after data remap
14
17
  smrt db:diff # Show schema differences without generating migration files
15
- smrt db:rollback # Rollback migrations
18
+ smrt db:rollback # Roll back migrations by executing their recorded DOWN
19
+ smrt db:rollback --mark-only # Record-only flip; schema deliberately untouched
16
20
  smrt docs:agents # Generate .agents/smrt-framework.md
17
21
  smrt docs:claude # Deprecated alias writing .claude/smrt-framework.md
18
22
  smrt dev:knowledge-* # Deterministic agent knowledge index/check/diff
@@ -34,6 +38,107 @@ migrations are manifest-driven through registered objects and project manifests.
34
38
 
35
39
  `smrt test` is **deprecated** — use vitest plugin directly.
36
40
 
41
+ ## `db:migrate` on PostgreSQL
42
+
43
+ `db:migrate` always bounds a PostgreSQL batch with `SET LOCAL lock_timeout` and
44
+ `SET LOCAL statement_timeout` inside its transaction, from
45
+ `migrations.postgres.lockTimeout` / `.statementTimeout` (defaults `30s` / `60s`;
46
+ accepts `ms`/`s`/`min`/`h` suffixes, and `0` disables as PostgreSQL defines it).
47
+ A migration queued behind a long-running writer therefore fails fast and rolls
48
+ back instead of holding the locks it already took against every writer.
49
+
50
+ `--postgres-safe` selects **concurrent-index mode**: non-index DDL still commits
51
+ in one transaction, then each index statement runs
52
+ `CREATE INDEX CONCURRENTLY` / `DROP INDEX CONCURRENTLY` on one pinned session
53
+ after that commit. This is the mode for large index rollouts.
54
+
55
+ - **Concurrent mode is not atomic.** Committed column/table changes survive a
56
+ later index failure; unfinished index migrations are recorded `failed`, and
57
+ `db:migrate` (which reconciles) retries them on the next run. The retry
58
+ resumes at the index build — their `error_message` carries a
59
+ `[smrt: concurrent-index phase 1 committed]` marker, so the non-index
60
+ statements that already committed are not re-run.
61
+ - INVALID indexes — the stump a cancelled or timed-out
62
+ `CREATE INDEX CONCURRENTLY` leaves, which `pg_indexes` still reports as
63
+ present — are detected via `pg_index.indisvalid` and dropped before the
64
+ rebuild.
65
+ - `migrations.postgres.useConcurrently: false` vetoes the flag; index DDL then
66
+ runs inside the atomic transaction (still bounded by the timeouts).
67
+ - Without the flag, a batch containing explicit `CONCURRENTLY` DDL is rejected
68
+ before the transaction opens — PostgreSQL cannot run it there.
69
+
70
+ ## Live-schema parity (#2368)
71
+
72
+ `doctor --db` and `db:status --parity` share `src/commands/db-parity.ts`, which
73
+ runs core's `checkLiveSchemaParity()`. This answers a different question than
74
+ `db:status`/`db:diff`: those compare the live database to the **manifest**, i.e.
75
+ to the artifact that dropped the index in the first place, which is why a
76
+ database missing 164 tenant-column indexes reported "in sync" (#2356).
77
+
78
+ Expected shape comes from the manifest schemas, the hand-DDL `_smrt_*` system
79
+ tables (parsed by core's `system-table-shapes.ts` — they are in no manifest and
80
+ enter no diff), and an index policy that consults no manifest at all: every
81
+ foreign-key/cross-package-ref/tenant column leads an index, every registry
82
+ conflict target has a matching UNIQUE index, every `unique: true` column is
83
+ unique live. Conflict targets come from `ObjectRegistry.getConflictColumns()`,
84
+ not from the schema definition.
85
+
86
+ - Severity is the contract: `error` fails the command (missing table/column,
87
+ type drift, orphan NOT NULL, conflict target unindexed or non-unique,
88
+ PostgreSQL INVALID index), `warning` does not (index coverage), `info` is
89
+ hidden without `--verbose` (undeclared tables/columns/indexes).
90
+ - Both surfaces **fail closed**: an unreachable database, or an adapter with no
91
+ `getTableSchema`, is an error, never a silent pass. Where index metadata
92
+ cannot be read at all, index checks are skipped and the report says so
93
+ (`indexIntrospection: 'unavailable'`) rather than inventing missing indexes.
94
+ - The check is read-only and lives in a new core module; it does not share code
95
+ with `migrations/differ.ts`.
96
+
97
+ ## `db:migrate` on SQLite: type changes rebuild the table
98
+
99
+ SQLite has no `ALTER COLUMN ... TYPE`, so a type-bucket change (the common one
100
+ being a numeric default edited `0` → `0.0`) is applied as the documented table
101
+ rebuild — stage, copy, drop, rename, replay indexes and triggers — planned by
102
+ `smrt-core`'s `migrations/sqlite-rebuild.ts` and executed inside the same
103
+ atomic batch as everything else. It is no longer a "manual intervention" that
104
+ makes `db:migrate` exit 1 on every run (#2370). All drifted columns of one
105
+ table are fixed by one rebuild; `--dry-run` prints the whole statement list.
106
+
107
+ The rebuild refuses — and the column stays manual drift — when another table
108
+ declares a foreign key onto the target while `PRAGMA foreign_keys` is ON,
109
+ because `DROP TABLE` would fire those children's `ON DELETE` actions. Fix that
110
+ one by hand (or against a connection with enforcement disabled).
111
+
112
+ ## `db:rollback` is execute-or-refuse
113
+
114
+ Schema state is diff-driven: `db:migrate` derives every migration from the
115
+ manifest at run time and stores **no SQL** in `_smrt_schema_migrations`. So
116
+ `db:rollback` can only honour the one DOWN script that is reconstructible from
117
+ a tracking row — `create_table_<table>` → `DROP TABLE IF EXISTS "<table>"`,
118
+ the exact statement `db:migrate` records for `diff.added_tables`.
119
+
120
+ - Rows with a reconstructible DOWN are **executed** through
121
+ `MigrationTracker.rollback` (transactional), then marked `rolled_back`.
122
+ - Anything else is **refused**: non-zero exit, an error naming each migration
123
+ and why, and no row touched. Refusal is all-or-nothing across the selected
124
+ set — a partial revert would leave the chain in a state the remaining DOWN
125
+ scripts were not written against. This includes rows recorded
126
+ `is_reversible` under a caller-chosen name: reversible at apply time, but the
127
+ SQL was never persisted, so it cannot be replayed (#2378).
128
+ - `--mark-only` is the explicit opt-in for the record-only flip (for an
129
+ operator who already reverted the schema by hand). It says in its own output
130
+ that the schema was not changed, and it is the only path that moves a row
131
+ without running DDL.
132
+ - A failed DOWN stops the batch; the migrations behind it are reported
133
+ `Not attempted` and left `completed`.
134
+ - `--dry-run` previews the DOWN statements and still exits non-zero when the
135
+ real run would refuse. Both `--dry-run` and `--mark-only` are declared
136
+ kebab-cased, so handlers must read `options['dry-run']` / `options['mark-only']`
137
+ (`parseCliArgs` returns keys verbatim — the #1385 data-loss class).
138
+
139
+ Reverting a non-`create_table` change is a forward operation: update the
140
+ `@smrt` object definitions and run `db:migrate` again.
141
+
37
142
  ## Architecture
38
143
 
39
144
  - **Lazy command loading**: commands loaded on-demand via dynamic import (~100ms overhead on first use)
@@ -64,3 +169,7 @@ migrations are manifest-driven through registered objects and project manifests.
64
169
  global when it appears before or without a subcommand (#2279). `--help` is
65
170
  deliberately untouched.
66
171
  - **Schema history nuance**: `db:status` / `db:history` should distinguish active live drift from superseded failed generated schema repairs instead of treating all failed rows as current blockers
172
+ - **Decorator check follows the Vite major**: doctor requires `oxc.decorator` in
173
+ vite.config on Vite 8+ and accepts tsconfig `experimentalDecorators` only
174
+ below it. The old unconditional tsconfig check flagged correct Vite 8 projects
175
+ and passed broken ones (#2368)
package/README.md CHANGED
@@ -48,7 +48,8 @@ The four snapshot options are atomic: supplying any one requires all four.
48
48
  | `smrt db:migrate --force-migration <exact-id> [--force-migration <exact-id>...]` | Force one or more exact generated migrations in one atomic batch while preserving every other guard |
49
49
  | `smrt db:migrate-uuid` | Convert schema-declared UUID text columns to native PostgreSQL uuid after data has been remapped |
50
50
  | `smrt db:diff` | Show schema differences without generating migration files |
51
- | `smrt db:rollback` | Rollback last migration |
51
+ | `smrt db:rollback` | Roll back the last migration by executing its recorded DOWN script; refuses when no DOWN script exists |
52
+ | `smrt db:rollback --mark-only` | Record-only: mark migrations rolled back without running any DOWN script (schema untouched) |
52
53
  | `smrt db:history` | Show migration history with active-vs-superseded failure classification |
53
54
 
54
55
  File-backed SQL/TypeScript migration generation is not supported. s-m-r-t schema