Changelog & Versioning
How ObjectOS is versioned, what changes between releases, and what's supported.
Versioning policy
ObjectOS follows Semantic Versioning: MAJOR.MINOR.PATCH.
| Version bump | What it means | What to do |
|---|---|---|
Patch (14.7.0 → 14.7.1) | Bug fixes, no behavior change | Roll forward, no app changes needed |
Minor (14.6 → 14.7) | New features, backward-compatible | Roll forward, optionally adopt new features |
Major (13 → 14) | Breaking changes documented in release notes | Read the migration guide before upgrading |
All @objectstack/* packages release together with a synchronized
version number — they're tested as a matrix, not independently.
Compatibility matrix
| Component | Compatibility rule |
|---|---|
| ObjectOS image ↔ compiled artifact | Same minor version. A 14.7.x image runs a 14.7.x artifact; a 14.7 artifact may use features unavailable in a 14.0 image. The protocol handshake (PROTOCOL_VERSION, 12.0+) rejects incompatible packages at install time. |
| ObjectOS ↔ CLI | Same minor version recommended. The CLI in npm i -g writes scaffolds pinned to its own version. |
| ObjectOS ↔ database driver | Driver pinned by image build; verify Postgres ≥ 13 / MongoDB ≥ 5 / Turso (any current). |
| Node.js | 22 or newer — engines.node is >=22.0.0 across every published @objectstack/* package since 17.0. Node 20 reached end-of-life on 2026-04-30. |
Support windows
Support is defined relative to the current major, not against a fixed calendar:
| Branch | Status |
|---|---|
| Current major (17.x today) | Active development; new features and fixes |
| Previous major (16.x today) | Security fixes only |
| Older majors | Unsupported |
Critical security fixes are backported to the current and previous
major. Everything else lands on main. Dates attached to a specific
major are published with that major's
release notes.
Release notes
Released ObjectOS versions and their CHANGELOG entries are published at:
- npm:
@objectstack/runtime - GitHub: github.com/objectstack-ai/objectos/releases
- Source CHANGELOG:
CHANGELOG.md - Long-form release notes:
RELEASE_NOTES.md
Subscribe to releases on GitHub to get notified.
Recent highlights
17.1
@objectstack 17.1.0 (released 2026-08-20) is the current release of
the v17 line. Where 17.0 made the authorable surface loud, 17.1 makes
the runtime's answers loud: a flag that promised to revoke access now
revokes it, a read that failed stops being served as an empty one, a flow
that never dispatched stops being reported as a run that failed, and a
credential that was never meant to be readable stops being served. Full
notes:
docs.objectstack.ai/docs/releases/v17.
Warning: 17.1.0 is a minor by version number, not by blast radius. Several of its security corrections change who can read or write on an existing deployment, with no migration step to notice. Work through the 17.1.0 upgrade checklist in the full notes before rolling forward.
What moved:
- Deactivating a permission set or a position now actually stops
granting access (security) — both objects ship a Deactivate action
whose dialog promises that access stops, and nothing read the column: a
position seeded
active: falsestill granted its permission sets, and a permission set seededactive: falsestill returned a platform-admin posture. A sharing rule reached users by a second road that never passed that seam, so a rule sharing records with a deactivated position kept sharing them — those shares are revoked on the next reconcile. A row whoseactivecolumn is absent or NULL is unaffected. On a deployment that used the switch believing it was inert this is a real revocation on live data, so audit your deactivated rows first. See Permission Sets. - The shipped admin sets no longer grant export on the
*wildcard (breaking) —admin_full_access,organization_adminand the derivedorganization_admin_no_bypasscarriedobjects['*'].allowExport: true, which made the 17.0 export axis undeniable: an application could declare an object exportable by nobody and the platform exported it anyway. Export is now granted per object, in an app's own permission set; there is deliberately no automatic replacement. Read is untouched — this narrows bulk egress only. - Partial field masking —
FieldSchemadeclaresmaskingRule: the closed preset enumphone/id_card/bank_account/email/name, plus a{ keepHead, keepTail }escape hatch. A field declaring a rule is served masked-but-recognizable (138****5678) to every non-system caller, with the field'srequiredPermissionsas the unmask gate. Masking rides the single runtime channel, so API callers, browser users and the CSV/XLSX export route all see the same value; a masked caller cannot filter, sort, group or aggregate on the field. See Field-Level Security. sys_audit_logcan answer "who viewed this record" — the ledger covered writes only; it gains areadaction, its writer, and arecord_viewslist view. Record-detail reads only, per-object opt-in with no global switch, batched off the request path, and never any field values — read auditing runs ahead of field masking, so copying values in would mint a plaintext copy of exactly what field-level security withholds. See Audit Logs.- Read-only approval visibility, per object, default off — an object can be named so that a user who can read a business record may also see that record's approval requests and full action history. Omitted or empty leaves visibility exactly as it is; on an object you do name it is not a no-op — a supervisor who holds full read but never appears in the approval used to get an empty list, and now sees the request row, every actor and decision, the action's comment text (意见正文), and decision attachments. The tier is read-only and introduces no new permission concept: the service reads the record as the caller, so ordinary CRUD and record access decide. See Approvals.
- All three flow doors answer one honest status table — a refused
dispatch stops being reported as a failed run:
404not found,409FLOW_DISABLED,422FLOW_NO_START_NODE, and400FLOW_FAILEDfor a run that actually ran and was rejected. The automationtriggerroutes, the actions door, and declaredtype: 'flow'endpoints now read one shared definition instead of three private copies, and anoutputMappingis no longer applied to a failure. Callers should branch on the HTTP status, not on an innersuccessflag. See Flows. error.codeis a closed vocabulary at every door — a thrown code outside the platform's standard set no longer reacheserror.code; it rides the new optionaldeclaredCodeinstead, so an application's own spellings survive without widening the platform vocabulary.- A failed read stops reading as an empty one — a recurring class
closed across the metadata protocol, the roll-up summary index, and the
cascade-delete and registry probes: a read that failed used to be
indistinguishable from one that legitimately found nothing, so an
upsert pre-load turned every update into an insert and "what would
break if I delete this" answered "nothing". No
catchis removed — each is discriminated by error type, so an unprovisioned table stays benign and everything else surfaces. - Author-time gates reach the runtime publish door — rules that only
os build/os validateran now also judge a runtime write, so Studio and the metadata API cannot land what the CLI refuses. Re-runos build/os validateafter upgrading: several new refusals can fail a stack that built clean on 17.0, including an unknown top-level stack key, a dashboard headermodalaction whose target is not a declared page, and a list-viewsortnaming a formula field or no field at all. - Console (Studio) — two objectui pin moves bring dashboard component
re-keying, the retirement of the structured
confirmobject on actions,I18nLabelon metric widgets, and host-performedsubmitBehavior.urlredirects for consoles mounted at a sub-path.
17.0
@objectstack 17.0.0 (released 2026-08-14) is a truth-telling
release: where 16.0 made declared metadata honest, 17.0 does the same
for the surfaces around it. Full notes:
docs.objectstack.ai/docs/releases/v17.
What moved:
- Node.js 22 is the floor (breaking) —
engines.nodemoves from>=18.0.0to>=22.0.0across all 50 published manifests. CI, the release pipeline, and every shipped Docker image already ran 22; the promise now matches the evidence. On Node 22+ nothing changes. If your CI pins Node, pin it to 22. - Bulk export is its own privilege (breaking) —
allowExportunset used to mean "inherit read"; it now means denied, andviewAllRecords/modifyAllRecordsno longer confer it either. Package-shipped sets are re-seeded on upgrade, soadmin_full_accessandorganization_adminkeep exporting — environment-authored sets are not. AddallowExport: trueto any custom set whose users export. See Permission Sets. - A file is a record, not a blob in a column — media fields
(
file/image/avatar/video/audio) store an opaquesys_fileid; the{ url, name, size, … }object becomes the read (expanded) form. Because the platform owns the bytes,accept/maxSizeare declarable and server-enforced rather than browser-side hints.os migrate files-to-referencesperforms the conversion. See Field Types. - The GraphQL surface is removed (breaking) — it was schema-only from
day one: the handler answered 501 unconditionally because the service
was never assigned.
/graphqlnow returns 404. Unaffected:graphqlas a protocol for an external datasource, which is someone else's API rather than one ObjectOS serves. - A sharing rule with no criteria shares nothing, and a disabled RLS
policy is disabled (breaking, security) — a rule stored without
criteria used to evaluate as every record of the object, and an RLS
policy switched off with
enabled: falsekept contributing its OR-branch grant. Both now fail closed. See Record Access. - An approval request is visible to its participants (security) —
getRequest/listRequests/countRequestsapplied only the tenant half of the visibility rule, so any authenticated user could read any request in their tenant: payload snapshot, decision history, and attachments. - Approvers can be routed dynamically — approval nodes gain
expressionapprovers (CEL overcurrent.*/trigger.*/vars.*), a node-levelonEmptyApproverspolicy, and author-declared decision outputs that resume the run as<nodeId>.<key>flow variables. See Approvals. - A broken datasource is loud at boot — a datasource that objects
bind to must connect or the boot fails,
objectql.init()refuses to start on a dead data driver, and/api/v1/readyanswers 503 when one stops answering. Previously such a boot started clean, exited zero, and then failed every read and write of the bound objects. enable.apiMethodsshrinks to six primitives (breaking) — the authorable enum is exactlyget,list,create,update,delete,bulk. The eight legacy values are derived effective operations; a stored one is stripped at parse with a warning naming its replacement.- The dead-metadata sweep continues (breaking) —
object.enable.trashandenable.mru,tool.requiresConfirmation,agent.tools[],agent.knowledge,SkillSchema.permissions, reportaria/performance,flow.active, and a long tail of parsed-but-ignored keys are removed. Each rejects at parse time with its upgrade prescription rather than being silently stripped. Runos migrate meta --from 16to rewrite your sources. - The authorable surface is closed — every authorable metadata type
now rejects unknown keys with a named prescription, on the parse path
and not only in
create(). A typo is a located error instead of a setting that quietly never took effect.
16.0
@objectstack 16.0.0 converges the developer surface and makes
declared metadata honest: one blessed name for the caller's org, real
multi-approver governance, time-relative automations that actually fire,
and a platform-wide sweep that turns silently-ignored metadata into loud
errors at authoring time. Full notes:
docs.objectstack.ai/docs/releases.
What moved:
tenantIdalias removed from hook/actionctx(16.0, breaking) — read the caller's org asctx.user.organizationId/ctx.session.organizationIdin every*.hook.ts/*.action.tsbody (the value is unchanged;ctx.userisundefinedfor system writes). The driver-layer tenancy axis (ExecutionContext.tenantId) is a different concept and is untouched.- Approvals: quorum, per-group sign-off (会签), and a metadata-driven
inbox — approval steps support M-of-N quorum (
minApprovals) and one-approver-from-each-group steps, tallied against an open-time snapshot with OOO substitution; a single rejection still vetoes, and thresholds clamp so misconfiguration can never deadlock. Decisions carry file attachments, progress ("2 of 3 · finance pending") is computed server-side, and approve / reject / reassign / send-back / request-info / remind / recall / resubmit are declaredtype:'api'actions onsys_approval_request— the approvals inbox renders them through its generic action runtime instead of hand-written buttons. - Time-relative automations — a flow start node can declare
timeRelative(offsetDays: [60, 30, 7]orwithinDays); a daily sweep launches the flow once per matching record, so "remind me 60 days before the contract ends" finally just works — no more date-equality conditions that only fire on a lucky edit. - Filtered roll-ups —
summaryOperations.filterlets a parent total aggregate only the matching child rows, and re-aggregates when a child moves in or out of the predicate. - Strict dashboard widgets (16.0, breaking) —
DashboardWidgetSchemais.strict(): an undeclared key (typo or removed inline-analytics key) is a parse error naming the key and pointing at the dataset shape (dataset+dimensions+values) instead of silently rendering nothing. - MCP: stdio gets an identity, agents get a linter — stdio
auto-start moves to its own
OS_MCP_STDIO_ENABLEDswitch (default off) and requires anOS_MCP_STDIO_API_KEY=osk_...principal, fail-closed, resolved through the same authorization chain as HTTP (RLS/FLS/tenant apply; nosystembypass). A newvalidate_expressiontool lets agents lint a formula against the real object schema before saving it. - Enforce-or-remove sweep (16.0, breaking) — declared-but-unenforced
metadata is now loud: dead field/object/agent props are removed or
tombstoned with guidance, hook events collapse 18 → 8, validation
rules lose the never-evaluated
'delete'event, webhookundelete/apitriggers are removed, and unknownrequirescapability tokens are rejected at authoring (theaiStudio/aiSeataliases are gone — use kebab-caseai-studio/ai-seat). - Engine-owned system rows are read-only through the generic data API
(ADR-0103) — jobs, notifications, approval-runtime rows, sharing rows,
audit trails, secrets, and the like are locked to
get/list, with a fail-closed guard rejecting user-context writes through/data. Third-partysystemobjects that legitimately take user writes must declareuserActions. - Date logic stops lying —
record.due_date == today()now matches (temporal equality comparisons are rewritten to coerce the field operand), while date arithmetic in formulas (end - start + 1,today() + 30) becomes a build-time error pointing atdaysBetween/daysFromNow/addDays/addMonths— those expressions always faulted tonullat runtime anyway. - Bulk user import defaults to
passwordPolicy: 'auto'— rows with a deliverable channel (real email + wired email service, or phone + SMS invite path) are invited; only unreachable rows get a one-time temporary password (must_change_password). PasspasswordPolicy: 'none'explicitly for the old identity-only behavior; per-row outcomes are onrows[].delivery.
14.x
ObjectOS One and the bundled server shipped on @objectstack 14.7.0.
The runtime boot contract is unchanged — createStandaloneStack takes the
same artifact, environment, and database settings — but 13.0 and 14.0 both
land breaking changes in the authorization vocabulary, so review your
permission metadata before rolling forward from ≤ 12.x. Full notes:
docs.objectstack.ai/docs/releases.
What moved in 14.x:
- ADR-0090 vocabulary convergence completed (14.0, breaking) —
book.audiencegates on{ permissionSet }(was{ profile }),PortalSchema.profiles→positions,RLSUserContextSchema.role→positions(string array),sys_record_share.recipient_type: 'role'→'position'. - Object capability flags are enforced (14.0, breaking-ish) — the
enable.*flags went from parsed-but-unused to real gates.activitiesandfeedsare opt-out (defaulttrue;feeds: falserejects comments with 403FEEDS_DISABLED),trackHistorydrives the History tab, andfilesis opt-in (403FILES_DISABLEDuntilenable: { files: true }). - Member baseline lost delete (14.2, breaking) —
member_defaultno longer grants record deletion; re-grant per object through position-distributed permission sets. - Admin user management & phone auth (14.3) — direct user creation
(
POST /api/v1/auth/admin/create-user, one-time passwords with forced rotation), bulk import (rows/CSV/XLSX, dry-run, upsert), opt-in phone sign-in with SMS OTP via@objectstack/plugin-sms(Aliyun / Twilio), and ansmsnotification channel. - Data-lifecycle contract (14.4, ADR-0057) — objects declare
lifecycle(class, retention, rotation, archive); the default-on LifecycleService reaps, rotates, and archives platform data (sys_activity14 days,sys_audit_log90 days hot). Disable withOS_LIFECYCLE_DISABLED=1; tune vialifecycle.retention_overrides. 14.5 removed the per-pluginretentionDays/retentionSweepMsoptions (JobRunRetention,NotificationRetention) in favor of lifecycle declarations, and split lifecycle-classed system data into a dedicated telemetry datasource (OS_TELEMETRY_DB);os db cleanreclaims SQLite space. - Effective-dated grants & delegation (14.4, ADR-0091) — position and
permission-set assignments support
valid_from/valid_untilwindows (fail-closed, no background job), anddelegatablepositions let holders self-delegate for a bounded window (≤ 30 days, reason required, never admin scopes). - MCP scope ceiling (14.5, ADR-0090 D10) — OAuth-connected agents run
under
effective_permission = scope_ceiling ∩ user_grants(data:read/data:write/actions:execute), failing closed. - Security fixes (14.4–14.5) — FLS keys must be object-qualified
(bare keys silently matched nothing; now lint-rejected with autofix);
settings and share-link routes no longer trust spoofable
x-user-id-style headers; analytics queries are scoped to the caller's read filter.
13.x — Permission Model v2
@objectstack 13.0 restructured authorization (ADR-0090). Breaking:
- Roles and Profiles converge on Positions —
sys_role*tables,RoleSchema/defineRole, metadata kindsrole/profile, andExecutionContext.roles[]are gone; positions are flat and hierarchy moved to the business-unit tree. Sharing recipients renamed (role→position,role_and_subordinates→unit_and_subordinates). - Custom objects default to private — an owner-carrying object with
no explicit
sharingModelis now private; theread/read_write/fullOWD aliases were removed. DeclaresharingModel: 'public_read_write'to restore prior behavior. - RBAC link-table writes gated — writes to
sys_user_position,sys_position_permission_set,sys_user_permission_set, andsys_permission_setrequire tenant admin or a delegated-admin scope.
And new: everyone / guest audience anchors, delegated administration
(PermissionSet.adminScope), the explain() engine with per-layer
attribution, the access-matrix snapshot gate for os compile,
self-serve OAuth 2.1 for MCP clients, author-time security lints
(validateSecurityPosture), per-operation Object.requiredPermissions
maps, and package namespace-prefix enforcement. Never-enforced schemas
(compliance / masking / global-RLS configs) were removed outright.
12.x
@objectstack 12.0 hardened the API default posture:
- Anonymous data access denied by default (breaking) —
api.requireAuthnow defaults totrue; anonymous/data/*requests get 401. Public-data deployments must opt out explicitly withapi: { requireAuth: false }(boot warning). Share links, public forms,/auth, and/healthare unaffected. - Enforced protocol handshake —
PROTOCOL_VERSION+checkProtocolCompat()reject incompatible packages at install time. - Adaptive record surfaces — records derive page vs. modal/drawer
display from field complexity;
FormField.spangoes responsive ('auto'/'full'); related lists gain'primary'tab promotion. - Package-shipped permissions — packages declare default permission sets that materialize at boot with provenance tracking.
- Build-time lints —
lint-view-refs,validateListViewMode,validateFormLayout, and destructive-operation RBAC mapping gate compilation.
10.x
The 10.x train shipped on @objectstack 10.0 – 10.2. Every breaking
change landed in the multi-org tenancy layer, so a single-tenant ObjectOS
deployment rolled forward from 9.x with no config changes. What moved:
- Row-level org scoping split into its own plugin (10.0, breaking) — the
organization_idauto-stamp, per-org seed replay, and default-org bootstrap moved out of@objectstack/plugin-securityinto the opt-in@objectstack/plugin-org-scoping. Single-org deployments are leaner (no wildcard RLS to strip on every query), andOS_MULTI_TENANT=truestill auto-registers the plugin ahead ofplugin-security, so projects driven by the CLI need no code changes. Per ADR-0002, a tenant is physical isolation (one Environment = one database);organization_idis logical scoping inside one DB, so it earns its own plugin. - No more automatic personal workspaces (10.0, breaking) —
plugin-securityno longer creates a "<User>'s Workspace" org for every new user. Users now accept an invitation or explicitly create their first organization (the Slack / Linear / GitHub-Orgs model). The first registered user — auto-promoted to platform admin — still gets a singleDefault Organizationin multi-tenant mode so their session resolves RLS; single-tenant deployments create no orgs. recordform field type (ADR-0014) —Record<string, X>properties (such as an object'sfieldsmap) are now editable as first-class form fields in Studio instead of leaking through as raw JSON, and the object preview pane mounts the realObjectGridrenderer — what you preview is what ships.- Pluggable embeddings via
IEmbedder— a new embedder protocol plus@objectstack/embedder-openai; the knowledge / RAG adapters now consumeIEmbedderand were renamed to drop theplugin-prefix. - Public Forms — anonymous Web-to-Lead / Web-to-Case forms served at
/f/:slug, a unified FormPage (public/f/:slug+ internal/forms/:name), atype: 'form'action variant, anddefaultDetailFormonObjectSchema. - Cloud identity split —
os cloud loginis now separate fromos login, the cloud control plane moved to a private repo (a leaner OSS runtime), andobjectstack initagain scaffolds a project that builds and boots.
9.x
The 9.x train ran on @objectstack 9.0 – 9.11 before the 10.0 cutover.
The runtime boot contract is unchanged from 8.0 — createStandaloneStack
still takes the same artifact, environment, and database settings — so an
8.0 deployment rolls forward without config changes. The author-facing
surface is what moved:
- Analytics datasets are the single author surface (9.0, breaking) —
dashboard widgets, reports, and list-charts now bind a semantic
dataset(defineDataset(...)) and select dimensions/measures by name. The old inline query fields (object/valueField/aggregateon widgets,objectName/columns/groupingsDownon reports,xAxisField/yAxisFieldson list-charts) were removed. Migration: move the inline query into adefineDatasetand reference it by name.ChartTypeSchemaalso dropped 8 variant types that only rendered as their base (stacked-bar→bar,spline→line,bubble→scatter, …). - Stricter build-time validation (9.6–9.7) —
os compilenow fails on bare field references (amountinstead ofrecord.amount), unknown CEL functions, and wrong flow-value interpolation syntax, each with a did-you-mean hint. A stack that previously "built but was silently wrong" now fails loudly — re-runos compileafter upgrading and fix what it flags. - Number-field formulas compute mixed arithmetic (9.7) —
record.amount / 100andrecord.price * 2now evaluate instead of silently yieldingnull; the/ 100.0float-literal workaround is no longer needed. - Object-level REST gating, now enforced (ADR-0049) — an object's
apiEnabled: falseremoves it from the REST surface, and anapiMethodswhitelist restricts which operations are reachable. Previously parsed but not enforced. - Package documentation as metadata +
booknavigation (9.3–9.6) —src/docs/*.mdregister asdocmetadata; thebookelement (ADR-0046) declares a derived-membership navigation spine, served atGET /api/v1/meta/book/:name/treewith audience gating. os package install(9.3) — install a package into a running runtime from a catalog id or an inline air-gapped artifact, authenticating with--email/--password.- Approvals (9.3) — send-back-for-revision (
maxRevisions, default 3), jobs-backed SLA auto-escalation, list search/pagination, and session-less bilingual approve/reject confirm links. - Inbound webhook flow triggers (9.3) — a
type: 'api'flow mounts an HMAC-verifiedPOST /api/v1/automation/hooks/:flowName/:hookIdendpoint with idempotent, queue-backed ingestion. - Notification retention defaults on (9.5) — notification history
auto-prunes at 90 days; set the messaging
retentionDays: 0to keep history forever. - CLI bundles AI provider SDKs (9.0) — OpenAI-compatible providers (DeepSeek, DashScope, SiliconFlow, OpenRouter, Cloudflare) work out of the box on a globally-installed CLI.
One flow-authoring behavior change to note: a create_record node's
outputVariable now holds the created record object (was the bare id), so
update {var} references that expected an id to {var.id}.
8.0.x
ObjectOS One and the bundled server shipped on @objectstack 8.0.1.
- MCP over Streamable HTTP — every deployment can act as a
network-reachable Model Context Protocol
server. Opt in with
OS_MCP_SERVER_ENABLED=true; the endpoint is served at/api/v1/mcpwith fail-closed auth (anonymous requests are rejected). The plugin was renamed@objectstack/plugin-mcp-server→@objectstack/mcp. - Self-serve API keys —
POST /api/v1/keysmints a show-oncesys_api_key. The REST data and metadata APIs (/api/v1/data,/api/v1/meta) now authenticate API keys through the same verifier as MCP, running under the key owner's permissions and record-level security. - Field-level conditional rules —
visibleWhen,readonlyWhen, andrequiredWhenare enforced server-side by ObjectQL, not just in the form UI. - Reusable RLS read filter —
security.getReadFilter(object, context)exposes the record-access read scope; analytics datasets, dashboards, and reports bridge to it and fail closed when the scope cannot be applied. - Standalone host stack — the runtime ships a single-tenant
createStandaloneStackhost; the 7.x cloud-connected, hostname-routedcreateObjectOSStackwrapper was removed. A deployment that runs a separately published app names that artifact by URL from then on. For the variable that does this today, and the retired spellings that do not, see Environment Variables.
5.0 — project → environment rename (shipped)
The runtime concept formerly called Project was renamed to Environment throughout. Affected:
- CLI flags:
--environment/-e - HTTP paths:
/api/v1/environments/:environmentId/... - Headers:
X-Environment-Id - Env vars:
OS_ENVIRONMENT_ID(OS_PROJECT_IDwas kept as a deprecated alias at the time; it is no longer read) - DB columns:
environment_id - JSON schemas:
EnvironmentArtifact
Upgrading
See Upgrade and Rollback for the mechanical steps. Pre-flight:
- Read the CHANGELOG entry for every minor between your current and target version.
- Run
os diff <old-artifact> <new-artifact>to surface breaking schema changes. - Run
os doctoragainst the target version. - Bring up one canary instance before rolling the fleet.
- Have a rollback plan for both the image tag and the artifact version (they roll independently).
Reporting regressions
If a patch or minor breaks something previously working, file a bug at github.com/objectstack-ai/objectos/issues with the version you upgraded from / to. We treat regressions as the highest-priority class of bug.