Session 55: End-to-end UI test pass + csv_upload path fix
Engineering log for session 55.
Live exploration session walking the platform as a first-time user: workspace → project → CSV upload → EDA → (planned) experiment → promote → deploy → downstream features (monitoring, drift, schedules, webhooks, approvals, lineage, LLM). Bugs found while clicking through are tagged below.
FIXED — CSV upload stored a relative path, breaking refresh + future loaders#
FIXED—data_sources.py:175(upload_csv) now storesstr(target.resolve())instead ofstr(target). The CSV upload writes tosettings.artifact_dir / "data-sources" / "<uuid>.csv". With the defaultartifact_dir = Path("./artifacts"), the unresolved string was the relative"artifacts\data-sources\<uuid>.csv". On the refresh path (POST /data-sources/{id}/refreshinconnections.py:394),csv_driver._read_bytescallsLocalFsObjectStore.get_bytes(path). The store's_path_for_uridoesn't recognise the relative form as afile://URI or absolute path, so it falls through to_path_for(key)which joins againstartifact_root→<artifact_root>/artifacts/data-sources/<uuid>.csv→ file not found → driver raises → handler returns 400. Symptom in the UI: "Refresh failed: Request failed with status code 400" on the dataset Versions tab. Fix is one-line; behaviour for new uploads is to store the resolved absolute path, which_path_for_uriaccepts via itsuri[1] == ":"Windows-absolute branch.FIXED— One-shot migration for existing rows: anydata_sources.config.pathforkind='csv_upload'that is a relative string was rewritten to its absolute form by resolving againstartifact_dir.parent(the BE CWD). Done locally on the dev DB; for prod we'll need a proper Alembic data migration if any wheels with the buggy upload code shipped — none have, this only affects dev DBs.
REMOVED — Pipelines navbar entry + page (folded into Model Registry)#
REMOVED—apps/web/src/pages/Pipelines.tsx+apps/web/src/pages/PipelineDetail.tsxdeleted. The "Pipelines" entry is gone from the navbar; the/workspaces/:wsId/pipelines+/workspaces/:wsId/pipelines/:pipelineIdroutes are removed fromApp.tsx. ThePipelineDB table stays — it's still the artifact pointer the predict path uses — but the UI for browsing it has folded into the Model Registry surface (since session-56's unified promote, every Pipeline has a matching RegisteredModelVersion).ADDED—apps/web/src/pages/RegisteredModelDetail.tsxnow carries the Deploy action: a per-version Deploy button opens a Dialog that callsregistryApi.deploy(modelId, versionId, {endpoint_slug, auth_mode}). The version row shows existing endpoint slugs inline as/slugchips that link to the Deployment detail page. The model-detail page is now the canonical "look at an artifact, deploy it, see what's serving it" surface.CHANGED—services/api/pycaret_server/api/deployments.py::_serialise_pipelinenow back-fillsregistered_model_id+registered_model_version_idby walkingPipeline → Trial → RegisteredModelVersion. ThePipelineTS type gains both fields. Pre-session-56 Pipelines (no matching registry row) return null — which is the correct historical fact and gates the "Open in registry →" link in the UI accordingly.CHANGED— Sweep of UI references to the deleted pages:TrialDetail.tsx— "Open pipeline →" became "Open in registry →" (gated onregistered_model_id).TrialsCard.tsx— promoted-trial "view →" link now points at/workspaces/:wsId/models(registry list).RunDetail.tsx::PromotedPipelinesSection— section renamed to "Promoted versions"; rows link to/workspaces/:wsId/models/:registered_model_id(or the workspace list as fallback). Empty-state copy reframed around the Model registry.DeploymentDetail.tsx— "· pipeline {name}" → "· model {name}" with link into the registry.Deployments.tsxempty state — link rerouted from/pipelinesto/models.WorkspaceDetail.tsxKPI tile relabeled "Pipelines" → "Model versions" with link to/models.WorkspaceHome.tsxKPI tile + quick-action card same.CommandPalette.tsx— collapsed two entries ("Pipelines registry" + "Model registry") into one "Model registry" command with the pipeline keyword preserved for discoverability.TrialCompare.tsxsection title "Pipelines" → "Pipeline structure" (it shows the sklearn pipeline DAG per trial, not our Pipeline table — disambiguation only).
CHANGED — Single unified promote (Pipeline + Model Registry version, atomic)#
CHANGED—services/api/pycaret_server/api/runs.py::promote_trialis now the single source of truth for promotion. One atomic transaction writes the Pipeline row (legacy artifact pointer used bypredict/rollback) AND a RegisteredModelVersion off the same Trial bytes. The RegisteredModel is found-or-created by(workspace_id, name), so the first promote of a name creates the registry entry; subsequent promotes bump bothPipeline.versionandRegisteredModelVersion.versionin lock-step. The response now includesregistered_model_id+registered_model_version_idso the UI can deep-link without a follow-up call.CHANGED—unpromote_trialsymmetrically deletes both the Pipeline AND the matching RegisteredModelVersion. Returns 409 if either has an active Deployment. The parent RegisteredModel is preserved (other versions may still exist; the user can delete the empty container explicitly).CHANGED—services/api/pycaret_server/api/deployments.py::create_deployment(pipeline-based) now back-fillsregistered_model_id+registered_model_version_idon the new Deployment by walkingPipeline → Trial → RegisteredModelVersion. Deployments created from pre-unified-promote Pipelines stay registry-less (no governance row existed to point at) — which is the correct historical fact.REMOVED—POST /api/v1/registered-models/{model_id}/versions(registry.py::create_version). It was always-broken (readRun.stored_path / sha256 / params / metrics, which live onTrialinstead — wouldAttributeErroron the first non-empty trial) and had zero test coverage. Removal is forward-only; no migration needed.REMOVED—apps/web/src/components/RegistryPromoteDialog.tsxfile deleted. The "Promote to registry" button on the Trial Detail page is gone; the legacy "Promote (legacy)" button is renamed to plain "Promote". Single button, single mental model.REMOVED—registryApi.promotecall inapps/web/src/api/endpoints.ts(the FE wrapper around the deleted backend endpoint).TESTS—test_session25.py::test_trial_promote_creates_pipelineextended to assert the unified write: response carries bothregistered_model_id+registered_model_version_id, the workspace Registry list includes the new model, and a second promote under the same name produces a v2 attached to the SAME RegisteredModel. All 13 promote-related tests acrosstest_session25.py / test_session32_revert_phase0.py / test_phase9_finish.pypass green.DOCS—docs/revamp/DECISIONS.mdADR added (see entry below). The maintainer (Moez, 2026-05-15) explicitly approved the consolidation; the Phase-7 split was leaky from inception (broken handler, no tests, parallel-table UX).
ADDED — Interactive dataset-explore modal (click the row body)#
ADDED—apps/web/src/components/DatasetExploreModal.tsx— full-width Dialog (size="full") opened when a user clicks the body of a dataset row inDataSourcesSection. SingleGET /data-sources/{id}/profile?sample_rows=50call drives the whole experience — no per-column round trips, no loading flicker between selections.- Layout (viewport-bounded, both panes scroll internally — fixed
h-[78vh]body so the modal never overflows the laptop screen): top region holds the dataset-health pill strip (rows · cols · memory · duplicates · missing% with red/amber tones at thresholds) + a heuristic-chip strip (clickable → jumps to the column). 7/5 grid below: sample-rows table on the left, tabbed pane on the right. - Right-pane tabs: Columns (filterable picker + selected column detail), Overview (dtype-distribution bars, top-N most-missing columns ranked, full warning list with severity), Correlations (top-10 strongest pairs + diverging-colour SVG mini-heatmap with cell tooltips). Tab counts: column count on Columns, warning count badge on Overview, disabled state on Correlations when no numeric columns exist.
- Column picker rows carry a dtype-kind badge, the column name, an inline 14-bar sparkline for numeric columns (a peek at distribution shape without a click), and a missing-% bar.
- Per-column detail renders a stats grid (unique / cardinality / missing / dtype + numeric extras: min/max/mean/median/std/Q1/Q3/skew/kurt/zeros%/IQR-outliers), a pure-CSS histogram (numeric) or weighted top-values bars (categorical/text/boolean), and a "Top correlated" strip with diverging negative-on-the-left bars for numeric columns. Selecting a column highlights the matching cells column-wise in the sample table.
- "Looks-like" hints — informational badges on the column header inferring role:
target?(low-cardinality numeric/boolean),id(id-like),drop?(constant),leak?(high-cardinality categorical close to row count),sparse(>50% missing). Not consumed by the engine — surfaced for the human before they configure an experiment.
- Layout (viewport-bounded, both panes scroll internally — fixed
ADDED—apps/web/src/components/Dialog.tsx— newsize="xl"(max-w-5xl) andsize="full"(max-w-7xl) options, used by the explore modal.CHANGED—apps/web/src/components/DataSourcesSection.tsx— the row body (icon + name + size summary) is now a button that opens the explore modal. The existing Explore (page) / Versions / AI / Delete action buttons are unaffected.
CHANGED — LLM provider settings page: clear stored-state UI#
CHANGED—apps/web/src/pages/LLMSettings.tsxrewritten to make stored-vs-not-stored state obvious. Whenhas_api_key === true: emerald "✓ API key on file" status card at the top with provider, model, relative saved-at timestamp + Test connection / Rotate key / Clear buttons. The password input is hidden behind a lock icon ("API key hidden — use Rotate key above to replace") so users no longer face an empty password field with a "keep existing (leave blank)" placeholder they have to mentally parse. Whenhas_api_key === false: amber "No API key on file — LLM features will fail" banner above the form; password input is required. Clear button calls the newDELETE /workspaces/{id}/llm/settingsendpoint after a confirm prompt.CHANGED— Default Anthropic model inLLMSettings.tsxPROVIDERSarray bumped fromclaude-sonnet-4-5(stale) toclaude-sonnet-4-6. Same bump applied to the model-name input placeholder and the initialmodelNamestate.
ADDED — DELETE endpoint for LLM provider settings#
ADDED—DELETE /workspaces/{workspace_id}/llm/settingsinservices/api/pycaret_server/api/llm.py. Admin-gated. Idempotent (204 even if nothing was stored). Deletes everyLLMProviderSettingrow for the workspace (handles the rare case where a previous provider's row was disabled but not removed). Wired into the frontend asllmApi.deleteSettingsinapps/web/src/api/endpoints.ts.
ADDED — Workspace-level Datasets navbar entry + page#
ADDED—apps/web/src/pages/AllDatasets.tsx+ new route/workspaces/:wsId/datasets(apps/web/src/App.tsx). Workspace-level index of all DataSources, mirrors the data model (DataSources are workspace-scoped, not project-scoped — see DOCS entry below). Thin wrapper over the existingDataSourcesSectioncomponent so behaviour stays in lockstep with the project-page surface.ADDED— Datasets nav link inapps/web/src/components/Layout.tsx, placed between Projects and Pipelines with a database-cylinder icon. The Project-page "Data sources" section still works exactly as before — the new top-level entry is an additional, more-prominent surface.
FIXED — Lineage writes now log at WARNING instead of silently swallowing#
FIXED—services/api/pycaret_server/api/connections.py:record_lineagenow logs the failed edge (workspace_id, kind/id pair, relation, error type+message) at WARNING when an insert raises, instead ofexcept Exception: db.rollback()with no breadcrumb. Behaviour is unchanged (still best-effort, never propagates) — but the previous "lineage graph silently goes stale" failure mode flagged in the session-55 audit is now observable inservices/api/logs.log. Addedloggingimport +_logmodule logger.
ADDED — Display-only banner on Model Library#
ADDED—apps/web/src/components/ModelLibrarySection.tsxnow renders a prominent amber banner above the controls:v1 — display only. Toggles below are saved to the catalog but the engine does not consult them yet. The prior treatment was a single muted line inside the description text that users had no reason to read. Engine-side enforcement is now tracked as an explicit ROADMAP V2 bullet.
DOCS — ROADMAP V2: worker heartbeats + engine-side Model Library enforcement#
DOCS—docs/revamp/ROADMAP.mdV2 section gained two explicit bullets, both surfaced by the session-55 build-status audit:- Worker heartbeats — replace the "derive workers from
Job.locked_by" hack with a realworker_heartbeatstable. Currently idle workers vanish from the queue admin and a dead worker is indistinguishable from a healthy idle one. - Engine-side Model Library enforcement — wire
compare_modelsto skipenabled=Falserows from the workspace'sModelLibrary. Today the UI toggle does nothing; the new banner above calls this out, but the fix is V2.
- Worker heartbeats — replace the "derive workers from
DOCS — Clarified workspace-vs-project ownership of DataSources#
DOCS—DataSourcebelongs to the workspace, not the project. Confirmed againstdb/models.py:161("Registered CSV/S3/Postgres source a Project can point at") anddata_sources.pyroute surface (all upload/register routes are workspace-scoped). The "upload dataset" modal on project pages is a UX convenience — it still callsPOST /workspaces/{id}/data-sources/upload. Projects/experiments reference DataSources; they don't own them. No code change needed; flagged for the eventual user docs so the modal placement doesn't mislead.
Sessions 20–54 — 2026-04-24 → 2026-05-08 — V2 platform + UI surfacing + design refresh#
Consolidated catch-up entry covering the arc from the failure-debugger / API-key landing in session 19 through the engine MVP-1 finish (sessions 22-46), the V2 platform features (sessions 22-24, server-side; tagged on top of MVP-1 numbering), and the front-end surface that exposes them (UI sessions through s54). Written retroactively from the shipped code; individual sessions weren't entered as their own headers during the build, but each block below maps to a concrete roadmap milestone.
Baseline: session 19 left the platform with 4-of-6 LLM copilots, API keys + workspace members + audit logs not yet wired, and the engine god-class still partially undrained. Engine 4.0.0a2 was the last published wheel.
Theme: graduate every "stub" to "real implementation" across all three layers. Engine: revive every NotImplementedError verb. Server: deliver V2 control-plane features (drift monitor, scheduled retraining, deployment versioning, webhooks, AutoML pipeline search, encrypted secrets, backup/restore, expanded roles). Web: surface every new endpoint + a full design-system unification pass.
ADDED — Engine: revive the six legacy-only verbs (sessions 53–54)#
ADDED—Experiment.get_leaderboard()returns a defensive copy of the most recentcompare_modelsleaderboard. Snapshotted into_fit_state["last_leaderboard"]after every supervised + time-seriescompare_modelscall. RaisesRuntimeErrorif no compare has run.ADDED—Experiment.automl()— convenience wrapper forcompare_models(n_select=1)→tune_model. Returns a fittedsklearn.pipeline.Pipeline. Pass-throughs foroptimize/n_iter/turbo/include/exclude/fold/fit_kwargs/round/verbose.ADDED—Experiment.interpret_model()— wrapsshap.Explainerover a pipeline's preprocessor + final estimator. Returns ashap.Explanation.plot=argument renders'summary'/'beeswarm'/'bar'/'waterfall'inline. SHAP is an opt-in extra:pip install pycaret[interpret].ADDED—TimeSeriesExperiment.check_stats()— runs Summary / Ljung-Box (white noise) / ADF + KPSS (stationarity) / Shapiro-Wilk (normality) over the experiment's series. Returns a 6-columnDataFrame.test=filter andsplit='all'|'train'|'test'.ADDED—Experiment.plot_model(estimator, plot=None, save=False, **kw)— looks upplotin a per-task registry and returns aplotly.graph_objects.Figure.save=Truewritesf"{plot}.png"(requirespycaret[export]forkaleido);save="my.png"writes to that path.ADDED—Experiment.evaluate_model(estimator)— returns adict[str, Figure]of the curated diagnostic bundle for the task. Plots that fail (e.g. shap missing, estimator lackingfeature_importances_) are silently dropped rather than raising.ADDED— Per-task plot registries on every leaf class. Classification (16 kinds), Regression (12), Clustering (6), Anomaly (4), Time-series (8). Default plots:auc / residuals / cluster / score / forecast.ADDED—[interpret] = ["shap>=0.46"]optional extra. Folded into[full].ADDED— 22 new tests intest_session53_revive_legacy_verbs.py+test_session54_plot_model_dispatcher.py.
REMOVED — Engine: drop transitional core deps (sessions 47-50 area)#
REMOVED, BREAKING—imbalanced-learnfrom core deps. The legacyPipelineclass that subclassedimblearn.pipeline.Pipelineis also gone — every native verb has been operating onsklearn.pipeline.Pipelinesince session 24.fix_imbalanceusers now installimbalanced-learnthemselves.REMOVED, BREAKING—category-encodersfrom core deps. The 4.0 native preprocessor uses sklearn-native encoders (OneHotEncoder/OrdinalEncoder/TargetEncoder).REMOVED— Deletedpackages/engine/pycaret/internal/preprocess/(the entire legacy preprocessor: preprocessor.py, transformers.py, iterative_imputer.py, target/, time_series/forecasting/preprocessor.py).REMOVED— Deletedpackages/engine/pycaret/internal/pipeline.py(the legacyPipelineandTimeSeriesPipelineclasses).REMOVED— Deletedpackages/engine/pycaret/internal/patches/sklearn.py(the only file that importedinternal.pipeline).CHANGED—packages/engine/pycaret/utils/_show_versions.py— pruned the dep list from a 35-name 3.x relic down to a clean 10-required + 9-optional view that matches the actual ship surface.
ADDED — Engine: published 4.0.0a3 → a8 to PyPI#
BUILD—4.0.0a3(first PyPI release of the 4.0 line).4.0.0a1and4.0.0a2had only ever been distributed via GitHub release downloads.BUILD—4.0.0a4— relaxedscikit-learn>=1.7→>=1.5andimbalanced-learn>=0.13→>=0.12after thea3install triggered Colab kernel restart-loops by force-upgrading the preinstalled scientific stack.BUILD—4.0.0a5— cappedipython>=8.18,<9aftera4pulled IPython 9.x which removedIPython.utils.coloransi.TermColors.Greenthatgoogle.colab._shell_customizationsreads at kernel startup.BUILD—4.0.0a6— revivedget_leaderboard/automl/interpret_model/check_stats. Added[interpret]extra. Fixed__version__hardcoded to4.0.0a2inpycaret/__init__.py.BUILD—4.0.0a7— revivedplot_model/evaluate_model(Plotly dispatchers wired into per-task plot registries).BUILD—4.0.0a8— droppedimbalanced-learn+category-encodersfrom core deps. Core surface lands at 10 deps (numpy, pandas, scipy, scikit-learn, joblib, plotly, tqdm, requests, jinja2, ipython).
ADDED — Server: control-plane V2 features#
ADDED— Secrets encryption (services/api/pycaret_server/crypto.py). Fernet-backed at-rest encryption for LLM API keys + future cloud creds. Stored ciphertext is prefixed withENC:v1:so reads can transparently fall back to plaintext for rows written before this module existed. Key rides in env varPYCARET_SECRETS_KEY; missing key in dev synthesises an ephemeral per-process key with a loud warning. Addedcryptography>=43to backend core deps.ADDED—PredictionLogtable (db/models.py). Append-only log of every served prediction. Drift detection, latency forensics, audit. The/deployments/{slug}/predicthandler writes one row per call (ok or error path). Sample of input/output rows capped at 50 per log entry. NewGET /api/v1/deployments/{id}/prediction-logsendpoint paginates withstatus_filter.ADDED—Trialtable + auto-persistence. Promotes the JSONRun.leaderboardrows into queryable entities so the UI can sort, filter, link to fitted pipelines. Written byRunOrchestrator._transitionwhenever leaderboard JSON lands. NewGET /api/v1/runs/{id}/trialsendpoint.ADDED—ModelLibrarytable + lazy-seed CRUD. Workspace-scoped, editable mirror of the engine's model registry. Lazy-seeded frompycaret.api.list_modelson first read of a (workspace, task) pair. v1 enforcement is informational; engine-side filtering ships in V2.ADDED— Admin user routes (/api/v1/admin/usersGET + PATCH). Superuser-only. Togglesis_superuser/is_activewith last-superuser + self-deactivation guards.ADDED—ScheduledJobtable +services/api/pycaret_server/scheduler.py(APScheduler in-process). Two job kinds:drift_monitor(snapshots prediction-log distributions vs. baseline; writes aDriftReportrow) andretrain(re-runs a configured experiment viadispatch_run). New/workspaces/{id}/schedulesCRUD +POST /schedules/{id}/run-nowfor immediate execution.ADDED—drift_monitor.py— PSI for numeric features, chi² (Cramer's V) for categorical, mean as aggregate. Auto-writesDriftReportrow + firesdrift.alertwebhook onmoderate/severescores.ADDED—WebhookSubscriptiontable + delivery infrastructure (webhooks.py). HMAC-SHA256-signed POSTs fired on platform events:run.{succeeded,failed,cancelled},deployment.{created,deleted,rollback},drift.alert,schedule.failed. Secrets stored encrypted via the new crypto module. Best-effort fire-and-forget on a daemon thread so a slow webhook target can't pin a user request. New/workspaces/{id}/webhooksCRUD +POST /webhooks/{id}/testfor synthetic ping.ADDED—ExperimentTemplatetable + CRUD. Saved (task, setup_params, plan_params) bundles users can pre-fill the New Experiment screen from. New/workspaces/{id}/experiment-templatesCRUD.ADDED— Pipeline versioning (deployments.py). Pipelines that share(workspace_id, name)are revisions of the same logical model and share afamily_id. Promote bumpsversion. NewGET /pipelines/{id}/versions. NewPOST /deployments/{id}/rollbackrepoints the deployment at any earlier pipeline in the same family; in-memory registry is evicted so the next/predictreloads.ADDED—plan="search"(runs/plans.py). AutoML pipeline search: orchestrator iterates a list of preprocessing variants (each asetup_paramsoverride merged on top of the experiment defaults), runscompare_modelsper variant, concatenates leaderboards into a singleVariant-tagged DataFrame, returns the globally-best fitted pipeline.ADDED— Backup/restore (api/backup.py). Superuser-only.GET /admin/backupstreams a tarball containingdatabase.json(every row from every table) + raw artifacts under the artifact dir.POST /admin/restore(multipart) wipes + reloads from the tarball with aconfirm=trueguard.ADDED— Expanded user-role lattice (Spec § 17.2).WorkspaceMember.rolewidened from{admin, member}to 7 roles:owner / admin / project_admin / ml_engineer / data_scientist / viewer / service_accountplus the legacymember.ADMIN_ROLESset governs admin gating + the last-admin guard.ADDED—pycaret-clientSDK (packages/sdk-python/). Python HTTP client for the Control Plane. Hand-written namespaced façades (cp.workspaces.list(),cp.runs.submit(...),cp.deployments.predict(...), etc.). To be published aspycaret-clienton PyPI.ADDED—runs/dispatch.py:dispatch_runextracted from the route handler so the scheduler (and any future programmatic enqueue path) can submit a Run without going through HTTP.ADDED— Single Alembic migrationb2c3d4e5f6a7coveringscheduled_jobs+webhook_subscriptions+experiment_templates+pipelines.family_id+pipelines.version. Plus priora1b2c3d4e5f6forprediction_logs+trials+model_library.
FIXED — Server bootstrap auto-migrate#
FIXED—db/bootstrap.py— pre-Alembic legacy DBs were being stamped atheadeven when their actual schema was older, so newer migrations never landed andOperationalError: no such table: trialswould appear at first prediction. Fix: detect the schema state by walking distinguishing tables (newest fingerprint first), stamp at the matched revision, thenupgrade headif behind. When already at head, stamp head (no upgrade) — this also avoids polluting Alembic's process-global ScriptDirectory cache that breaks tests bootstrapping many DBs.CHANGED—db/bootstrap.py— whenalembic_versionis already present in dev, also runupgrade headso newer migrations land between server boots without operator action.
ADDED — Web: surface every new endpoint#
ADDED— Schedules screen (/workspaces/:wsId/schedules) +<NewScheduleForm>. Lists active drift-monitor + retrain schedules with last-run state. Toggle / run-now / delete inline.ADDED— Experiment templates screen (/workspaces/:wsId/templates). CRUD with a JSONsetup_paramseditor. Templates surface as a "Start from template" picker on the New Experiment screen.ADDED— Webhooks screen (/workspaces/:wsId/webhooks) +<NewWebhookForm>. Event-type checkboxes, secret input, test-fire button.ADDED—<TrialsCard>on RunDetail. Resolvesmodel_id→ full model name viadescribeApi.models(task)so rows show "Logistic Regression / Random Forest" instead oflr / rf. Two view modes: Table (sortable columns + inline progress bar on the primary metric) and Chart (Plotly horizontal bar chart with metric picker; "higher is better" / "lower is better" caption auto-flips per metric; best-row tinted green).ADDED—<PredictionLogsCard>on DeploymentDetail. Status filter, auto-refresh, latency in tabular-nums, truncated request-id.ADDED—<DeploymentVersionsCard>on DeploymentDetail. Lists every Pipeline in the family with rollback button per row; current version pilled green.ADDED—<RunRunningCard>— animated "in flight" card that replaces the empty leaderboard while a run is queued / running. Pulsing accent dot, current stage from the latest event, live elapsed-time counter, skeleton bars where the leaderboard will land. Polls/runs/{id}/eventsevery 1.5 s.ADDED— Admin screens —/admin/users(superuser-only platform user mgmt withis_superuser+is_activetoggles),/workspaces/:wsId/admin(workspace admin hub: members + LLM + audit cards + ModelLibrarySection inline),/workspaces/:wsId/admin/integrations(LLM / webhooks / object-storage / SSO cards).ADDED—<BackupRestoreCard>on AdminUsers. Download tarball / upload + confirm restore.ADDED—<AIAdvisorWidget>floating button + side panel (mounted in Layout). Shows the workspace's recent LLM consultations + provider status + quick-jump links. "Connect a provider" prompt when none configured.ADDED— API client methods in endpoints.ts:pipelinesApi.versions,deploymentsApi.rollback / predictionLogs, fullschedulesApi,templatesApi,webhooksApi,modelLibraryApi,adminApi,runsApi.trials.ADDED— AutoML search plan option on NewExperiment (4th plan kind). Submits with default preprocessing variants[{}, {normalize:true}, {normalize:true, transformation:true}].ADDED— Sidebar navigation entries — Schedules / Templates / Webhooks (each with a custom 16px lucide-style SVG icon — Clock, Template, Hook). Workspace-scoped Admin link below LLM. Superuser-only "Platform users" entry under Account.ADDED—workspace_id+project_idon the Run response so/runs/:iddeep-links can resolve the active workspace for the sidebar.Layout.tsxreadsrunForCtx.data?.workspace_idas a fallback when the URL doesn't carry a/workspaces/:wsId/...segment.ADDED— Run-context fields propagated into api/types.ts +Pipeline.family_id+Pipeline.version+RunPlanwidened to include'search'+WorkspaceRolewidened to the 7-role lattice +ADMIN_WORKSPACE_ROLESexported set.
CHANGED — Web: design-system unification#
CHANGED— WorkspaceHome dashboard rebuild — replaced inlinestyle={{...}}everywhere with design tokens. KPI strip usescard-tight+ tabular-nums. Recent-runs row usespill-success / pill-accent / pill-warn / pill-danger / pill-neutral(no more bespoke colored badges). Shortcuts panel rebuilt to the modern Linear/Vercel/GitHub pattern:icon + label + small description + chevron-on-hover,hover:bg-ink-50rows,text-ink-400 → text-ink-700icon transition. 10 lucide-style 16px SVG icons inline.CHANGED— ProjectDetail full rewrite — clean breadcrumb,h-pageheader, tags askbd, primary "New experiment" CTA. Drops<DataSourcesSection>inline so users upload CSVs in project context. Experiments list rendered as a tight rows-with-divider list (matches the data-sources list visually) instead of a stack of cards. Empty-state with beaker icon + first-experiment CTA.CHANGED, BREAKING— ExperimentDetail full rewrite — replaced the permanent right-sidebar "New run" form with a header CTA button that opens a<Dialog>. New-run defaulting fixed: defaults to whatever data source the experiment's most recent run used (was hard-coded tosklearn:iris). Sklearn samples now only appear in the dropdown when zero CSVs are registered (true demo case) or when a prior run actually used one. Runs table resolvesdata_source_idUUIDs to friendly CSV names + uses pill primitives for status. Plan dropdown gains thesearchoption.CHANGED— RunDetail polish — full run UUID in breadcrumb (mono, no truncation). Status badge in header is now apill-*class. Removed duplicate UUID under the title. Request snapshot redesigned as a 3-coldlwithdivide-yrows. Dropped the standalone<Leaderboard>section — TrialsCard is the single canonical view. "Event stream" → "Event log" with a one-line description; status pill instead of bullet-prefix text; empty-state copy is context-aware ("No events were emitted by this run." when terminal vs. "Waiting for events…" while connecting).CHANGED— Schedules / Templates / Webhooks / Admin pages design unification. All useh-page/h-section,space-y-8, canonical table pattern (bg-white text-ink-500thead,px-4 py-2 text-left font-medium,border-t hover:bg-ink-50rows),pill-*for status,rounded-xl border-dashed border-ink-300 p-8 text-centerempty states.CHANGED—<AIAdvisorWidget>— was a circular candy-teal floating bubble; now a small rounded-full pill with the spark icon + "AI" label that flips tobg-ink-900 text-whitewhen open.CHANGED—<Leaderboard>— canonical white-thead pattern, model column bolded, sort chevron in accent.CHANGED—<EventStream>— header usesh-section, status pill instead of● opentext, context-aware empty state.
TESTS#
TESTS— 265 → 330 engine tests passing (1 skip on[interpret]extra). New:test_session53_revive_legacy_verbs.py(11 tests) +test_session54_plot_model_dispatcher.py(19 tests). Sessions 47-52 plot tests now run as part of the engine suite (previously excluded).TESTS— 115 backend tests passing. New:test_session22.py(9 tests — encryption + prediction logs + trials),test_session23.py(8 tests — model library + admin),test_session24.py(9 tests — schedules + templates + webhooks + pipeline versioning + search plan).TESTS— 59 UI tests passing. UpdatedEventStream.test.tsxto match the new pill-based status indicator (replaced the● closedregex match).
DEPS#
DEPS, BREAKING— Engine core deps: 12 → 10. Droppedimbalanced-learn+category-encoders.DEPS— Engine core: cappedipython>=8.18,<9to coexist with Colab's preinstalledgoogle.colabextension which depends on the IPython 8.xcoloransi.TermColorsAPI.DEPS— Engine core: relaxedscikit-learn>=1.7→>=1.5+imbalanced-learn>=0.13→>=0.12(before the dep was dropped) to avoid forcing upgrades on Colab/Kaggle preinstalled images.DEPS— New engine optional extra[interpret] = ["shap>=0.46"]— folded into[full].DEPS— Server core: addedcryptography>=43(was previously only in[mysql]extra) for Fernet at-rest secret encryption.DEPS— Server core: addedapscheduler>=3.10for the in-process scheduler.