# Changelog Operator- and user-facing changes. Internal refactoring, test additions and documentation-only commits are not listed unless they change what an operator sees or does. ## Unreleased ### Release qualification - Candidate version `1.3.0-rc.1` reports the `rc` channel rather than stable. - The P15A path binds the exact canonical source identity, OCI labels and filesystem layer sets into a retained image bundle before isolated RC qualification. - This is not a release announcement: no RC tag, Gitea prerelease or production promotion is authorized until the complete qualification matrix is verified. ### Product workflow - Added the project-first model-management flow: persisted project bindings, human-readable model and update context, guided onboarding, selected current/candidate comparison, impact and migration/reindex boundaries, and explicit lifecycle verification/rollback. Projects continue to bind to stable capabilities rather than model paths or concrete model names. - Selected comparisons now retain immutable embedding-space dimensions and exact gateway-bound current/candidate evidence; approved route replacement and rollback are fenced by generation and CAS checks. ### Fixed - The v1.2.1 upgrade path now detects when `modelforge` is PostgreSQL's protected bootstrap superuser (OID 10) before mutating any role or privilege. Such installations are routed to a documented fresh-cluster logical-restore path; direct system-catalog edits remain unsupported. - Compose now passes the retained-admin variable names required by the bounded existing-cluster provisioning script, while continuing to keep administrator and owner credentials out of the API and migration processes. ### Operations - Recorded the completed v1.2.2 production rollout, exact deployed image identities, role-split cluster migration, verified recovery point, measured downtime and final production-worker LAB probe. ## v1.2.2 — 2026-09-01 ### Security - Runtime Worker packaging now upgrades the digest-pinned Ubuntu base before installation and pins the inference stack plus inherited Python security dependencies to reviewed fixed versions. The public-candidate gate scans the resulting fourth image instead of treating the private worker as an unexamined exception. - The Runtime Worker removes unneeded kernel-development headers after installation, eliminating their upstream-unfixed vulnerability surface without removing required runtime libraries. - Public candidate scans distinguish fixable or newly observed HIGH/CRITICAL findings from an exact reviewed upstream-unfixed baseline. A changed CVE, package, installed version, severity or newly available fix fails closed. - Node enrollment and publisher credentials now require their exact fixed scopes at both service and database boundaries. Schema `20260830_0023` validates every existing row before adding the constraints and refuses the upgrade when malformed legacy scope data exists. - Request-body streaming now has finite ASGI event/progress budgets in addition to byte limits; progressless frame floods receive the typed `request_body_progress_exhausted` response. - PostgreSQL role provisioning now removes the database's default public temporary-table grant; production startup continues to attest that the runtime role cannot create or use it. ### Fixed - Fresh schema `20260830_0024` installs no longer let Psycopg misread PostgreSQL `%rowtype` and `%I` syntax as client placeholders. - Public-candidate acceptance now works across the Gitea sibling Docker daemon while retaining isolated volumes, private workers, exact HTTP contract probes and deterministic cleanup. ## v1.2.1 — 2026-08-30 A packaging and provenance patch. No feature, API, schema or runtime-behaviour change; schema stays `20260828_0022` and no migration runs. It exists because v1.2.0's production acceptance found two defects that every existing gate was blind to — both in the artifacts rather than the source. ### Fixed - **The published console could not reach its own API.** Vite inlines `VITE_API_BASE_URL` at build time, but the release build never passed it, so every release image since v1.1.0 compiled the Dockerfile's development default `http://localhost:8000` into an immutable bundle — and the nginx CSP, derived from the same argument, hardcoded the same wrong origin. A release build now requires an explicit `--public-api-origin` (or `MODELFORGE_PUBLIC_API_ORIGIN`) and refuses to package without one. - **The Node Agent's identity could drift from its tag.** `docker-compose.node-agent.yml` declared a build with no arguments, so a Compose-built agent carried version `0.0.0` and empty revision/created labels while still being tagged from `MODELFORGE_VERSION`. Production ran an image tagged `1.1.1` whose contents were `1.2.0`. The projection now passes the release identity, as the API and console projections already did. ### Added - `scripts/release_image_acceptance.py`: a gate that inspects the **built** console image — its compiled bundle, its rendered CSP and its OCI labels — rather than the source that produced it. The release build runs it before packaging and refuses to publish an image that cannot reach the API it was built for. Verified against the exact image that broke production. - Regression coverage for both defects: the release build fails closed without an origin, the origin reaches the console build, the CSP names the same origin and is never widened to a wildcard, every release image declares complete OCI labels, and every Compose projection that builds a published image passes the release identity. ### Operations - The release manifest now records the console's compiled-in API origin, so an operator can see it without unpacking the image. - Local development is unchanged: `docker compose up` still defaults to `http://localhost:8000`. Fail-closed applies to the release path only. ## v1.2.0 — 2026-08-29 The Premium Operator Console release. No API contract, domain model, runtime behavior or database schema changed; schema stays `20260828_0022` and no migration runs. ### Added - A production command center built only from backend-authoritative evidence, stating unknowns as unknown rather than as zero. - Quick jump (Ctrl+K): a modal command palette over all 13 workspaces, matching name, domain and keywords. - An evidence timeline of recent control-plane activity, each entry linking to the workspace where it can be verified. - A split-pane Registry detail view covering upstream facts, local governance, revisions, artifacts and provenance. - One console-wide guarded destructive-action dialog showing identity, dependency posture and permanent impact before an explicit acknowledgement unlocks the confirm control. ### Changed - Workspaces are grouped by operator mental model (Command, Model supply, Serving, Infrastructure, Assurance, Projects) while every existing route hash is preserved. - One refined design token layer for light and dark, with semantic status roles that always include text and explicit focus rings. - Every route is usable from 1440 px down to a 390 px mobile viewport through a modal navigation drawer, with no horizontal overflow at any tested width. ### Fixed - The ARIA tabs pattern is complete across all six tabbed workspaces: every tab carries a stable `id` and `aria-controls`, and every panel is a real `role="tabpanel"` naming its tab back. - Lifecycle, Migrations and Recovery tablists gained roving `tabindex`, an accessible tablist name and Left/Right/Home/End keyboard movement, which they previously lacked entirely. - Capability residency unload no longer uses a native `window.confirm`; it uses the console safety dialog and shows the VRAM released and the cold-load cost the next request will pay. - Operator token fields in Operations, Recovery, Lifecycle and Migrations now sit in a real form with a stable `id`, a `name` and an explicit `