This guide defines the operational artifact teams should expect around planReflect(...) even before the runtime exports a persisted plan format directly.
planReflect(...) already returns useful preview data, but teams operating shared environments usually need more than an in-memory object.
They need an artifact that can be:
A good plan artifact should answer these questions without executing reflect(...):
The recommended shape is a stable JSON document with four sections:
Recommended filename:
plan-reflect-artifact.json{
"metadata": {
"generatedAt": "2026-04-22T18:15:00.000Z",
"libraryVersion": "1.1.0",
"engine": "mysql",
"environment": "staging",
"target": {
"schema": "sqlh_test"
},
"source": {
"command": "npm run plan:staging",
"gitCommit": "25df719",
"release": "v1.1.0"
}
},
"summary": {
"tableCount": 3,
"actions": {
"create": 0,
"alter": 1,
"rebuild": 1,
"noop": 1
},
"requiresDestructiveApproval": true,
"requiresChangeTicket": true,
"blockedInProduction": false,
"usesDependencyOrchestration": true
},
"tables": [
{
"qualifiedName": "sqlh_test.users",
"action": "rebuild",
"renameHints": [
{
"kind": "case-only",
"from": "Legacy_Code",
"to": "legacy_code",
"approved": false
}
],
"addColumns": ["status"],
"droppedColumns": ["legacy_code"],
"incompatibleColumns": [],
"destructiveReasons": [
"column removals detected (legacy_code)",
"dependent objects detected (view:sqlh_test.vw_users)"
],
"blockingDependencies": [
{
"kind": "view",
"qualifiedName": "sqlh_test.vw_users"
}
],
"indexes": {
"create": ["idx_users_status"],
"drop": [],
"recreate": []
},
"policy": {
"requiresDestructiveApproval": true,
"requiresChangeTicket": true,
"blockedInProduction": false
}
}
],
"estimatedExecution": {
"level": "approximate",
"statements": [
"DROP VIEW IF EXISTS sqlh_test.vw_users;",
"CREATE TABLE __sqlh_rebuild_users ...",
"INSERT INTO __sqlh_rebuild_users (...) SELECT ... FROM sqlh_test.users;",
"DROP TABLE sqlh_test.users;",
"RENAME TABLE __sqlh_rebuild_users TO sqlh_test.users;",
"CREATE VIEW sqlh_test.vw_users AS ...;"
]
}
}
The exact keys can still evolve, but the top-level shape should remain stable enough for CI review and diffing.
The following interface set is the recommended first implementation target.
type PlanArtifactEngine = "sqlite" | "postgres" | "mysql";
type PlanArtifactAction = "create" | "alter" | "rebuild" | "destructive-blocked" | "noop";
type PlanArtifactExecutionLevel = "exact" | "approximate" | "none";
interface PlanArtifactMetadata {
generatedAt: string;
libraryVersion: string;
engine: PlanArtifactEngine;
environment?: string;
target?: {
schema?: string;
database?: string;
};
source?: {
command?: string;
gitCommit?: string;
release?: string;
};
}
interface PlanArtifactSummary {
tableCount: number;
actions: {
create: number;
alter: number;
rebuild: number;
noop: number;
};
requiresDestructiveApproval: boolean;
requiresChangeTicket: boolean;
blockedInProduction: boolean;
usesDependencyOrchestration: boolean;
}
interface PlanArtifactDependency {
kind: string;
qualifiedName: string;
details?: string;
}
type PlanArtifactRenameHintKind = "case-only" | "suspected-rename";
interface PlanArtifactRenameHint {
kind: PlanArtifactRenameHintKind;
from: string;
to: string;
approved: boolean;
notes?: string;
}
interface PlanArtifactTableEntry {
qualifiedName: string;
action: PlanArtifactAction;
renameHints: PlanArtifactRenameHint[];
addColumns: string[];
droppedColumns: string[];
incompatibleColumns: string[];
destructiveReasons: string[];
blockingDependencies: PlanArtifactDependency[];
indexes: {
create: string[];
drop: string[];
recreate: string[];
};
policy: {
requiresDestructiveApproval: boolean;
requiresChangeTicket: boolean;
blockedInProduction: boolean;
};
}
interface PlanArtifactEstimatedExecution {
level: PlanArtifactExecutionLevel;
statements: string[];
}
interface PlanReflectArtifact {
metadata: PlanArtifactMetadata;
summary: PlanArtifactSummary;
tables: PlanArtifactTableEntry[];
estimatedExecution: PlanArtifactEstimatedExecution;
}
This is intentionally narrow. It should be enough for the first persisted implementation without locking the runtime into an oversized schema.
Metadata should include:
Summary should aggregate:
Each table entry should expose at least:
This makes the artifact usable both for engineering review and operational approval.
Rename handling should be explicit in the artifact instead of being inferred only from dropped and added columns.
The first useful distinction is:
case-only: the runtime detected a casing-only rename that may already map to an existing approval modelsuspected-rename: the runtime or review tooling believes a drop-plus-add pair may really be a rename, but cannot prove it safelyRecommended rules:
approved: true means the runtime already has enough approval data to treat the hint as reviewedapproved: false means reviewers still need to decide whether the hint is legitimateThe artifact is not responsible for inventing rename semantics. It is responsible for exposing them clearly enough that reviewers do not mistake a rename candidate for a routine destructive delete.
The summary section should eventually expose rename pressure as well.
Recommended future summary fields:
renameHintCountapprovedRenameHintCountunapprovedRenameHintCountThat makes rename-heavy schema refactors visible before execution.
To keep the artifact diff-friendly:
addColumns, droppedColumns, and index listsStable ordering matters as much as field coverage if the artifact will live in CI or release evidence.
The artifact does not need exact final SQL for every engine on day one, but it should aim to provide one of these levels:
The important part is that reviewers can see the expected execution boundary before runtime execution begins.
Suggested execution levels:
exact: runtime can provide the real SQL sequence confidentlyapproximate: runtime can provide a faithful execution outline but not every final statement verbatimnone: runtime currently cannot emit meaningful SQL estimates for that part of the planWhen rename hints exist, estimated execution should prefer clarity over confidence. If the runtime cannot guarantee rename-safe SQL, the artifact should still show the likely rebuild or copy path instead of pretending a safe rename statement exists.
The recommended workflow is:
That turns planReflect(...) from a preview helper into an operational review object.
The current preview types remain the execution-facing contract.
The plan artifact should be understood as a persisted projection of that data, enriched with:
The minimum useful first version of a persisted plan artifact would be:
That is enough to support review and audit even before richer UI or HTML rendering exists.
Once the basic JSON artifact exists, the next useful additions would be:
At that point the likely extension path is a objects array next to tables, instead of overloading table entries with every future engine-specific object kind.
Another likely extension is a dedicated reviewHints array that can carry rename hints, drift suspicions, and engine-specific cautions without bloating the core table fields too early.
Use semantic-change-detection.md as the stricter design reference for how rename hints, equivalence, destructive changes, and unknown-risk states should be classified.