feat(ui): add chat-driven dataset review flow

Move dataset review clarification into the assistant workspace and
rework the review page into a chat-centric layout with execution rails.

Add session-scoped assistant actions for mappings, semantic fields,
and SQL preview generation. Introduce optimistic locking for dataset
review mutations, propagate session versions through API responses,
and mask imported filter values before assistant exposure.

Refresh tests, i18n, and spec artifacts to match the new workflow.

BREAKING CHANGE: dataset review mutation endpoints now require the
X-Session-Version header, and clarification is no longer handled
through ClarificationDialog-based flows
This commit is contained in:
2026-03-26 13:33:12 +03:00
parent c56b8dad92
commit 202823aba5
74 changed files with 6122 additions and 2970 deletions

View File

@@ -467,6 +467,90 @@ def _ensure_filter_source_enum_values(bind_engine):
# [/DEF:_ensure_filter_source_enum_values:Function]
# [DEF:_ensure_dataset_review_session_columns:Function]
# @COMPLEXITY: 4
# @PURPOSE: Apply additive schema upgrades for dataset review persistence required by optimistic-lock and recovery metadata semantics.
# @PRE: bind_engine points to the application database where dataset review tables are stored.
# @POST: Missing additive columns across legacy dataset review tables are created without removing existing data.
# @SIDE_EFFECT: Executes ALTER TABLE statements against dataset review tables in the application database.
# @RELATION: [DEPENDS_ON] ->[DatasetReviewSession]
# @RELATION: [DEPENDS_ON] ->[ImportedFilter]
def _ensure_dataset_review_session_columns(bind_engine):
with belief_scope("_ensure_dataset_review_session_columns"):
inspector = inspect(bind_engine)
existing_tables = set(inspector.get_table_names())
migration_plan = {
"dataset_review_sessions": [
(
"version",
"ALTER TABLE dataset_review_sessions "
"ADD COLUMN version INTEGER NOT NULL DEFAULT 0",
)
],
"imported_filters": [
(
"raw_value_masked",
"ALTER TABLE imported_filters "
"ADD COLUMN raw_value_masked BOOLEAN NOT NULL DEFAULT FALSE",
)
],
}
for table_name, planned_columns in migration_plan.items():
if table_name not in existing_tables:
logger.reason(
"Dataset review table does not exist yet; skipping additive schema migration",
extra={"table": table_name},
)
continue
existing_columns = {
str(column.get("name") or "").strip()
for column in inspector.get_columns(table_name)
}
alter_statements = [
statement
for column_name, statement in planned_columns
if column_name not in existing_columns
]
if not alter_statements:
logger.reason(
"Dataset review table schema already up to date",
extra={"table": table_name, "columns": sorted(existing_columns)},
)
continue
logger.reason(
"Applying additive dataset review schema migration",
extra={"table": table_name, "statements": alter_statements},
)
try:
with bind_engine.begin() as connection:
for statement in alter_statements:
connection.execute(text(statement))
logger.reflect(
"Dataset review additive schema migration completed",
extra={
"table": table_name,
"added_columns": [
stmt.split(" ADD COLUMN ", 1)[1].split()[0]
for stmt in alter_statements
],
},
)
except Exception as migration_error:
logger.explore(
"Dataset review additive schema migration failed",
extra={"table": table_name, "error": str(migration_error)},
)
raise
# [/DEF:_ensure_dataset_review_session_columns:Function]
# [DEF:init_db:Function]
# @COMPLEXITY: 3
# @PURPOSE: Initializes the database by creating all tables.
@@ -475,6 +559,7 @@ def _ensure_filter_source_enum_values(bind_engine):
# @SIDE_EFFECT: Creates physical database files if they don't exist.
# @RELATION: [CALLS] ->[ensure_connection_configs_table]
# @RELATION: [CALLS] ->[_ensure_filter_source_enum_values]
# @RELATION: [CALLS] ->[_ensure_dataset_review_session_columns]
def init_db():
with belief_scope("init_db"):
Base.metadata.create_all(bind=engine)
@@ -487,6 +572,7 @@ def init_db():
_ensure_auth_users_columns(auth_engine)
ensure_connection_configs_table(engine)
_ensure_filter_source_enum_values(engine)
_ensure_dataset_review_session_columns(engine)
# [/DEF:init_db:Function]