diff --git a/.changeset/11979-default-org-admin-grant.md b/.changeset/pre/11979-default-org-admin-grant.md similarity index 100% rename from .changeset/11979-default-org-admin-grant.md rename to .changeset/pre/11979-default-org-admin-grant.md diff --git a/.changeset/15196-retired-catalog-residue.md b/.changeset/pre/15196-retired-catalog-residue.md similarity index 100% rename from .changeset/15196-retired-catalog-residue.md rename to .changeset/pre/15196-retired-catalog-residue.md diff --git a/.changeset/15212-8a-one-predicate.md b/.changeset/pre/15212-8a-one-predicate.md similarity index 100% rename from .changeset/15212-8a-one-predicate.md rename to .changeset/pre/15212-8a-one-predicate.md diff --git a/.changeset/15212-8c0-unattributable-rows-block.md b/.changeset/pre/15212-8c0-unattributable-rows-block.md similarity index 100% rename from .changeset/15212-8c0-unattributable-rows-block.md rename to .changeset/pre/15212-8c0-unattributable-rows-block.md diff --git a/.changeset/15953-s8-cli-dashboard-windows-flag.md b/.changeset/pre/15953-s8-cli-dashboard-windows-flag.md similarity index 100% rename from .changeset/15953-s8-cli-dashboard-windows-flag.md rename to .changeset/pre/15953-s8-cli-dashboard-windows-flag.md diff --git a/.changeset/15953-s8-window-sum-derived.md b/.changeset/pre/15953-s8-window-sum-derived.md similarity index 100% rename from .changeset/15953-s8-window-sum-derived.md rename to .changeset/pre/15953-s8-window-sum-derived.md diff --git a/.changeset/22161-s21-validate-fix-lines.md b/.changeset/pre/22161-s21-validate-fix-lines.md similarity index 100% rename from .changeset/22161-s21-validate-fix-lines.md rename to .changeset/pre/22161-s21-validate-fix-lines.md diff --git a/.changeset/22270-print-record-door.md b/.changeset/pre/22270-print-record-door.md similarity index 100% rename from .changeset/22270-print-record-door.md rename to .changeset/pre/22270-print-record-door.md diff --git a/.changeset/22444-list-view-component-props-row.md b/.changeset/pre/22444-list-view-component-props-row.md similarity index 100% rename from .changeset/22444-list-view-component-props-row.md rename to .changeset/pre/22444-list-view-component-props-row.md diff --git a/.changeset/23050-s12-crud-liveness-citations.md b/.changeset/pre/23050-s12-crud-liveness-citations.md similarity index 100% rename from .changeset/23050-s12-crud-liveness-citations.md rename to .changeset/pre/23050-s12-crud-liveness-citations.md diff --git a/.changeset/23050-s12-crud-routes-move.md b/.changeset/pre/23050-s12-crud-routes-move.md similarity index 100% rename from .changeset/23050-s12-crud-routes-move.md rename to .changeset/pre/23050-s12-crud-routes-move.md diff --git a/.changeset/23050-s13a-meta-collection-liveness-citations.md b/.changeset/pre/23050-s13a-meta-collection-liveness-citations.md similarity index 100% rename from .changeset/23050-s13a-meta-collection-liveness-citations.md rename to .changeset/pre/23050-s13a-meta-collection-liveness-citations.md diff --git a/.changeset/23050-s13a-meta-collection-routes-move.md b/.changeset/pre/23050-s13a-meta-collection-routes-move.md similarity index 100% rename from .changeset/23050-s13a-meta-collection-routes-move.md rename to .changeset/pre/23050-s13a-meta-collection-routes-move.md diff --git a/.changeset/23050-s13b-meta-item-read-liveness-citations.md b/.changeset/pre/23050-s13b-meta-item-read-liveness-citations.md similarity index 100% rename from .changeset/23050-s13b-meta-item-read-liveness-citations.md rename to .changeset/pre/23050-s13b-meta-item-read-liveness-citations.md diff --git a/.changeset/23050-s13b-meta-item-read-routes-move.md b/.changeset/pre/23050-s13b-meta-item-read-routes-move.md similarity index 100% rename from .changeset/23050-s13b-meta-item-read-routes-move.md rename to .changeset/pre/23050-s13b-meta-item-read-routes-move.md diff --git a/.changeset/23050-s13c-meta-item-write-routes-move.md b/.changeset/pre/23050-s13c-meta-item-write-routes-move.md similarity index 100% rename from .changeset/23050-s13c-meta-item-write-routes-move.md rename to .changeset/pre/23050-s13c-meta-item-write-routes-move.md diff --git a/.changeset/23050-s13d-meta-item-history-routes-move.md b/.changeset/pre/23050-s13d-meta-item-history-routes-move.md similarity index 100% rename from .changeset/23050-s13d-meta-item-history-routes-move.md rename to .changeset/pre/23050-s13d-meta-item-history-routes-move.md diff --git a/.changeset/23050-s13e-meta-object-state-published-move.md b/.changeset/pre/23050-s13e-meta-object-state-published-move.md similarity index 100% rename from .changeset/23050-s13e-meta-object-state-published-move.md rename to .changeset/pre/23050-s13e-meta-object-state-published-move.md diff --git a/.changeset/23217-core-request-execution-context.md b/.changeset/pre/23217-core-request-execution-context.md similarity index 100% rename from .changeset/23217-core-request-execution-context.md rename to .changeset/pre/23217-core-request-execution-context.md diff --git a/.changeset/23217-s2-rest-door.md b/.changeset/pre/23217-s2-rest-door.md similarity index 100% rename from .changeset/23217-s2-rest-door.md rename to .changeset/pre/23217-s2-rest-door.md diff --git a/.changeset/23217-s3-settings-door.md b/.changeset/pre/23217-s3-settings-door.md similarity index 100% rename from .changeset/23217-s3-settings-door.md rename to .changeset/pre/23217-s3-settings-door.md diff --git a/.changeset/23217-s4-storage-door.md b/.changeset/pre/23217-s4-storage-door.md similarity index 100% rename from .changeset/23217-s4-storage-door.md rename to .changeset/pre/23217-s4-storage-door.md diff --git a/.changeset/23890-one-audit-entry.md b/.changeset/pre/23890-one-audit-entry.md similarity index 100% rename from .changeset/23890-one-audit-entry.md rename to .changeset/pre/23890-one-audit-entry.md diff --git a/.changeset/23933-installed-apps-packages-filter.md b/.changeset/pre/23933-installed-apps-packages-filter.md similarity index 100% rename from .changeset/23933-installed-apps-packages-filter.md rename to .changeset/pre/23933-installed-apps-packages-filter.md diff --git a/.changeset/23945-invite-role-cap-codes.md b/.changeset/pre/23945-invite-role-cap-codes.md similarity index 100% rename from .changeset/23945-invite-role-cap-codes.md rename to .changeset/pre/23945-invite-role-cap-codes.md diff --git a/.changeset/23952-declared-sharing-rules-registry.md b/.changeset/pre/23952-declared-sharing-rules-registry.md similarity index 100% rename from .changeset/23952-declared-sharing-rules-registry.md rename to .changeset/pre/23952-declared-sharing-rules-registry.md diff --git a/.changeset/23968-print-local-loopback-host.md b/.changeset/pre/23968-print-local-loopback-host.md similarity index 93% rename from .changeset/23968-print-local-loopback-host.md rename to .changeset/pre/23968-print-local-loopback-host.md index f1583da56d8..37727200536 100644 --- a/.changeset/23968-print-local-loopback-host.md +++ b/.changeset/pre/23968-print-local-loopback-host.md @@ -2,6 +2,8 @@ '@objectstack/service-print': patch --- +**Shipped in 18.0.0-next.3**, whose CHANGELOG missed this entry; listed here one prerelease late. + Clause-②: no fix(service-print): the local placement binds its browser server's control socket to `127.0.0.1` itself, instead of relying on `playwright-core`'s default host diff --git a/.changeset/23983-previous-null-on-insert.md b/.changeset/pre/23983-previous-null-on-insert.md similarity index 100% rename from .changeset/23983-previous-null-on-insert.md rename to .changeset/pre/23983-previous-null-on-insert.md diff --git a/.changeset/24002-start-mcp-stdio-early-initialize.md b/.changeset/pre/24002-start-mcp-stdio-early-initialize.md similarity index 100% rename from .changeset/24002-start-mcp-stdio-early-initialize.md rename to .changeset/pre/24002-start-mcp-stdio-early-initialize.md diff --git a/.changeset/24016-org-invite-action.md b/.changeset/pre/24016-org-invite-action.md similarity index 100% rename from .changeset/24016-org-invite-action.md rename to .changeset/pre/24016-org-invite-action.md diff --git a/.changeset/24023-keys-actions-405-allow.md b/.changeset/pre/24023-keys-actions-405-allow.md similarity index 100% rename from .changeset/24023-keys-actions-405-allow.md rename to .changeset/pre/24023-keys-actions-405-allow.md diff --git a/.changeset/24025-put-patch-request-id.md b/.changeset/pre/24025-put-patch-request-id.md similarity index 100% rename from .changeset/24025-put-patch-request-id.md rename to .changeset/pre/24025-put-patch-request-id.md diff --git a/.changeset/24040-reseed-converted-body.md b/.changeset/pre/24040-reseed-converted-body.md similarity index 100% rename from .changeset/24040-reseed-converted-body.md rename to .changeset/pre/24040-reseed-converted-body.md diff --git a/.changeset/24059-unmanaged-sweep-sys-packages.md b/.changeset/pre/24059-unmanaged-sweep-sys-packages.md similarity index 100% rename from .changeset/24059-unmanaged-sweep-sys-packages.md rename to .changeset/pre/24059-unmanaged-sweep-sys-packages.md diff --git a/.changeset/24060-activity-gate-named-parents.md b/.changeset/pre/24060-activity-gate-named-parents.md similarity index 100% rename from .changeset/24060-activity-gate-named-parents.md rename to .changeset/pre/24060-activity-gate-named-parents.md diff --git a/.changeset/24061-auth-prelude-queries.md b/.changeset/pre/24061-auth-prelude-queries.md similarity index 100% rename from .changeset/24061-auth-prelude-queries.md rename to .changeset/pre/24061-auth-prelude-queries.md diff --git a/.changeset/24062-authored-install-scope-project.md b/.changeset/pre/24062-authored-install-scope-project.md similarity index 100% rename from .changeset/24062-authored-install-scope-project.md rename to .changeset/pre/24062-authored-install-scope-project.md diff --git a/.changeset/24063-view-liveness-evidence.md b/.changeset/pre/24063-view-liveness-evidence.md similarity index 100% rename from .changeset/24063-view-liveness-evidence.md rename to .changeset/pre/24063-view-liveness-evidence.md diff --git a/.changeset/24070-withdraw-meta-object-copies.md b/.changeset/pre/24070-withdraw-meta-object-copies.md similarity index 100% rename from .changeset/24070-withdraw-meta-object-copies.md rename to .changeset/pre/24070-withdraw-meta-object-copies.md diff --git a/.changeset/24086-install-primitive-parses-manifest.md b/.changeset/pre/24086-install-primitive-parses-manifest.md similarity index 100% rename from .changeset/24086-install-primitive-parses-manifest.md rename to .changeset/pre/24086-install-primitive-parses-manifest.md diff --git a/.changeset/24087-search-title-namefield-first.md b/.changeset/pre/24087-search-title-namefield-first.md similarity index 100% rename from .changeset/24087-search-title-namefield-first.md rename to .changeset/pre/24087-search-title-namefield-first.md diff --git a/.changeset/24090-db-dir-dangling-symlink.md b/.changeset/pre/24090-db-dir-dangling-symlink.md similarity index 100% rename from .changeset/24090-db-dir-dangling-symlink.md rename to .changeset/pre/24090-db-dir-dangling-symlink.md diff --git a/.changeset/24091-serve-runtime-state-write-warning.md b/.changeset/pre/24091-serve-runtime-state-write-warning.md similarity index 100% rename from .changeset/24091-serve-runtime-state-write-warning.md rename to .changeset/pre/24091-serve-runtime-state-write-warning.md diff --git a/.changeset/24097-metadata-protocol-by-name-disabled-package.md b/.changeset/pre/24097-metadata-protocol-by-name-disabled-package.md similarity index 100% rename from .changeset/24097-metadata-protocol-by-name-disabled-package.md rename to .changeset/pre/24097-metadata-protocol-by-name-disabled-package.md diff --git a/.changeset/24097-rest-exposure-gate-disabled-package.md b/.changeset/pre/24097-rest-exposure-gate-disabled-package.md similarity index 100% rename from .changeset/24097-rest-exposure-gate-disabled-package.md rename to .changeset/pre/24097-rest-exposure-gate-disabled-package.md diff --git a/.changeset/24097-runtime-meta-object-disabled-package.md b/.changeset/pre/24097-runtime-meta-object-disabled-package.md similarity index 100% rename from .changeset/24097-runtime-meta-object-disabled-package.md rename to .changeset/pre/24097-runtime-meta-object-disabled-package.md diff --git a/.changeset/24120-migrate-plan-declared-datasources.md b/.changeset/pre/24120-migrate-plan-declared-datasources.md similarity index 100% rename from .changeset/24120-migrate-plan-declared-datasources.md rename to .changeset/pre/24120-migrate-plan-declared-datasources.md diff --git a/.changeset/24126-system-overview-metric-height.md b/.changeset/pre/24126-system-overview-metric-height.md similarity index 100% rename from .changeset/24126-system-overview-metric-height.md rename to .changeset/pre/24126-system-overview-metric-height.md diff --git a/.changeset/24130-form-submit-docstring.md b/.changeset/pre/24130-form-submit-docstring.md similarity index 100% rename from .changeset/24130-form-submit-docstring.md rename to .changeset/pre/24130-form-submit-docstring.md diff --git a/.changeset/24131-core-required-emptiness.md b/.changeset/pre/24131-core-required-emptiness.md similarity index 100% rename from .changeset/24131-core-required-emptiness.md rename to .changeset/pre/24131-core-required-emptiness.md diff --git a/.changeset/24131-objectql-required-emptiness-import.md b/.changeset/pre/24131-objectql-required-emptiness-import.md similarity index 100% rename from .changeset/24131-objectql-required-emptiness-import.md rename to .changeset/pre/24131-objectql-required-emptiness-import.md diff --git a/.changeset/24131-rest-public-form-required.md b/.changeset/pre/24131-rest-public-form-required.md similarity index 100% rename from .changeset/24131-rest-public-form-required.md rename to .changeset/pre/24131-rest-public-form-required.md diff --git a/.changeset/24137-oauth-token-organization.md b/.changeset/pre/24137-oauth-token-organization.md similarity index 100% rename from .changeset/24137-oauth-token-organization.md rename to .changeset/pre/24137-oauth-token-organization.md diff --git a/.changeset/24140-line-items-columns-hydrate.md b/.changeset/pre/24140-line-items-columns-hydrate.md similarity index 100% rename from .changeset/24140-line-items-columns-hydrate.md rename to .changeset/pre/24140-line-items-columns-hydrate.md diff --git a/.changeset/24142-settings-organization-noun.md b/.changeset/pre/24142-settings-organization-noun.md similarity index 100% rename from .changeset/24142-settings-organization-noun.md rename to .changeset/pre/24142-settings-organization-noun.md diff --git a/.changeset/24143-export-hidden-columns.md b/.changeset/pre/24143-export-hidden-columns.md similarity index 100% rename from .changeset/24143-export-hidden-columns.md rename to .changeset/pre/24143-export-hidden-columns.md diff --git a/.changeset/24148-cloud-connection-setup-access.md b/.changeset/pre/24148-cloud-connection-setup-access.md similarity index 100% rename from .changeset/24148-cloud-connection-setup-access.md rename to .changeset/pre/24148-cloud-connection-setup-access.md diff --git a/.changeset/24150-cloud-connection-runtime-version.md b/.changeset/pre/24150-cloud-connection-runtime-version.md similarity index 100% rename from .changeset/24150-cloud-connection-runtime-version.md rename to .changeset/pre/24150-cloud-connection-runtime-version.md diff --git a/.changeset/24151-body-after-anonymous-floor.md b/.changeset/pre/24151-body-after-anonymous-floor.md similarity index 100% rename from .changeset/24151-body-after-anonymous-floor.md rename to .changeset/pre/24151-body-after-anonymous-floor.md diff --git a/.changeset/24154-invitation-role-label.md b/.changeset/pre/24154-invitation-role-label.md similarity index 100% rename from .changeset/24154-invitation-role-label.md rename to .changeset/pre/24154-invitation-role-label.md diff --git a/.changeset/24162-record-json-retired.md b/.changeset/pre/24162-record-json-retired.md similarity index 100% rename from .changeset/24162-record-json-retired.md rename to .changeset/pre/24162-record-json-retired.md diff --git a/.changeset/24165-mcp-token-organization-id.md b/.changeset/pre/24165-mcp-token-organization-id.md similarity index 100% rename from .changeset/24165-mcp-token-organization-id.md rename to .changeset/pre/24165-mcp-token-organization-id.md diff --git a/.changeset/24166-oauth-consent-organization.md b/.changeset/pre/24166-oauth-consent-organization.md similarity index 100% rename from .changeset/24166-oauth-consent-organization.md rename to .changeset/pre/24166-oauth-consent-organization.md diff --git a/.changeset/24179-step18-rationale-tracker-ids.md b/.changeset/pre/24179-step18-rationale-tracker-ids.md similarity index 100% rename from .changeset/24179-step18-rationale-tracker-ids.md rename to .changeset/pre/24179-step18-rationale-tracker-ids.md diff --git a/.changeset/24182-extender-withdrawal-restores-owner.md b/.changeset/pre/24182-extender-withdrawal-restores-owner.md similarity index 100% rename from .changeset/24182-extender-withdrawal-restores-owner.md rename to .changeset/pre/24182-extender-withdrawal-restores-owner.md diff --git a/.changeset/24183-export-json-fields.md b/.changeset/pre/24183-export-json-fields.md similarity index 100% rename from .changeset/24183-export-json-fields.md rename to .changeset/pre/24183-export-json-fields.md diff --git a/.changeset/24186-mcp-verifier-keyring.md b/.changeset/pre/24186-mcp-verifier-keyring.md similarity index 100% rename from .changeset/24186-mcp-verifier-keyring.md rename to .changeset/pre/24186-mcp-verifier-keyring.md diff --git a/.changeset/24187-docblock-mechanisms.md b/.changeset/pre/24187-docblock-mechanisms.md similarity index 100% rename from .changeset/24187-docblock-mechanisms.md rename to .changeset/pre/24187-docblock-mechanisms.md diff --git a/.changeset/24190-line-items-hydrated-check.md b/.changeset/pre/24190-line-items-hydrated-check.md similarity index 100% rename from .changeset/24190-line-items-hydrated-check.md rename to .changeset/pre/24190-line-items-hydrated-check.md diff --git a/.changeset/24191-serve-mcp-stdio-early-initialize.md b/.changeset/pre/24191-serve-mcp-stdio-early-initialize.md similarity index 100% rename from .changeset/24191-serve-mcp-stdio-early-initialize.md rename to .changeset/pre/24191-serve-mcp-stdio-early-initialize.md diff --git a/.changeset/24192-start-compile-stdout-purity.md b/.changeset/pre/24192-start-compile-stdout-purity.md similarity index 100% rename from .changeset/24192-start-compile-stdout-purity.md rename to .changeset/pre/24192-start-compile-stdout-purity.md diff --git a/.changeset/24195-page-template-entry-reason.md b/.changeset/pre/24195-page-template-entry-reason.md similarity index 100% rename from .changeset/24195-page-template-entry-reason.md rename to .changeset/pre/24195-page-template-entry-reason.md diff --git a/.changeset/24196-auth-session-api-declared-fields.md b/.changeset/pre/24196-auth-session-api-declared-fields.md similarity index 100% rename from .changeset/24196-auth-session-api-declared-fields.md rename to .changeset/pre/24196-auth-session-api-declared-fields.md diff --git a/.changeset/24197-formula-traversal-subject.md b/.changeset/pre/24197-formula-traversal-subject.md similarity index 100% rename from .changeset/24197-formula-traversal-subject.md rename to .changeset/pre/24197-formula-traversal-subject.md diff --git a/.changeset/24197-lint-formula-one-hop.md b/.changeset/pre/24197-lint-formula-one-hop.md similarity index 100% rename from .changeset/24197-lint-formula-one-hop.md rename to .changeset/pre/24197-lint-formula-one-hop.md diff --git a/.changeset/24197-objectql-formula-one-hop.md b/.changeset/pre/24197-objectql-formula-one-hop.md similarity index 100% rename from .changeset/24197-objectql-formula-one-hop.md rename to .changeset/pre/24197-objectql-formula-one-hop.md diff --git a/.changeset/24197-objectql-serve-formula-field.md b/.changeset/pre/24197-objectql-serve-formula-field.md similarity index 100% rename from .changeset/24197-objectql-serve-formula-field.md rename to .changeset/pre/24197-objectql-serve-formula-field.md diff --git a/.changeset/24197-runtime-hook-title-one-hop.md b/.changeset/pre/24197-runtime-hook-title-one-hop.md similarity index 100% rename from .changeset/24197-runtime-hook-title-one-hop.md rename to .changeset/pre/24197-runtime-hook-title-one-hop.md diff --git a/.changeset/24197-spec-blueprint-one-hop-title.md b/.changeset/pre/24197-spec-blueprint-one-hop-title.md similarity index 100% rename from .changeset/24197-spec-blueprint-one-hop-title.md rename to .changeset/pre/24197-spec-blueprint-one-hop-title.md diff --git a/.changeset/24198-blueprint-option-kind-stage-field.md b/.changeset/pre/24198-blueprint-option-kind-stage-field.md similarity index 100% rename from .changeset/24198-blueprint-option-kind-stage-field.md rename to .changeset/pre/24198-blueprint-option-kind-stage-field.md diff --git a/.changeset/24210-metadata-protocol-by-name-disabled-non-object.md b/.changeset/pre/24210-metadata-protocol-by-name-disabled-non-object.md similarity index 100% rename from .changeset/24210-metadata-protocol-by-name-disabled-non-object.md rename to .changeset/pre/24210-metadata-protocol-by-name-disabled-non-object.md diff --git a/.changeset/24210-metadata-protocol-drafts-disabled-package.md b/.changeset/pre/24210-metadata-protocol-drafts-disabled-package.md similarity index 100% rename from .changeset/24210-metadata-protocol-drafts-disabled-package.md rename to .changeset/pre/24210-metadata-protocol-drafts-disabled-package.md diff --git a/.changeset/24210-plugin-security-echo-binding-disabled-package.md b/.changeset/pre/24210-plugin-security-echo-binding-disabled-package.md similarity index 100% rename from .changeset/24210-plugin-security-echo-binding-disabled-package.md rename to .changeset/pre/24210-plugin-security-echo-binding-disabled-package.md diff --git a/.changeset/24210-rest-event-diff-doors-disabled-package.md b/.changeset/pre/24210-rest-event-diff-doors-disabled-package.md similarity index 100% rename from .changeset/24210-rest-event-diff-doors-disabled-package.md rename to .changeset/pre/24210-rest-event-diff-doors-disabled-package.md diff --git a/.changeset/24210-rest-layered-doors-disabled-package.md b/.changeset/pre/24210-rest-layered-doors-disabled-package.md similarity index 100% rename from .changeset/24210-rest-layered-doors-disabled-package.md rename to .changeset/pre/24210-rest-layered-doors-disabled-package.md diff --git a/.changeset/24210-rest-published-state-event-body-disabled-package.md b/.changeset/pre/24210-rest-published-state-event-body-disabled-package.md similarity index 100% rename from .changeset/24210-rest-published-state-event-body-disabled-package.md rename to .changeset/pre/24210-rest-published-state-event-body-disabled-package.md diff --git a/.changeset/24210-runtime-layered-doors-disabled-package.md b/.changeset/pre/24210-runtime-layered-doors-disabled-package.md similarity index 100% rename from .changeset/24210-runtime-layered-doors-disabled-package.md rename to .changeset/pre/24210-runtime-layered-doors-disabled-package.md diff --git a/.changeset/24210-runtime-meta-disabled-package-doors.md b/.changeset/pre/24210-runtime-meta-disabled-package-doors.md similarity index 100% rename from .changeset/24210-runtime-meta-disabled-package-doors.md rename to .changeset/pre/24210-runtime-meta-disabled-package-doors.md diff --git a/.changeset/24210-runtime-published-disabled-package.md b/.changeset/pre/24210-runtime-published-disabled-package.md similarity index 100% rename from .changeset/24210-runtime-published-disabled-package.md rename to .changeset/pre/24210-runtime-published-disabled-package.md diff --git a/.changeset/24222-get-session-readers-declared.md b/.changeset/pre/24222-get-session-readers-declared.md similarity index 100% rename from .changeset/24222-get-session-readers-declared.md rename to .changeset/pre/24222-get-session-readers-declared.md diff --git a/.changeset/24228-actions-every-verb-anonymous-floor.md b/.changeset/pre/24228-actions-every-verb-anonymous-floor.md similarity index 100% rename from .changeset/24228-actions-every-verb-anonymous-floor.md rename to .changeset/pre/24228-actions-every-verb-anonymous-floor.md diff --git a/.changeset/24230-marketplace-installed-list-retired.md b/.changeset/pre/24230-marketplace-installed-list-retired.md similarity index 100% rename from .changeset/24230-marketplace-installed-list-retired.md rename to .changeset/pre/24230-marketplace-installed-list-retired.md diff --git a/.changeset/24235-audit-write-locale-seam.md b/.changeset/pre/24235-audit-write-locale-seam.md similarity index 100% rename from .changeset/24235-audit-write-locale-seam.md rename to .changeset/pre/24235-audit-write-locale-seam.md diff --git a/.changeset/24239-public-form-grant-skill.md b/.changeset/pre/24239-public-form-grant-skill.md similarity index 100% rename from .changeset/24239-public-form-grant-skill.md rename to .changeset/pre/24239-public-form-grant-skill.md diff --git a/.changeset/24243-plugin-auth-hook-refusal-codes.md b/.changeset/pre/24243-plugin-auth-hook-refusal-codes.md similarity index 100% rename from .changeset/24243-plugin-auth-hook-refusal-codes.md rename to .changeset/pre/24243-plugin-auth-hook-refusal-codes.md diff --git a/.changeset/24245-head-dispatched-as-get.md b/.changeset/pre/24245-head-dispatched-as-get.md similarity index 100% rename from .changeset/24245-head-dispatched-as-get.md rename to .changeset/pre/24245-head-dispatched-as-get.md diff --git a/.changeset/24251-mcp-anonymous-first.md b/.changeset/pre/24251-mcp-anonymous-first.md similarity index 100% rename from .changeset/24251-mcp-anonymous-first.md rename to .changeset/pre/24251-mcp-anonymous-first.md diff --git a/.changeset/24252-endpoint-fallback-body-after-match.md b/.changeset/pre/24252-endpoint-fallback-body-after-match.md similarity index 100% rename from .changeset/24252-endpoint-fallback-body-after-match.md rename to .changeset/pre/24252-endpoint-fallback-body-after-match.md diff --git a/.changeset/24253-client-publish-drafts-backfill.md b/.changeset/pre/24253-client-publish-drafts-backfill.md similarity index 100% rename from .changeset/24253-client-publish-drafts-backfill.md rename to .changeset/pre/24253-client-publish-drafts-backfill.md diff --git a/.changeset/24253-metadata-protocol-publish-backfill.md b/.changeset/pre/24253-metadata-protocol-publish-backfill.md similarity index 100% rename from .changeset/24253-metadata-protocol-publish-backfill.md rename to .changeset/pre/24253-metadata-protocol-publish-backfill.md diff --git a/.changeset/24253-runtime-publish-drafts-backfill.md b/.changeset/pre/24253-runtime-publish-drafts-backfill.md similarity index 100% rename from .changeset/24253-runtime-publish-drafts-backfill.md rename to .changeset/pre/24253-runtime-publish-drafts-backfill.md diff --git a/.changeset/24253-spec-publish-drafts-backfill-applied.md b/.changeset/pre/24253-spec-publish-drafts-backfill-applied.md similarity index 100% rename from .changeset/24253-spec-publish-drafts-backfill-applied.md rename to .changeset/pre/24253-spec-publish-drafts-backfill-applied.md diff --git a/.changeset/24254-plugin-auth-hook-codes.md b/.changeset/pre/24254-plugin-auth-hook-codes.md similarity index 100% rename from .changeset/24254-plugin-auth-hook-codes.md rename to .changeset/pre/24254-plugin-auth-hook-codes.md diff --git a/.changeset/24264-approval-request-strip-flow-label.md b/.changeset/pre/24264-approval-request-strip-flow-label.md similarity index 100% rename from .changeset/24264-approval-request-strip-flow-label.md rename to .changeset/pre/24264-approval-request-strip-flow-label.md diff --git a/.changeset/24265-app-page-record-binding.md b/.changeset/pre/24265-app-page-record-binding.md similarity index 100% rename from .changeset/24265-app-page-record-binding.md rename to .changeset/pre/24265-app-page-record-binding.md diff --git a/.changeset/24266-record-blocks-empty-state.md b/.changeset/pre/24266-record-blocks-empty-state.md similarity index 100% rename from .changeset/24266-record-blocks-empty-state.md rename to .changeset/pre/24266-record-blocks-empty-state.md diff --git a/.changeset/24268-declarative-update-undeclared-params-refused.md b/.changeset/pre/24268-declarative-update-undeclared-params-refused.md similarity index 100% rename from .changeset/24268-declarative-update-undeclared-params-refused.md rename to .changeset/pre/24268-declarative-update-undeclared-params-refused.md diff --git a/.changeset/24271-org-rename-door.md b/.changeset/pre/24271-org-rename-door.md similarity index 100% rename from .changeset/24271-org-rename-door.md rename to .changeset/pre/24271-org-rename-door.md diff --git a/.changeset/24273-transfer-ownership-honest.md b/.changeset/pre/24273-transfer-ownership-honest.md similarity index 100% rename from .changeset/24273-transfer-ownership-honest.md rename to .changeset/pre/24273-transfer-ownership-honest.md diff --git a/.changeset/24281-approval-request-strip-step-label.md b/.changeset/pre/24281-approval-request-strip-step-label.md similarity index 100% rename from .changeset/24281-approval-request-strip-step-label.md rename to .changeset/pre/24281-approval-request-strip-step-label.md diff --git a/.changeset/24282-ceremony-null-org-guard.md b/.changeset/pre/24282-ceremony-null-org-guard.md similarity index 100% rename from .changeset/24282-ceremony-null-org-guard.md rename to .changeset/pre/24282-ceremony-null-org-guard.md diff --git a/.changeset/24283-console-pin-0707b4ae3712.md b/.changeset/pre/24283-console-pin-0707b4ae3712.md similarity index 100% rename from .changeset/24283-console-pin-0707b4ae3712.md rename to .changeset/pre/24283-console-pin-0707b4ae3712.md diff --git a/.changeset/24284-search-all-internal-skip.md b/.changeset/pre/24284-search-all-internal-skip.md similarity index 100% rename from .changeset/24284-search-all-internal-skip.md rename to .changeset/pre/24284-search-all-internal-skip.md diff --git a/.changeset/24284-search-empty-set-refused.md b/.changeset/pre/24284-search-empty-set-refused.md similarity index 100% rename from .changeset/24284-search-empty-set-refused.md rename to .changeset/pre/24284-search-empty-set-refused.md diff --git a/.changeset/24287-hono-stream-string-verbatim.md b/.changeset/pre/24287-hono-stream-string-verbatim.md similarity index 100% rename from .changeset/24287-hono-stream-string-verbatim.md rename to .changeset/pre/24287-hono-stream-string-verbatim.md diff --git a/.changeset/24289-inprocess-hook-ctx-title.md b/.changeset/pre/24289-inprocess-hook-ctx-title.md similarity index 100% rename from .changeset/24289-inprocess-hook-ctx-title.md rename to .changeset/pre/24289-inprocess-hook-ctx-title.md diff --git a/.changeset/24291-cbp-docblock-tense.md b/.changeset/pre/24291-cbp-docblock-tense.md similarity index 100% rename from .changeset/24291-cbp-docblock-tense.md rename to .changeset/pre/24291-cbp-docblock-tense.md diff --git a/.changeset/24293-filter-token-message.md b/.changeset/pre/24293-filter-token-message.md similarity index 100% rename from .changeset/24293-filter-token-message.md rename to .changeset/pre/24293-filter-token-message.md diff --git a/.changeset/24298-draft-key-provenance.md b/.changeset/pre/24298-draft-key-provenance.md similarity index 100% rename from .changeset/24298-draft-key-provenance.md rename to .changeset/pre/24298-draft-key-provenance.md diff --git a/.changeset/24299-api-key-read-outage.md b/.changeset/pre/24299-api-key-read-outage.md similarity index 100% rename from .changeset/24299-api-key-read-outage.md rename to .changeset/pre/24299-api-key-read-outage.md diff --git a/.changeset/24301-seed-null-organization-holder-refused.md b/.changeset/pre/24301-seed-null-organization-holder-refused.md similarity index 100% rename from .changeset/24301-seed-null-organization-holder-refused.md rename to .changeset/pre/24301-seed-null-organization-holder-refused.md diff --git a/.changeset/24309-approval-current-step-label.md b/.changeset/pre/24309-approval-current-step-label.md similarity index 100% rename from .changeset/24309-approval-current-step-label.md rename to .changeset/pre/24309-approval-current-step-label.md diff --git a/.changeset/24314-extension-no-injected-columns.md b/.changeset/pre/24314-extension-no-injected-columns.md similarity index 100% rename from .changeset/24314-extension-no-injected-columns.md rename to .changeset/pre/24314-extension-no-injected-columns.md diff --git a/.changeset/24316-formula-hop-family-refusal.md b/.changeset/pre/24316-formula-hop-family-refusal.md similarity index 100% rename from .changeset/24316-formula-hop-family-refusal.md rename to .changeset/pre/24316-formula-hop-family-refusal.md diff --git a/.changeset/24326-main-region-width-refused.md b/.changeset/pre/24326-main-region-width-refused.md similarity index 100% rename from .changeset/24326-main-region-width-refused.md rename to .changeset/pre/24326-main-region-width-refused.md diff --git a/.changeset/24327-runtime-this-system-copy.md b/.changeset/pre/24327-runtime-this-system-copy.md similarity index 100% rename from .changeset/24327-runtime-this-system-copy.md rename to .changeset/pre/24327-runtime-this-system-copy.md diff --git a/.changeset/24328-history-source-describe.md b/.changeset/pre/24328-history-source-describe.md similarity index 100% rename from .changeset/24328-history-source-describe.md rename to .changeset/pre/24328-history-source-describe.md diff --git a/.changeset/24338-object-default-row-scope.md b/.changeset/pre/24338-object-default-row-scope.md similarity index 100% rename from .changeset/24338-object-default-row-scope.md rename to .changeset/pre/24338-object-default-row-scope.md diff --git a/.changeset/24343-8b0c-activity-never-null.md b/.changeset/pre/24343-8b0c-activity-never-null.md similarity index 100% rename from .changeset/24343-8b0c-activity-never-null.md rename to .changeset/pre/24343-8b0c-activity-never-null.md diff --git a/.changeset/24343-producers-name-organization.md b/.changeset/pre/24343-producers-name-organization.md similarity index 100% rename from .changeset/24343-producers-name-organization.md rename to .changeset/pre/24343-producers-name-organization.md diff --git a/.changeset/24348-import-control-opt-out-gate.md b/.changeset/pre/24348-import-control-opt-out-gate.md similarity index 100% rename from .changeset/24348-import-control-opt-out-gate.md rename to .changeset/pre/24348-import-control-opt-out-gate.md diff --git a/.changeset/24348-import-opt-out-describes.md b/.changeset/pre/24348-import-opt-out-describes.md similarity index 100% rename from .changeset/24348-import-opt-out-describes.md rename to .changeset/pre/24348-import-opt-out-describes.md diff --git a/.changeset/24349-page-walk-panel-children.md b/.changeset/pre/24349-page-walk-panel-children.md similarity index 100% rename from .changeset/24349-page-walk-panel-children.md rename to .changeset/pre/24349-page-walk-panel-children.md diff --git a/.changeset/24358-family-hydration-refusal.md b/.changeset/pre/24358-family-hydration-refusal.md similarity index 100% rename from .changeset/24358-family-hydration-refusal.md rename to .changeset/pre/24358-family-hydration-refusal.md diff --git a/.changeset/24360-metadata-protocol-this-system.md b/.changeset/pre/24360-metadata-protocol-this-system.md similarity index 100% rename from .changeset/24360-metadata-protocol-this-system.md rename to .changeset/pre/24360-metadata-protocol-this-system.md diff --git a/.changeset/24360-objectql-this-system.md b/.changeset/pre/24360-objectql-this-system.md similarity index 100% rename from .changeset/24360-objectql-this-system.md rename to .changeset/pre/24360-objectql-this-system.md diff --git a/.changeset/24360-platform-objects-this-system.md b/.changeset/pre/24360-platform-objects-this-system.md similarity index 100% rename from .changeset/24360-platform-objects-this-system.md rename to .changeset/pre/24360-platform-objects-this-system.md diff --git a/.changeset/24366-storage-gate-credential.md b/.changeset/pre/24366-storage-gate-credential.md similarity index 100% rename from .changeset/24366-storage-gate-credential.md rename to .changeset/pre/24366-storage-gate-credential.md diff --git a/.changeset/24368-services-this-system-copy.md b/.changeset/pre/24368-services-this-system-copy.md similarity index 100% rename from .changeset/24368-services-this-system-copy.md rename to .changeset/pre/24368-services-this-system-copy.md diff --git a/.changeset/24371-data-read-one-overlay-read.md b/.changeset/pre/24371-data-read-one-overlay-read.md similarity index 100% rename from .changeset/24371-data-read-one-overlay-read.md rename to .changeset/pre/24371-data-read-one-overlay-read.md diff --git a/.changeset/24384-platform-objects-org-admin-reach.md b/.changeset/pre/24384-platform-objects-org-admin-reach.md similarity index 100% rename from .changeset/24384-platform-objects-org-admin-reach.md rename to .changeset/pre/24384-platform-objects-org-admin-reach.md diff --git a/.changeset/24384-spec-org-reach-rows.md b/.changeset/pre/24384-spec-org-reach-rows.md similarity index 100% rename from .changeset/24384-spec-org-reach-rows.md rename to .changeset/pre/24384-spec-org-reach-rows.md diff --git a/.changeset/24387-port-zero-busy-warning.md b/.changeset/pre/24387-port-zero-busy-warning.md similarity index 100% rename from .changeset/24387-port-zero-busy-warning.md rename to .changeset/pre/24387-port-zero-busy-warning.md diff --git a/.changeset/24404-cloud-connection-this-system.md b/.changeset/pre/24404-cloud-connection-this-system.md similarity index 100% rename from .changeset/24404-cloud-connection-this-system.md rename to .changeset/pre/24404-cloud-connection-this-system.md diff --git a/.changeset/24404-mcp-this-system.md b/.changeset/pre/24404-mcp-this-system.md similarity index 100% rename from .changeset/24404-mcp-this-system.md rename to .changeset/pre/24404-mcp-this-system.md diff --git a/.changeset/24409-migrate-boot-no-dev-seed.md b/.changeset/pre/24409-migrate-boot-no-dev-seed.md similarity index 100% rename from .changeset/24409-migrate-boot-no-dev-seed.md rename to .changeset/pre/24409-migrate-boot-no-dev-seed.md diff --git a/examples/app-crm/CHANGELOG.md b/examples/app-crm/CHANGELOG.md index 6a9e470c9a3..cc9508f3e86 100644 --- a/examples/app-crm/CHANGELOG.md +++ b/examples/app-crm/CHANGELOG.md @@ -1,5 +1,67 @@ # @objectstack/example-crm +## 4.0.100-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [8eccf51] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/service-i18n@18.0.0-next.4 + ## 4.0.100-next.3 ### Patch Changes diff --git a/examples/app-crm/package.json b/examples/app-crm/package.json index a7831dfb4f9..47208e54508 100644 --- a/examples/app-crm/package.json +++ b/examples/app-crm/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/example-crm", - "version": "4.0.100-next.3", + "version": "4.0.100-next.4", "description": "Minimal CRM example \u2014 a smoke-test workspace that exercises the metadata loading pipeline (objects \u2192 views \u2192 app \u2192 dashboard \u2192 hook \u2192 flow \u2192 seed). For a full-featured enterprise CRM see https://github.com/objectstack-ai/hotcrm.", "license": "Apache-2.0", "private": true, diff --git a/examples/app-multi-package/CHANGELOG.md b/examples/app-multi-package/CHANGELOG.md index 4923470e0af..55ecacf085b 100644 --- a/examples/app-multi-package/CHANGELOG.md +++ b/examples/app-multi-package/CHANGELOG.md @@ -1,5 +1,46 @@ # @objectstack/example-multi-package +## 0.0.7-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + ## 0.0.7-next.3 ### Patch Changes diff --git a/examples/app-multi-package/package.json b/examples/app-multi-package/package.json index 1c6a9df8da7..a5ca23c6c63 100644 --- a/examples/app-multi-package/package.json +++ b/examples/app-multi-package/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/example-multi-package", - "version": "0.0.7-next.3", + "version": "0.0.7-next.4", "description": "One release artifact carrying TWO packages that share a namespace (ADR-0130 D4) — the producer-side fixture for `packages[]`", "license": "Apache-2.0", "private": true, diff --git a/examples/app-showcase/CHANGELOG.md b/examples/app-showcase/CHANGELOG.md index 412a511098a..1c575704e42 100644 --- a/examples/app-showcase/CHANGELOG.md +++ b/examples/app-showcase/CHANGELOG.md @@ -1,5 +1,84 @@ # @objectstack/example-showcase +## 0.3.22-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [d9a749b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [a237d7f] +- Updated dependencies [1fee13d] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [8692a08] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [2bcaefb] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [6973014] +- Updated dependencies [669296b] +- Updated dependencies [8eccf51] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] +- Updated dependencies [71fa166] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/service-print@18.0.0-next.4 + - @objectstack/cloud-connection@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/connector-mcp@18.0.0-next.4 + - @objectstack/connector-openapi@18.0.0-next.4 + - @objectstack/connector-rest@18.0.0-next.4 + - @objectstack/connector-slack@18.0.0-next.4 + - @objectstack/driver-sql@18.0.0-next.4 + - @objectstack/service-datasource@18.0.0-next.4 + - @objectstack/service-i18n@18.0.0-next.4 + ## 0.3.22-next.3 ### Patch Changes diff --git a/examples/app-showcase/package.json b/examples/app-showcase/package.json index e18da2d81a2..94b3522c61d 100644 --- a/examples/app-showcase/package.json +++ b/examples/app-showcase/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/example-showcase", - "version": "0.3.22-next.3", + "version": "0.3.22-next.4", "description": "Kitchen-sink showcase workspace — exercises every metadata type, every view type, every chart type, and the major end-to-end capability chains (security, automation, analytics). Built for demonstration, debugging, and coverage-driven verification.", "license": "Apache-2.0", "private": true, diff --git a/examples/app-todo/CHANGELOG.md b/examples/app-todo/CHANGELOG.md index 00d89887eb8..8e3670a53e5 100644 --- a/examples/app-todo/CHANGELOG.md +++ b/examples/app-todo/CHANGELOG.md @@ -1,5 +1,85 @@ # @objectstack/example-todo +## 4.0.100-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [2ac0616] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [8eccf51] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [ec749fb] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [71fa166] + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/client@18.0.0-next.4 + - @objectstack/mcp@18.0.0-next.4 + - @objectstack/driver-sqlite-wasm@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/knowledge-memory@18.0.0-next.4 + - @objectstack/service-i18n@18.0.0-next.4 + - @objectstack/service-knowledge@18.0.0-next.4 + ## 4.0.100-next.3 ### Patch Changes diff --git a/examples/app-todo/package.json b/examples/app-todo/package.json index 6fc799449c0..3cec91b7b0c 100644 --- a/examples/app-todo/package.json +++ b/examples/app-todo/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/example-todo", - "version": "4.0.100-next.3", + "version": "4.0.100-next.4", "description": "Example Todo App using ObjectStack Protocol", "license": "Apache-2.0", "private": true, diff --git a/examples/embed-objectql/CHANGELOG.md b/examples/embed-objectql/CHANGELOG.md index c24159702e5..666e80712e1 100644 --- a/examples/embed-objectql/CHANGELOG.md +++ b/examples/embed-objectql/CHANGELOG.md @@ -1,5 +1,57 @@ # @objectstack/example-embed-objectql +## 0.0.40-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [ec749fb] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/driver-memory@18.0.0-next.4 + ## 0.0.40-next.3 ### Patch Changes diff --git a/examples/embed-objectql/package.json b/examples/embed-objectql/package.json index 91d4b3164c1..f86781cf51c 100644 --- a/examples/embed-objectql/package.json +++ b/examples/embed-objectql/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/example-embed-objectql", - "version": "0.0.40-next.3", + "version": "0.0.40-next.4", "private": true, "description": "Embed the ObjectQL engine as a plain library via @objectstack/objectql/core — no kernel, no plugins, no metadata protocol (ADR-0076).", "type": "module", diff --git a/packages/adapters/hono/CHANGELOG.md b/packages/adapters/hono/CHANGELOG.md index 8d37f72b3f6..5fd97d42c42 100644 --- a/packages/adapters/hono/CHANGELOG.md +++ b/packages/adapters/hono/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/hono +## 18.0.0-next.4 + +### Patch Changes + +- 9822b05: fix(hono): on a `createHonoApp` host a `HEAD` is answered as its `GET` (no body), as the standalone server answers it; `HEAD /api/v1/mcp/skill` no longer answers `405` with `Allow: GET` + + Clause-②: no + + `createHonoApp`'s `${prefix}/*` catch-all handed the dispatcher the request's raw verb. Hono answers a `HEAD` from the `GET` route, but the verb still read `HEAD`, and the dispatcher's domains decide by comparing it with `GET`, so a `HEAD` was not answered as its `GET` on this host. Measured: `HEAD /api/v1/mcp/skill` answered `405 METHOD_NOT_ALLOWED` with `Allow: GET`; `/health`, `/ready` and the scoped root `/environments/:id` answered `404 ROUTE_NOT_FOUND`; signed in, `/analytics/meta` answered `404` and `/ai/agents` `501`; anonymous, the public `/share-links/:token/resolve` answered `401` where `GET` answers `501`. With an auth service, `HEAD /auth/config` answered `404` (it was forwarded to better-auth instead of answered as the `GET`). + + - **`@objectstack/hono`.** The catch-all now dispatches a `HEAD` as `GET`, and the `/auth/config` branch answers a `HEAD` as its `GET`. Every dispatcher route answers `HEAD` with its `GET` answer's status and headers, and Hono drops the body. A path that serves no `GET` keeps its `GET` answer for `HEAD` (its `405` and `Allow`, or `401` to an anonymous caller). The standalone server (`HonoServerPlugin` with the dispatcher plugin) already answered `HEAD` this way. Only the verb changes: a domain that forwards the request (the `/mcp` transport, the `/approvals/act` page) is still handed the client's `HEAD` request. + - **`@objectstack/runtime`.** The `/approvals/act` domain decides whether a request is a `HEAD` from the forwarded request's own method, not from the dispatcher's verb. So a `HEAD` still reaches the approvals member as a `GET` and is answered with no body. +- 1ef0ee8: fix(hono): on a `createHonoApp` host, `GET /api/v1/mcp/skill` answers the raw markdown with one `Content-Type: text/markdown; charset=utf-8` and `Cache-Control: no-store`, as the standalone server does + + Clause-②: no + + - **What a caller got.** The skill is served as a single string event on the dispatcher's `result` stream channel. The adapter's stream writer framed every event outside a Vercel data stream as `data: `, so the body was an SSE frame of a JSON-encoded string (`data: "---\nname: …"`), not markdown. It also set its own `Content-Type` and `Cache-Control` beside the domain's, so the answer carried `text/markdown; charset=utf-8, text/markdown; charset=utf-8` and `no-cache, no-store` (on a socket: two `Content-Type` lines and two `Cache-Control` lines). + - **What it answers now.** A string event is written as given, in every stream mode, and a header the producer sets replaces the adapter's default of the same name, whatever its case. Object events are still one `data:` frame each (one JSON line in a Vercel data stream), and a stream that sets no headers still gets `text/event-stream`, `no-cache` and `keep-alive`. + - **Unchanged:** the AI chat streams, which reach the same writer as pre-encoded Vercel data stream frames or as object parts, and every other route. +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [5a9a80b] +- Updated dependencies [8eccf51] +- Updated dependencies [1bf5f6e] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [d79dc48] +- Updated dependencies [51ad8e4] +- Updated dependencies [c4526a7] +- Updated dependencies [6868514] +- Updated dependencies [2a1e48a] + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/plugin-hono-server@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/adapters/hono/package.json b/packages/adapters/hono/package.json index 9dfb35bed50..16f1e6ce6f0 100644 --- a/packages/adapters/hono/package.json +++ b/packages/adapters/hono/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/hono", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/apps/account/CHANGELOG.md b/packages/apps/account/CHANGELOG.md index 3ceb559a377..30efb9f987b 100644 --- a/packages/apps/account/CHANGELOG.md +++ b/packages/apps/account/CHANGELOG.md @@ -1,5 +1,54 @@ # @objectstack/account +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/apps/account/package.json b/packages/apps/account/package.json index 814f28a877f..21e1037d15e 100644 --- a/packages/apps/account/package.json +++ b/packages/apps/account/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/account", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack Account — the end-user account/self-service console app, packaged as its own ObjectStack app package (ADR-0048: one app per package).", "main": "dist/index.js", diff --git a/packages/apps/setup/CHANGELOG.md b/packages/apps/setup/CHANGELOG.md index 5d28f74128d..f6aabfb3e5a 100644 --- a/packages/apps/setup/CHANGELOG.md +++ b/packages/apps/setup/CHANGELOG.md @@ -1,5 +1,54 @@ # @objectstack/setup +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/apps/setup/package.json b/packages/apps/setup/package.json index b1509f799b1..0930e8f51c8 100644 --- a/packages/apps/setup/package.json +++ b/packages/apps/setup/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/setup", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack Setup — the platform administration app, packaged as its own ObjectStack app package (ADR-0048: one app per package).", "main": "dist/index.js", diff --git a/packages/apps/studio/CHANGELOG.md b/packages/apps/studio/CHANGELOG.md index 5768b6def85..d61c2d1c7e9 100644 --- a/packages/apps/studio/CHANGELOG.md +++ b/packages/apps/studio/CHANGELOG.md @@ -1,5 +1,54 @@ # @objectstack/studio +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/apps/studio/package.json b/packages/apps/studio/package.json index f127a50e1ba..1f115b34fde 100644 --- a/packages/apps/studio/package.json +++ b/packages/apps/studio/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/studio", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack Studio — the metadata builder app, packaged as its own ObjectStack app package (ADR-0048: one app per package).", "main": "dist/index.js", diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index f4c7dbee6c3..84f60313125 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,334 @@ # @objectstack/cli +## 18.0.0-next.4 + +### Minor Changes + +- 78652c0: feat(cli,plugin-sharing)!: in the 17 → 18 ownership ceremony, a row whose owner cannot be derived blocks certification, and a declared-rule mirror's grants are renamed only onto a rule plugin-sharing serves + + Clause-②: yes (narrowing) + + + + **BREAKING** (narrowings), shipped as `minor` under the launch-window convention for breaking changes. + + - **FROM** (`@objectstack/cli`): `os migrate organization-ownership --apply` on a database holding a row whose owner cannot be derived exited `0` with `blocking: 0`, counted the row in `advisory`, and verified the `sys_migration` marker. This covers a reported (fate 4) row, and a row whose anchor is missing under a wall (fate 3). **TO:** it exits `1`, counts the row in `blocking`, and leaves the marker unverified, so a v18 `os serve` / `os start` does not start on the database. **Fix:** give each row its organization (write its `organization_id`, or repair the anchor its reason names), or delete it by hand, then re-run `--apply` with the same plan file. + - **FROM** (`@objectstack/cli`): a database an earlier version of this command certified while it held such a row was certified again by the next `--apply`. **TO:** that run leaves its marker unverified, for the same rows. **Fix:** the same as above. + - **FROM** (`@objectstack/cli`): `os dev`'s in-place upgrade of a development database started the server again whenever every ceremony step finished. **TO:** it starts it only when the post-check also holds nothing (`blocking` 0); otherwise it prints each table that holds, with its count and remedy, and the command that resumes the run. **Fix:** resolve the rows it names, then run the printed command. + - **FROM** (`@objectstack/cli`): the `mirror-deletion` step renamed the grants of a declared-rule mirror onto its rule's name, and deleted the mirror, even when plugin-sharing does not serve that rule (a condition that does not translate, an unmappable recipient, a retired owner-based rule); the next boot's orphaned-grant sweep then revoked those grants. **TO:** while `sys_record_share` grants name such a mirror, the step blocks, naming the rule, its rows and the reason, and writes nothing. **Fix:** correct the rule's declaration in its package so it is served, or delete each mirror row and the grants that name it, then re-run `--apply`. + + `@objectstack/cli` — **unattributable rows hold certification (ADR-0131 D10 fate 4).** The ceremony still never guesses an owner for such a row and never deletes it. The post-check now holds each one against the verdict: + + - Each table that holds one carries `held: { rows, remedy }` in the apply report, and counts those rows in its `remaining`. The terminal output names each table, its row count and the remedy. + - `advisory` now counts only the rows of an advisory departure and the reported rows of a table whose `organization_id` is not the tenancy anchor (`sys_sso_provider`, the retired `sys_scim_provider`). A row there that is bound to no organization is legitimate, is reported, and does not block. + - So a verified marker means no organization-less row on any table that the tenancy anchor governs. + - The plan's summary line says that such rows will hold `--apply`'s certification until they are resolved. + + `@objectstack/cli` — **the ceremony asks plugin-sharing which declared sharing rules it serves.** The port the `mirror-deletion` step reads (`DeclaredSharingRulesPort.rules()`, formerly `names()`) judges each declared rule through plugin-sharing's own translation. A mirror's grants are renamed only onto a rule that is served. A mirror of a refused rule that no grant names is still deleted. + + `@objectstack/plugin-sharing` — **exports `translateDeclaredSharingRule`** with its `DeclaredSharingRule` and `DeclaredSharingRuleSkip` types. It is the one translation enforcement serves a declared rule through. It is pure, and it is unchanged. +- 5e97907: feat(plugin-sharing,cli)!: declared sharing rules are read from the registry — the boot writes no `sys_sharing_rule` row for them, and the 17 → 18 ownership ceremony deletes the copies the retired seeder wrote + + Clause-②: no (narrowing) + + + + The narrowings, FROM → TO, each with its one-line remedy: + + - `DELETE /api/v1/sharing/rules/:idOrName` on a declared rule — FROM `204`, the seeded row deleted (and written again by the next boot), TO `403 PERMISSION_DENIED`. Remedy: change or remove the rule in its declaration in the package; an organization's own rule is deleted as before. + - `sys_sharing_rule` — FROM one `managed_by: package` row per declared rule, TO none: the boot writes none and the ownership ceremony deletes the copies the seeder wrote (Setup's Sharing Rules object list, which lists rows, no longer shows declared rules). Remedy: read declared rules through `GET /api/v1/sharing/rules`, which lists them beside the organization's own; on an upgraded database run `os migrate organization-ownership --apply` with a fresh plan. + + The retirement note for the seeder is the ADR-0087 semantic entry `sharing-rule-declared-mirror-retired`. + + **BREAKING**, shipped as `minor` under the repo's launch-window convention for breaking changes. + + `@objectstack/plugin-sharing` no longer copies a stack's declared `sharingRules` into `sys_sharing_rule` at boot. ADR-0131 D2 retires that seeder (`bootstrapDeclaredSharingRules`). The sharing-rule service reads each declared rule from the registry whenever it reads the rule set: listing, getting, evaluating, the per-record hooks, the boot backfill and the orphaned-grant sweep. Rules an organization authors keep their `sys_sharing_rule` rows. + + - **A declared rule still grants, and stops granting when its declaration goes.** It is translated from the registry's copy exactly as the seeder translated it: the CEL `condition` is lowered to a `criteria` filter, and `sharedWith` becomes the recipient. A condition that does not lower is not served, and never as a match-all. It says so in one WARN line per rule per process. A declared rule's id is its NAME, and the grants it materialises carry the name as `sys_record_share.source_id`. Remove the declaration and the next boot's orphaned-grant sweep revokes those grants. + - **Scopes follow the posture, as the seeder's passes did.** Under `single` a declared rule is served once, organization-less, so every organization reads it. Under a wall it is served once per organization — every organization, read page by page. An organization created after boot is served from its creation on. If the organizations cannot be read at boot, no declared rule is served until the next boot, and the boot's orphaned-grant sweep revokes none of their grants meanwhile: a failed read is never a revocation. + - **A stored row of the same name answers for it.** Under `single` that is a row of that name in any organization, and under a wall that organization's own row. Such a row is an organization's own rule, a seeded copy an administrator customized, or a seeded copy the ceremony has not deleted yet. The declared rule is not served beside it, the same as while the seeder ran: it never overwrote an admin-authored or customized row. + - **`DELETE /api/v1/sharing/rules/:idOrName` refuses a declared rule** with `403 PERMISSION_DENIED`. It has no row: it is changed or removed in its declaration. Before this release a platform administrator could delete the seeded row, and the next boot wrote it again. An organization's own rule is deleted as before. + - **Setup's Sharing Rules object list no longer shows declared rules.** It lists `sys_sharing_rule` rows. `GET /api/v1/sharing/rules` lists both. + + `@objectstack/cli` — **`os migrate organization-ownership --apply` deletes the declared-rule mirrors.** The `declared-rule-mirrors` rows on `sys_sharing_rule` are the seeder's uncustomized copies (`managed_by` `package` or `platform`, `customized` not true). They now block certification until the ceremony's `mirror-deletion` step removes them. It works in the ruled order. It renames every grant naming a mirror by row id (`sys_record_share.source_id`, `source = 'rule'`) to the rule's name and verifies that no grant names a mirror any more. Only then does it delete the mirrors. Under a wall it deletes the organization-stamped copies too. + - A mirror whose rule no installed package declares, and that grants still name, blocks with nothing written. The blocker names the rows and the remedy. A mirror no grant names is deleted. + - The step also runs on a database whose ceremony already completed while an earlier release still seeded the rows, where they were advisory. Re-run `--apply` with a fresh plan. + - `DECLARED_SHARING_RULE_SEEDER_RUNS` is now `false`. A customized copy and an organization's own rule stay. Under `single` they take the Default Organization, as before. + + `@objectstack/lint` — the sharing-rule enforceability verdicts and `os explain` say what now happens to a declared rule whose condition does not lower. It is "never served", not "never seeded", because it is never written to `sys_sharing_rule`. The rule ids, severities, paths and what each rule accepts or refuses are unchanged. + +### Patch Changes + +- 1eae21f: fix(cli): `os verify --dashboard-windows` describes the measures it proves: counts, sums and derived measures + + Clause-②: no (the flag, its default and what it accepts are unchanged; only its help text moves) + + The `--dashboard-windows` flag description ended "(date and datetime fields; count measures)". The date-window family in `@objectstack/verify` now also proves sums and derived measures over counts or sums, so it reads FROM "(date and datetime fields; count measures)" TO "(date and datetime fields; count, sum and derived measures)". Nothing else about the flag changes. +- 7d6ec91: Clause-②: no + + fix(cli): `os start` with the stdio MCP transport answers the `initialize` an MCP client writes when it spawns the server, and boots the configured app + + - **What was wrong.** An MCP client writes `initialize` to the server's stdin as soon as it spawns it. `os start` hands its stdin down to the processes it starts, and the CLI's argument parser fills an omitted positional argument from a pending stdin line. So the client's `initialize` became a config path. In a project with no `dist/objectstack.json`, the automatic compile step failed with `Config file not found: …/{"jsonrpc":"2.0",…}` and `os start` exited 1. In a project with a compiled artifact, the server printed `No objectstack.config.ts found — booting from artifact`, booted without the config's own plugins, and never answered `initialize`. + - **What changed.** The automatic compile step now gets no stdin, and `os start` names the config path when it starts `os serve` (the same `objectstack.config.ts` in the working directory that `os serve` used by default). The client's first message stays in the pipe for the stdio transport, and the config is loaded. + - **Unchanged:** the flags and environment variables of `os start`, `os serve` and `os compile`, and which config and artifact a boot uses. +- d3d0644: fix(cli): `os migrate plan` no longer lists `sys_packages` among the tables "declared by no object in this plan" after `os migrate apply` has created it + + Clause-②: no + + `sys_packages` is the package registry's table. It is not an object: `PackageServicePlugin` creates it with its own DDL, and `os migrate apply` creates it from the registry's `package.storage` declaration. The plan's unmanaged-table sweep built its known set from the planned drivers' managed tables and the declared objects only, so on every applied deployment the plan reported the live registry table as if it were stranded. + + - **Now:** the sweep also counts the tables the boot's composition declares outside the object set. It reads that name from the same `package.storage` declaration the plan creates the table from. It reads it on every boot, whether or not the table exists yet, the skip-schema-sync boot of a Turso remote primary included. On `examples/app-crm` after `os migrate apply --yes`, the plan lists only a genuinely stranded `sys_*` table. + - **Still reported:** a `sys_packages` table in the database of a deployment that composes no package registry. Nothing there declares it. + - **Refused in one more case:** a `package.storage` service whose shape this CLI does not read now fails `plan`, `apply` and `organization-ownership` on a skip-schema-sync boot too, with the error those commands already gave on every other boot: `@objectstack/cli` and `@objectstack/service-package` must come from the same release. + - **Unchanged:** the `--json` keys, the section's wording, and what `apply` creates. +- d702b18: fix(cli,runtime): a dangling symlink standing as the SQLite database's directory is refused at once by `os dev` and `os serve`, naming the database file and the directory, instead of failing the boot later in a plugin's start + + Clause-②: no + + **What was wrong.** `os dev` (for its default database under `OS_HOME/data`) and the standalone host behind `os serve` and `os start` (for a `file:` or `wasm-sqlite://` database URL) make the database file's directory before the driver opens it. Both accepted any `EEXIST` answer while making a missing level. A symbolic link that points nowhere answers `EEXIST` to `mkdir`, so it was taken for a directory, and the boot went on until the driver could not open the file. `os dev` then exited 1 with `Plugin com.objectstack.auth failed to start - rollback complete: The database refused to run this query for object 'sys_organization'`, and `os serve` with `Plugin package-service failed to start - rollback complete: The database refused to run a raw statement`. Neither message named the file, the directory or the link. + + **What an operator sees now.** Both make the directory with the same bounded walk as the other `OS_HOME` paths, which accepts `EEXIST` only when a directory stands there. A dangling link as the directory is refused before the boot starts: `os dev` prints `The default database '…/data/objectstack.db' cannot be created: its directory '…/data' could not be made: EEXIST: …` and exits 1, and `os serve` prints `[StandaloneStack] SQLite database file '…' cannot be created: its directory '…' could not be made: EEXIST: …` and exits 1. Remove the link, or point it at a directory that exists. A dangling link higher up the path was already refused at once, with `ENOENT` at the level below the link; it is now refused with `EEXIST` at the link itself. + + Nothing else changes. A directory that can be made is still made, every missing level of it; a directory that exists, or a symbolic link to one, is used as it stands; and a path the operating system refuses for any other reason (`ENOENT`, `ENOTDIR`, a regular file holding the directory's name) is refused with the same code and text as before. +- 7715d04: `os serve` says once when it cannot write its runtime state file, naming the file and the operating system's error, and goes on booting + + Clause-②: no + + - **Before:** when `runtime...json` could not be written — an `OS_HOME` under `/proc`, or one below a regular file (`ENOTDIR`) — the write's catch printed nothing. The boot printed its ready banner, an `os dev` parent got its `objectstack:listening` message, and a supervisor that then read the file got `ENOENT` with no reason anywhere. + - **Now:** one warning on stderr, among the other boot warnings and before the banner, names the file it could not write and carries the operating system's error verbatim (for example `ENOTDIR: not a directory, stat '…'`). It says the server runs on without the file and that `OS_HOME` must point at a directory the process can write. + - **Unchanged:** the file stays best-effort, so the boot never fails on it; its name, its payload and its removal on exit; the banner and the IPC message; stdout, which stays the stdio MCP transport's alone. +- f963d8e: fix(cli): `os migrate plan` and `os migrate apply` examine the objects a `datasourceMapping` rule routes to a declared datasource, the declared default included, in that datasource, as `os serve` connects it (#24120) + + Clause-②: no + + A project whose primary store is a declared datasource routed as the default (`datasources: [{ name: 'qa_main', … }]` with `datasourceMapping: [{ default: true, datasource: 'qa_main' }]`) got a plan that examined nothing. The plan resolved `qa_main`'s database as its primary, but it registered no driver under the name `qa_main`, so every object the rule routes there, platform objects included, was bound to no driver. The plan printed `Examined 0 managed table(s)` and a PARTIAL plan. Meanwhile `os dev` on the same project reported drift in that database and named `os migrate` as the remedy. + + - **Now:** `plan` and `apply` connect the project's declared **managed** datasources the way `os serve` connects them: through the same `datasource-connection` service, under the declared name, for the datasources a mapping rule routes objects to, an object binds to, or that set `autoConnect`. Each datasource that objects live in is diffed and applied beside the primary. A composition note names each one and its database, in both the human output and the `--json` output. On a `datasourceMapping`-default project, the plan's drift is the drift `os dev` reports there. + - **Not connected:** a datasource whose schema the remote owns (`schemaMode: 'external'` or `'validate-only'`). Its objects are federated, and a plan has no table on it. + - **Refused now:** a routed managed datasource that cannot connect fails `plan`, `apply` and `unmapped-columns` with the connect error `os serve` gives for it. It is no longer skipped with a PARTIAL plan. `OS_ALLOW_DRIVER_CONNECT_FAILURE` degrades it here as it does there. + - **`os migrate unmapped-columns` follows:** it filters the plan's own findings, so it composes the same datasources. On such a project it reads an object's unmapped columns from the declared datasource. Before, it refused with "not in the set os migrate plan diffs: it is bound to no driver". + - **Unchanged:** a project that declares no datasource gets the plan it got before. `os migrate security-catalog-overlays` and `organization-ownership` boot as before. The unmanaged-table sweep's `physicalTables` counts each database once, although the primary and a declared default resolved from it are two connections to one file. +- b7f29dd: Clause-②: no + + fix(cli): `os serve` with the stdio MCP transport and no config argument answers the `initialize` an MCP client writes when it spawns the server, and boots the configured app + + - **What was wrong.** An MCP client writes `initialize` to the server's stdin as soon as it spawns it. The CLI's argument parser fills an omitted positional argument from a pending stdin line, and `os serve`'s config path is an optional positional. So, started without a config path, `os serve` took the client's `initialize` as its config path. In a project with a compiled `dist/objectstack.json`, it printed `No objectstack.config.ts found — booting from artifact`, booted without the config's own plugins, and never answered `initialize`. `os start` already named the path when it started `os serve`; this is the same defect for `os serve` started directly. + - **What changed.** `os serve` no longer reads its config path from stdin. Without the argument it uses `objectstack.config.ts` in the working directory, as its help says, and the client's first message stays in the pipe for the stdio transport. Reading the config path from stdin was never documented, and no script, test or doc in this repository relied on it; to serve another config, pass its path as the argument. + - **Unchanged:** the flags and environment variables of `os serve`, and which config and artifact a boot uses when the path is given or when nothing waits on stdin. +- a8adf66: Clause-②: no + + fix(cli): on a first run, `os start` writes its compile step's progress to stderr, so stdout carries only the stdio MCP transport's JSON-RPC frames + + - **What was wrong.** With `OS_MCP_STDIO_ENABLED=true`, stdout is the stdio MCP transport's protocol channel, and an MCP client parses every line it reads there as JSON-RPC. `os start` already writes its own banner and progress to stderr, but in a project with no `dist/objectstack.json` it compiles the config first, and that compile step wrote its progress (`◆ Compile`, `✓ Build complete`, `Artifact: …`, 24 lines for a one-object app) to stdout, ahead of the answer to the client's `initialize`. + - **What changed.** The automatic compile step's output now goes to stderr, next to the rest of `os start`'s own output, with or without the stdio transport. Nothing is dropped: the same lines still print, on stderr. + - **Unchanged:** the flags and environment variables of `os start` and `os compile`, when `os start` compiles, the artifact it writes, and `os compile`'s own output when you run it directly. +- 51ad8e4: Clause-②: yes (widening) + + fix(cli,runtime): the 17 → 18 organization-ownership upgrade of a single-posture database that holds no organization leaves no row without one, the boot the ceremony prescribes writes nothing before it is refused, and a ceremony that did not verify is never served (ADR-0131 D3, D10) + + A 17.x single-posture install whose seed ran before any organization existed holds rows of organization-owned objects with `organization_id` NULL and an empty `sys_organization`. Upgrading it took this path: the ceremony's attribution checkpointed itself done at zero rows; the boot its rewrite step asks for created the Default Organization and let the inline seed stamp some rows before it refused; the second `--apply` did not run attribution again, dropped the columns and recorded discrepancies with `verified_at` NULL; and `os start` then served that database. + + - **The ceremony creates the Default Organization (`@objectstack/cli`).** Under the `single` posture, on a database that holds no organization and rows no anchor names, `os migrate organization-ownership` plans those rows as the Default Organization's and says so in a plan warning (`defaultOrganizationCreatedByApply: true` in the plan file). `--apply` creates it before its first write, through plugin-auth's boot invariant (the row a single-posture boot creates), and gives it every such row. A run that cannot create it — a boot without the auth family that declares `sys_organization` — blocks at attribution, naming why, instead of attributing nothing and certifying. Nothing is created under a walled posture, or when the database holds organizations. + - **The ceremony names the Default Organization as the boot does (`@objectstack/cli`).** Under `single` it is the organization with `slug = 'default'`, else the sole organization whatever its slug — the boot invariant's and the tenancy service's definition (ADR-0093 D3). Before, the plan required `slug = 'default'`, so a database whose only organization had another slug kept its unanchored rows NULL through the ceremony while v18 derives every owner from that organization. Several organizations, none of them `default`, still name none, and their rows are reported. In the plan file (the command's own output, not a published schema) `defaultOrganization.slug` is now that organization's own slug, no longer always `'default'`. + - **A checkpointed attribution runs again (`@objectstack/cli`)** whenever the plan derives an owner for rows still NULL: a row the operator made attributable since, or one an earlier run found no Default Organization for. It writes only where `organization_id` is NULL, so no owner already written is derived again. A database left by an earlier release's ceremony in that state is completed by re-running `--apply`. + - **The boot guard judges before any plugin writes (`@objectstack/runtime`, `@objectstack/plugin-auth`).** `OrganizationOwnershipBootGuardPlugin` judges in its own `start()`, after the engine's schema sync (the column the rewrite step waits for is still added) and before every plugin that writes rows at start: `AppPlugin` (the inline seed), `AuthPlugin` and the identity-objects plugin (the Default Organization) name it (`com.objectstack.organization-ownership-boot-guard`) in `optionalDependencies`. A refused boot leaves every row as it found it. + - **A ceremony that did not verify is refused (`@objectstack/runtime`).** With no table left in the v17 shape, a boot whose `adr-0131-organization-ownership` marker exists and is not verified — the post-check found rows the ceremony has not carried — is refused with `ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED`, naming the re-run. A database with no marker (a fresh one) boots as before. + - **The refusal now arrives as a `cause` (`@objectstack/runtime`).** A failed `start()` is reported by the kernel as its own error ("Plugin … failed to start"), with the plugin's error as its `cause`. A host that composes the guard and inspects the boot error asks `isOrganizationOwnershipBootRefusal` of each `cause` down the chain: asked of the kernel's own error it now answers `false`. `assertOrganizationOwnershipMigrated(engine)` still throws the refusal itself. `os serve` and `os dev` read it from the chain. + - **One new export (`@objectstack/plugin-auth`, `minor`).** `ensureDefaultOrganizationExists(ql, options?)` — the Default Organization's boot invariant, find-or-create under `single` — for the ceremony, so the organization it creates is the row a single-posture boot creates, from one producer. A host composing a kernel still never calls it: `AuthPlugin` and the identity-objects plugin keep the invariant on every boot. No other package entry changes. + + Unchanged: the ceremony's steps, fates and refusals otherwise; a walled posture's unattributable rows (reported, never assigned); the boot of a fresh or verified database; and every plugin's order on a kernel that does not compose the guard. Repairing a database whose organization-scoped seed load already inserted twins of NULL-organization rows is an operator's data repair. +- 538f923: fix: an activity row is written only when an organization is derivable, never with a NULL organization, and the v18 ownership ceremony deletes the existing NULL activity rows it cannot attribute (ADR-0131 D9) + + Clause-②: no + + - **`@objectstack/plugin-audit`.** The audit writer's activity mirror (`sys_activity`) takes its organization from the record, then from the acting session, then from the `tenancy` service's Default Organization under `single`. When none of them names one, the mirror now writes no activity row at all. Before, it wrote a row with `organization_id` NULL. Under a wall that row was readable by no organization member. The change is still recorded: the compliance ledger row (`sys_audit_log`) is written as before, with its `tenant_id` attribution. Under `single` the Default Organization is always derivable, so nothing changes there. A record that names its organization keeps its activity row, with that organization. + - **`@objectstack/cli`.** `os migrate organization-ownership` still attributes a NULL `sys_activity` row when its record carries an organization. On a walled database (any posture but `single`), the mirror-deletion step now deletes the NULL rows no anchor attributes, such as activity about a sign-up's `sys_user`. Before, those rows were reported as unattributable and held certification. They are 14-day telemetry that the mirror no longer writes, and `sys_audit_log` keeps each change. The plan counts them as the departure `underivable-activity`, and warns before the first write that `--apply` deletes them. The apply report lists the count as the table's `deleted`. Under `single` the Default Organization takes these rows, as before, and nothing is deleted. +- Updated dependencies [b2e7870] +- Updated dependencies [6a49efb] +- Updated dependencies [eebaf3b] +- Updated dependencies [78652c0] +- Updated dependencies [1eae21f] +- Updated dependencies [9c74d40] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [646303e] +- Updated dependencies [e9bc60b] +- Updated dependencies [52ff308] +- Updated dependencies [d1f082d] +- Updated dependencies [f5ef054] +- Updated dependencies [0d34066] +- Updated dependencies [0c3db0f] +- Updated dependencies [8d3dd6f] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [03ac4ec] +- Updated dependencies [5e97907] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [8692a08] +- Updated dependencies [62c4441] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [2bcaefb] +- Updated dependencies [f3174ef] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [a06f0d4] +- Updated dependencies [a06f0d4] +- Updated dependencies [a19b2a7] +- Updated dependencies [9975c26] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [04fd88a] +- Updated dependencies [aaaeeac] +- Updated dependencies [6973014] +- Updated dependencies [669296b] +- Updated dependencies [8eccf51] +- Updated dependencies [8a88901] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8259ea0] +- Updated dependencies [9040945] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [2bcd406] +- Updated dependencies [2bcd406] +- Updated dependencies [58a2faa] +- Updated dependencies [e74ec56] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [77e9f79] +- Updated dependencies [83dfbc0] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [e15897c] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [5da2e88] +- Updated dependencies [51ad8e4] +- Updated dependencies [f5e98a3] +- Updated dependencies [ec749fb] +- Updated dependencies [ec749fb] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [85be049] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [41c7405] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [538f923] +- Updated dependencies [d65826f] +- Updated dependencies [741aa85] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [d8e11b5] +- Updated dependencies [44a5011] +- Updated dependencies [1827571] +- Updated dependencies [f573976] +- Updated dependencies [f573976] +- Updated dependencies [2a1e48a] +- Updated dependencies [71fa166] +- Updated dependencies [71fa166] +- Updated dependencies [e22defb] + - @objectstack/core@18.0.0-next.4 + - @objectstack/plugin-security@18.0.0-next.4 + - @objectstack/plugin-auth@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/plugin-sharing@18.0.0-next.4 + - @objectstack/verify@18.0.0-next.4 + - @objectstack/lint@18.0.0-next.4 + - @objectstack/rest@18.0.0-next.4 + - @objectstack/service-settings@18.0.0-next.4 + - @objectstack/service-storage@18.0.0-next.4 + - @objectstack/plugin-audit@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/cloud-connection@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/plugin-hono-server@18.0.0-next.4 + - @objectstack/metadata-protocol@18.0.0-next.4 + - @objectstack/plugin-email@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/plugin-webhooks@18.0.0-next.4 + - @objectstack/client@18.0.0-next.4 + - @objectstack/plugin-approvals@18.0.0-next.4 + - @objectstack/console@18.0.0-next.4 + - @objectstack/mcp@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/driver-memory@18.0.0-next.4 + - @objectstack/driver-mongodb@18.0.0-next.4 + - @objectstack/driver-sql@18.0.0-next.4 + - @objectstack/driver-sqlite-wasm@18.0.0-next.4 + - @objectstack/driver-turso@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/plugin-pinyin-search@18.0.0-next.4 + - @objectstack/service-analytics@18.0.0-next.4 + - @objectstack/service-automation@18.0.0-next.4 + - @objectstack/service-cache@18.0.0-next.4 + - @objectstack/service-datasource@18.0.0-next.4 + - @objectstack/service-job@18.0.0-next.4 + - @objectstack/service-mailbox@18.0.0-next.4 + - @objectstack/service-messaging@18.0.0-next.4 + - @objectstack/service-package@18.0.0-next.4 + - @objectstack/service-queue@18.0.0-next.4 + - @objectstack/service-realtime@18.0.0-next.4 + - @objectstack/service-sms@18.0.0-next.4 + - @objectstack/trigger-api@18.0.0-next.4 + - @objectstack/trigger-record-change@18.0.0-next.4 + - @objectstack/trigger-schedule@18.0.0-next.4 + - @objectstack/account@18.0.0-next.4 + - @objectstack/setup@18.0.0-next.4 + - create-objectstack@18.0.0-next.4 + - @objectstack/observability@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/cli/package.json b/packages/cli/package.json index cd6a407b93e..7ea71bce742 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/cli", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "description": "Command Line Interface for ObjectStack Protocol", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/packages/client-react/CHANGELOG.md b/packages/client-react/CHANGELOG.md index be281c19ce9..2a4abe28038 100644 --- a/packages/client-react/CHANGELOG.md +++ b/packages/client-react/CHANGELOG.md @@ -1,5 +1,53 @@ # @objectstack/client-react +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/client@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/client-react/package.json b/packages/client-react/package.json index d78cd859406..d3e31de4894 100644 --- a/packages/client-react/package.json +++ b/packages/client-react/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/client-react", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "React hooks for ObjectStack Client SDK", "main": "dist/index.js", diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index 03a9a71ccb7..957751dc84c 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -1,5 +1,59 @@ # @objectstack/client +## 18.0.0-next.4 + +### Minor Changes + +- 1e001c7: feat(client): `packages.publishDrafts(id, { backfill })` sends the opt-in backfill of new fields' defaults + + Clause-②: yes (widening) + + - `opts.backfill?: Array<{ object, field }>` is sent in the publish body; the response's `backfillApplied` reports the rows written per field. A field the publish does not add, or one without a literal `defaultValue`, refuses the whole request with a `400` before anything is published. + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/client/package.json b/packages/client/package.json index c51216218ab..593d4d3a5ef 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/client", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Official Client SDK for ObjectStack Protocol", "main": "dist/index.js", diff --git a/packages/cloud-connection/CHANGELOG.md b/packages/cloud-connection/CHANGELOG.md index cd655bd48bc..e60227fb677 100644 --- a/packages/cloud-connection/CHANGELOG.md +++ b/packages/cloud-connection/CHANGELOG.md @@ -1,5 +1,204 @@ # @objectstack/cloud-connection +## 18.0.0-next.4 + +### Minor Changes + +- a237d7f: feat(cloud-connection)!: Setup's "Installed Apps" entry opens Setup › Packages filtered to marketplace installs, and the separate Installed Apps page is no longer shipped + + Clause-②: no (narrowing) + + + + - `MARKETPLACE_INSTALLED_UI_BUNDLE.pages` -> delete (the bundle carries no page; its entry is read from `navigationContributions`) + + Setup › Packages is now the one list of what is installed: every row says where the package came from (Source: code, marketplace or authored), and the rows whose Source is marketplace carry the actions the Installed Apps page had, Details and Uninstall. + + - **What changes.** The "Installed Apps" entry in Setup's Apps group, which the install-local plugin (`MarketplaceInstallLocalPlugin`) contributes, now opens Packages with the Source filter set to marketplace. Its label and icon are unchanged. In `MARKETPLACE_INSTALLED_UI_BUNDLE` the entry `nav_marketplace_installed` is a `component` entry on `developer:packages` with `params: { source: 'marketplace' }`, which the console opens as `/apps/setup/component/developer/packages?source=marketplace`. + - **What is no longer shipped.** The bundle no longer carries the `marketplace_installed` page, so `GET /api/v1/meta/page` stops listing it and `MARKETPLACE_INSTALLED_UI_BUNDLE` has no `pages` key. In the console that page only redirected to the filtered Packages list. The `pages.marketplace_installed` entries leave the Setup translation bundle in `@objectstack/platform-objects` (en, zh-CN, ja-JP, es-ES). The Setup nav label `nav_marketplace_installed` keeps its translations. + - **Unchanged.** The entry still appears only when the install-local plugin is mounted, so a runtime without it shows no Installed Apps entry. "Browse Marketplace" and its route are not touched. The marketplace's own package page still carries Update and the sample-data Re-seed / Purge menu, and Details on a Packages row opens it. + + What to do. Nothing in a normal deployment. If you linked to the `marketplace_installed` page directly, link to Setup › Packages with `?source=marketplace` instead. +- 8692a08: fix(cloud-connection)!: the marketplace `reseed-sample-data` door converts the installed package body forward once, as the install and the rehydrate do, so it reloads the translations those two doors load + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + `POST /api/v1/marketplace/install-local/:manifestId/reseed-sample-data` handed its side effects (the translations it reloads into the i18n service, and the seed datasets it runs) the package body exactly as the install ledger stores it. The install and the boot rehydrate hand the same side effects that body converted forward, through the ADR-0087 window the body's own `engines.protocol` opens (`ObjectQL.convertPackageBodyForward`). So for a package built before a translation retirement, a reseed reloaded a retired translation shape that the other two doors strip. The reseed now converts the body once, through the same entry point, after the protocol handshake has judged the entry as stored. + + - **Served less now:** for a package whose body declares a range this runtime admits, from a floor that predates a retirement, a reseed no longer reloads the translation shapes that the four translation conversions remove (`translation-per-app-settings-removed`, `translation-validation-messages-removed`, `translation-component-submit-label-removed` and `translation-widget-sub-caption-removed`). For example, a pre-17.4 per-app `settings` group is no longer loaded into the i18n service on reseed. After a reseed the i18n service holds what it holds after the install or a restart. + - **Refused now:** a stored body the converter throws on is no longer reloaded. The reseed raises the converter's error, as the install does, and loads nothing. Before, it reloaded the raw body. The protocol handshake that runs first throws on the same malformed range at every door, so this arises only for a body whose range reads differently to the handshake and to the converter. + - **Unchanged:** a body that declares no range is handed back as stored, so it reloads exactly as before. The handshake still runs first: a range that excludes this runtime is still answered `422 OS_PROTOCOL_INCOMPATIBLE`, before anything is converted or loaded. No conversion touches seed `data`, so a reseed seeds the same rows and answers the same counts. The ledger keeps the body as installed. + + What changes for you: nothing to rewrite. For such a package the install and every restart already log the conversion warning, once per conversion per process, and it names the remedy: rebuild the package with `os build` and republish it. +- 6973014: fix(cloud-connection)!: `bind/start`, `bind/poll` and `unbind` require `setup.access`, and `GET /status` serves the connection's details only to a caller who holds it + + Clause-②: no (narrowing) + + + + **BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The Console's Cloud Connection page sits on the Setup app, which requires the Setup Access permission (`setup.access`). The routes behind that page did not ask for it: the three that change this runtime's cloud connection checked only that the caller was signed in, and `GET /api/v1/cloud-connection/status` served the connection record the control plane reports to every caller. Both now follow the page's rule. + + - **Refused now:** `POST /api/v1/cloud-connection/bind/start`, `POST /api/v1/cloud-connection/bind/poll` and `POST /api/v1/cloud-connection/unbind` answer a signed-in caller who does not hold `setup.access` with `403` and the standard `PERMISSION_DENIED` code, and the message names the capability. The refusal comes before any call to the control plane and before the stored credential is touched. What the caller holds is read through `resolveAuthzContext`, the platform's one authorization resolver, for the session these routes already read: a grant bound through a position, or scoped to the session's active organization, counts as it does everywhere else, and an API key sent beside the session lends its owner's grants to nobody. + - **Narrowed now:** `GET /api/v1/cloud-connection/status` still answers every caller `200`, so a probe can still tell that the plugin is mounted. A caller without `setup.access` (anonymous, signed in without it, or a session the ADR-0069 policy holds) is served `bound`, `runtimeId` and `provider` only. `environmentId` and `connection` are served to a caller who holds it. + - **Unchanged:** a caller who holds `setup.access` is served exactly as before, so the Console panel's connected view, its "Approved by" row included, keeps working for an administrator. An anonymous caller of the three connection-changing routes still gets `401`, and a held session still gets the ADR-0069 `403`. The other four session routes (`install`, `installation`, `installed`, `org-packages`) are not touched. + - **An unreadable permission store is not a denial.** When the grants cannot be read, the three connection-changing routes raise the platform's `SERVICE_UNAVAILABLE` outage (`503` through the host's error render) instead of answering a `403` that reads like a real refusal. `status` keeps answering, withholds the details, and logs the outage. + + What changes for you. Whoever manages this runtime's cloud connection needs Setup Access, which the default platform-administrator and organization-administrator permission sets already carry. A script that reads `connection` or `environmentId` from `GET /status` has to call it as a signed-in user who holds Setup Access. + +### Patch Changes + +- 2bcaefb: Clause-②: no + + fix(cloud-connection): an install-local uninstall takes the package's objects out of `GET /api/v1/meta/object` and `GET /api/v1/meta/object/NAME` at once, not at the next restart + + - **What was wrong.** An install-local install registers the package through the `manifest` service, and that register does two things: it puts the package in the engine's registry, and it copies each of the package's objects into the `metadata` service, stamped with the package's id. `DELETE /api/v1/marketplace/install-local/:manifestId` withdrew the registry half only. The object listing merges the `metadata` service into its answer and the by-name read asks that service first, so both kept serving every object of the uninstalled package, with its `_packageId`, until the runtime restarted, while `GET /api/v1/data/OBJECT` already answered `404`. The console's object pickers and Studio's object list offered objects that no longer existed. + - **What changes.** Right after the registry withdrawal, the DELETE removes from the `metadata` service each object copy stamped with the package's id whose name the registry no longer serves. The object listing and the by-name read stop serving the package's objects in the same process, as its data route, `GET /api/v1/meta/app` and `GET /api/v1/meta/permission` already did. + - **Not touched.** A same-name object the package did not copy there (one without its stamp) stays. The package's tables and rows are untouched, as before. A refused registry withdrawal still leaves the whole package loaded until the next restart, its copies included. + - **A copy the service refuses to remove** is reported on the DELETE answer as a failed `cleanups` entry named `metadata.unregister`, the way a refused registry withdrawal is reported, and the operator log names the objects kept and the remedy: restart the runtime. The uninstall cleanups still run, and the answer is still `200`. When nothing is refused, `cleanups` is unchanged. +- 669296b: fix(cloud-connection): with `OS_RUNTIME_VERSION` unset, the server reports to the cloud the release `GET /api/v1/discovery` reports, not the Cloud Connection plugin's own `0.3.0` + + Clause-②: no + + `CloudConnectionPlugin` sends the server's version to the cloud in two places: as `runtime_version` on the approval links `POST /api/v1/cloud-connection/bind/start` returns, which the cloud's approval page shows next to the server's name, and as `runtime_version` in the registration request `POST /api/v1/cloud-connection/bind/poll` sends to the cloud's `/bind`. With `OS_RUNTIME_VERSION` unset, both carried the plugin's own `version` field, `0.3.0`. That string names the plugin, not the release the server runs, so an approver was shown `0.3.0` while discovery on the same server reported `18.0.0-next.*`. + + - **Unset now:** both places carry the `version` the server's discovery document reports. The plugin reads it from the discovery document itself, through the `protocol` service, each time it builds the approval links or the registration request, so the two answers cannot drift. + - **Set:** a set `OS_RUNTIME_VERSION` is sent verbatim, as before. + - **No discovery document:** on a server with no `protocol` service, or when reading it fails, neither place carries a version. The approval links have no `runtime_version` parameter and the registration request has no `runtime_version` key. Before, both carried `0.3.0`. + + What changes for you: nothing to rewrite. `OS_RUNTIME_VERSION` is now only needed to report a version other than the release, such as an image tag or a commit. +- aa31569: Clause-②: no + + fix(objectql): withdrawing a package that extends another package's object puts the owner's definition back at `GET /api/v1/meta/object/NAME` at once, not at the next restart + + - **What was wrong.** When a package whose content is an `objectExtensions` field on another package's object is registered on a started kernel, the ObjectQL bridge rewrites the OWNER's copy of that object in the `metadata` service with the merged body, extension included. `DELETE /api/v1/marketplace/install-local/:manifestId` of the extender withdrew its contribution from the registry, so the object listing and the data plane dropped the field, but nothing rewrote the owner's copy. The by-name read asks that copy first, so `GET /api/v1/meta/object/NAME` kept serving the withdrawn field until the runtime restarted. + - **What changes.** The ObjectQL bridge remembers which owners' copies each package's extensions rewrote, and the `manifest` service gains `restoreExtendedObjectCopies(packageId)`: once the registry no longer holds the package, it re-registers each such copy with the registry's current definition. The install-local DELETE calls it right after the registry withdrawal. The by-name read then serves the owner's body without the extension's fields, as the listing and the data plane already did. + - **Not touched.** Only a copy stamped with the registry owner's id is rewritten, the same rule the bridge overwrites by, so an authored copy stays. The DELETE answer is unchanged. A copy that cannot be restored is logged once at `error`, naming the objects and the remedy: restart the runtime. +- af284b0: feat(spec): the remaining `getSession` readers read `AuthSessionApi`'s declared result, and none reads a top-level `activeOrganizationId` + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains three optional `user` members**, each declared for a named reader: `isPlatformAdmin` (`boolean`, the ADR-0068 D2 rung plugin-auth's `customSession` derives) and `role` (`string | null`, better-auth's `admin` plugin scalar), both read by `judgePlatformAdmin` in `@objectstack/plugin-auth`; and `name` (`string`), read by the runtime dispatcher's AI-route `req.user` resolver as the display name. Every member is optional, so every implementer and caller typechecks unchanged. + - **The readers read through the declared type instead of `any` or a cast** (types only, runtime behaviour unchanged): `judgePlatformAdmin` and the four platform-admin mounts in `@objectstack/plugin-auth` (better-auth's own session type now has to fit the declaration at those reads), the REST server's two session reads (`@objectstack/rest`), the dispatcher's posture read, `resolveSessionData` and both `HttpDispatcher` reads (`@objectstack/runtime`), the cloud-connection and install-local session reads (`@objectstack/cloud-connection`), the webhook redeliver door (`@objectstack/plugin-webhooks`), and the storage session bridge (`@objectstack/service-storage`). + - **Fix — the top-level `activeOrganizationId` fallback is removed** from the storage upload stamp (`@objectstack/service-storage`) and from install-local's `resolveActiveOrgId` (`@objectstack/cloud-connection`). The active organization is read from `session.activeOrganizationId` alone, where the contract declares it. No in-process producer answers a top-level `activeOrganizationId` (better-auth's read is `{ user, session }`), so for the shipped auth plugin nothing changes. An auth service that answered only the flattened shape now gets no organization: the upload is not stamped, and install-local reports no active organization. To fix it, answer the active organization on `session.activeOrganizationId`, as `AuthSessionApi` declares it. +- 3a734e0: feat(spec)!: a page region named `main` takes only `width: 'full'` — `'small'`, `'medium'` and `'large'` there are refused at publish (#24326) + + Clause-②: yes (narrowing) + + + + **BREAKING** — an accept-set narrowing on a published authoring surface, shipped as `minor` under the repo's launch-window convention. + + `PageRegionSchema.width` is one enum shared by every region, so a `main` region accepted `'small'`, `'medium'` and `'large'`. The console never read them: every layout draws `main` at the width the layout leaves it (the flexible column beside `sidebar` and `aside`; one grid cell on `dashboard`), and the only reader of `main.width` is the page itself, for `'full'`, which lifts the page's max-width cap. A three-column page that asked for a `large` main got the space the two rails left, and every door reported success. + + - `PageSchema` now refuses `width: 'small' | 'medium' | 'large'` on a region named `main` (matched case-insensitively, as the console matches it), at `regions.N.width`, on every page kind and template. The message names the region and `'full'` as the one width it takes. The doors are `definePage`, `defineStack` (so `os validate` and `os build`, `STACK_SCHEMA_INVALID` 422) and the metadata save door (422 `INVALID_METADATA`). + - `'full'` on `main` is unchanged, with its page-wide meaning. Every other region accepts the same widths as before. + - The check is exported as `checkPageMainRegionWidth` from `@objectstack/spec/ui`, so a mirror built from `PageSchema.shape` can attach the same rule. + - Data at rest keeps loading: the retired ADR-0087 conversion `page-main-region-width-removed` deletes those widths on read: a stored page row, a compiled artifact inside the window its declared protocol range opens, and a package body registered with its declared range. The page renders exactly as before. + - `@objectstack/mcp` (the Connect an Agent page) and `@objectstack/cloud-connection` (the Cloud Connection page) drop `width: 'large'` from their `main` region. Both render the same. + + ### Migration: FROM → TO + + | Wrote | Write instead | + | :--- | :--- | + | `width: 'small' \| 'medium' \| 'large'` on a region named `main` | drop it; `'full'` stays | + + Deleting the key changes nothing on screen. Write `width: 'full'` only when you want a full-bleed page, which drops the page's max-width cap. +- 71fa166: Clause-②: no + + fix(cloud-connection): the sign-in refusals and the unmanaged-install copy no longer call the running system "this environment" + + - **What changed.** Inside the running system, user-facing copy names the system the user is in as "this system", or drops the noun. The seven `401 UNAUTHENTICATED` answers on `/api/v1/cloud-connection/*` now read "Sign in to connect a cloud account." (`bind/start`), "Sign in to install apps." (`install`) and "Sign in." (`bind/poll`, `unbind`, `installation`, `installed`, `org-packages`), where each used to say "Sign in to this environment …". On `/api/v1/marketplace/install-local`, the `409 RESOURCE_CONFLICT` that refuses a managed install over an unmanaged copy now names the holder as "the writable base 'BASE' — an unmanaged install of this package, or a base authored in this system — whose items are this system's own metadata", and the unmanaged install's success `note` reads "The package's metadata is now this system's own, in the base 'BASE' …". + - **Unchanged.** Every HTTP status, error code (`UNAUTHENTICATED`, `RESOURCE_CONFLICT`), `details` object and response shape. Server log lines are not touched. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [f3174ef] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [a06f0d4] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [8eccf51] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [ec749fb] +- Updated dependencies [ec749fb] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [41c7405] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [1827571] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/metadata-protocol@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/cloud-connection/package.json b/packages/cloud-connection/package.json index a3a34c7a1b9..413a626f5c0 100644 --- a/packages/cloud-connection/package.json +++ b/packages/cloud-connection/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/cloud-connection", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Runtime-side client for an ObjectStack cloud control plane — marketplace browse proxy, install-local, device-code binding, org catalog and installed views, and the /api/v1/runtime/config discovery endpoint. Open mechanism (cloud ADR-0008): the hub service, plan policy, and entitlements stay server-side.", "type": "module", diff --git a/packages/connectors/connector-imap/CHANGELOG.md b/packages/connectors/connector-imap/CHANGELOG.md index 7687b9ca853..53a368c11c2 100644 --- a/packages/connectors/connector-imap/CHANGELOG.md +++ b/packages/connectors/connector-imap/CHANGELOG.md @@ -1,5 +1,9 @@ # @objectstack/connector-imap +## 18.0.0-next.4 + +No changes in this release. + ## 18.0.0-next.3 No changes in this release. diff --git a/packages/connectors/connector-imap/package.json b/packages/connectors/connector-imap/package.json index 17d8abbb1cb..8903cecf821 100644 --- a/packages/connectors/connector-imap/package.json +++ b/packages/connectors/connector-imap/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/connector-imap", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "IMAP connector for ObjectStack — a read-only IMAP4rev1 session (EXAMINE, UID FETCH with BODY.PEEK) and the per-user-shaped mail account and credential abstraction the mailbox sync connects through.", "main": "dist/index.js", diff --git a/packages/connectors/connector-mcp/CHANGELOG.md b/packages/connectors/connector-mcp/CHANGELOG.md index ad50c7d20c8..cf14a87d28f 100644 --- a/packages/connectors/connector-mcp/CHANGELOG.md +++ b/packages/connectors/connector-mcp/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/connector-mcp +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/connectors/connector-mcp/package.json b/packages/connectors/connector-mcp/package.json index 3d5392c46ac..33c9f6d9de3 100644 --- a/packages/connectors/connector-mcp/package.json +++ b/packages/connectors/connector-mcp/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/connector-mcp", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Model Context Protocol (MCP) connector for ObjectStack — a generic adapter that turns any MCP server's tools into a connector's actions on the automation engine's connector registry (ADR-0024).", "main": "dist/index.js", diff --git a/packages/connectors/connector-openapi/CHANGELOG.md b/packages/connectors/connector-openapi/CHANGELOG.md index a83c7c04989..11cb5671995 100644 --- a/packages/connectors/connector-openapi/CHANGELOG.md +++ b/packages/connectors/connector-openapi/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/connector-openapi +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/connectors/connector-openapi/package.json b/packages/connectors/connector-openapi/package.json index ea752525d1d..983c9e82431 100644 --- a/packages/connectors/connector-openapi/package.json +++ b/packages/connectors/connector-openapi/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/connector-openapi", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "OpenAPI 3.x connector generator for ObjectStack — turns a declarative OpenAPI document into connector actions on the automation engine's registry, with a self-contained static-auth HTTP transport (ADR-0023).", "main": "dist/index.js", diff --git a/packages/connectors/connector-rest/CHANGELOG.md b/packages/connectors/connector-rest/CHANGELOG.md index ab3fad9a385..51f24c57f8b 100644 --- a/packages/connectors/connector-rest/CHANGELOG.md +++ b/packages/connectors/connector-rest/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/connector-rest +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/connectors/connector-rest/package.json b/packages/connectors/connector-rest/package.json index 9a31ca4d519..f40c33b0ec1 100644 --- a/packages/connectors/connector-rest/package.json +++ b/packages/connectors/connector-rest/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/connector-rest", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Generic REST connector for ObjectStack — the reference concrete connector that registers a `request` action on the automation engine's connector registry (ADR-0018 §Addendum).", "main": "dist/index.js", diff --git a/packages/connectors/connector-slack/CHANGELOG.md b/packages/connectors/connector-slack/CHANGELOG.md index 55ee8cc3322..37d83b60834 100644 --- a/packages/connectors/connector-slack/CHANGELOG.md +++ b/packages/connectors/connector-slack/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/connector-slack +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/connectors/connector-slack/package.json b/packages/connectors/connector-slack/package.json index 6b6b1a6e3af..569aa4b0395 100644 --- a/packages/connectors/connector-slack/package.json +++ b/packages/connectors/connector-slack/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/connector-slack", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Slack Web API connector for ObjectStack — registers `chat.postMessage` / `chat.update` / `call` actions on the automation engine's connector registry (ADR-0018 §Addendum, ADR-0022).", "main": "dist/index.js", diff --git a/packages/console/CHANGELOG.md b/packages/console/CHANGELOG.md index 3d80cd8a308..9404e7d340d 100644 --- a/packages/console/CHANGELOG.md +++ b/packages/console/CHANGELOG.md @@ -1,5 +1,126 @@ # @objectstack/console +## 18.0.0-next.4 + +### Minor Changes + +- f5e98a3: Console (objectui) refreshed to `0707b4ae3712`. Frontend changes in this range: + + Derived from the changesets objectui declared over the range — 84 releasing of 85 changesets added across 67 non-merge commits; omitted: 1 release-nothing changeset, 1 commit carrying no changeset (they ship no package code). + + - **minor** — **The AI build cards draw the plan and result details the build agent now sends** (objectui#12724). (objectui `44cb469db`) + - **minor** — **BREAKING** — Line-item grids read a boolean column's options and a file cell's text in the session language (objectui#12924). Under a zh-CN session, a master-detail or `record:line_items` grid… (objectui `32b492b67`) + - **minor** — A `${…}` hole inside an inline locale map is evaluated (objectui#12936). In a node's `properties` bag (or its legacy `props` alias, or a block member evaluated with `useConfigBagE… (objectui `a7ae2521a`) + - **minor** — The approval decision panel (`record:approval_decision`) shows where a request came from and where its flow stands (objectui#12491). A request no person submitted, its `submitter_… (objectui `7bbd718be`) + - **minor** — The Approval Center's request drawer no longer has a "Raw data (JSON)" panel, for any viewer (objectui#12490). (objectui `9b82cedad`) + - **minor** — feat(react): a page element's API data source is rendered — `dataSource: { provider: 'api', read: { url }, path? }` binds the selected answer as `data` (objectui#12197) (objectui `c4dd7078e`) + - **minor** — feat(app-shell): five generic product-page blocks — `hero`, `stat`, `usage-bar`, `checklist`, `tile-grid` (objectui#12197) (objectui `c4dd7078e`) + - **minor** — The separate Installed Apps view is retired (objectui#12733, ruling B on objectstack#23892). Setup → Packages filtered to Marketplace is the one list of what is installed (objectu… (objectui `35244b637`) + - **minor** — A host can ask the console to draw no app switcher when the viewer is served one app (objectui#12913, the objectui half of objectstack-ai/cloud#2919). (objectui `d15ad87b1`) + - **minor** — `record:path`: a stage derived from a picklist now takes its terminal from the option's declared lifecycle `kind` (objectstack#23809) — `won` is the forward terminus, `lost` sits… (objectui `8392f74af`) + - **minor** — A record whose record page declares `print` shows its header actions again (objectui#12906). (objectui `d637be321`) + - **minor** — A list's user-filter chip no longer shows the raw field key while the object definition is loading (objectui#12890). (objectui `1b8864556`) + - **minor** — A page laid out by a slotted template draws only the regions that template places, and says so on the page for any other (objectui#12476, maintainer ruling A on objectstack-ai/obj… (objectui `044f23193`) + - **minor** — **Breaking (types):** a form select option (`SelectOption` and its zod twin `SelectOptionSchema`) carrying `kind` or `default` is now refused (objectui#12736). (objectui `c999d4c01`) + - **minor** — In Studio's Interfaces pillar, a click on a card of a kanban view's board selects the board's card, and a 看板卡片 (Kanban card) section in the view's properties edits it (objectui#12… (objectui `f5009db72`) + - **minor** — `element:text` now draws the inline Markdown its spec declares (objectui#12778). `ElementTextPropsSchema.content` is described as "Text or Markdown content", and the renderer prin… (objectui `d2b21a122`) + - **minor** — Resolve `@objectstack/*` `18.0.0-next.3` (objectui#12873). (objectui `26323c4fc`) + - **minor** — In Studio's Interfaces pillar, a click on a column header in an `object` leaf's canvas selects that column in the view's properties (objectui#12725, target 2: clicking a canvas el… (objectui `e058d7697`) + - **minor** — New `isDeadlineSettled(field, row, { scope, fields })` (objectui#11815). It answers whether a declared deadline is settled for one row: `true` only when the field declares `dueLik… (objectui `f34e877b6`) + - **minor** — The record page and related lists honour a deadline's `settledWhen` (objectui#11815). The details section, the highlights strip and each related-list row judge a declared deadline… (objectui `f34e877b6`) + - **minor** — A date reads overdue only when its field declares it a deadline, and never once the deadline is settled for the record (objectui#11815). (objectui `f34e877b6`) + - **minor** — A done row's past deadline no longer reads overdue in the grid (objectui#11815). Every column shape — object columns, name columns, inline-row fields and the object's default colu… (objectui `f34e877b6`) + - **minor** — A kanban card's declared deadline reads neutral once the card's record settles it through the field's `settledWhen`, and "Overdue Nd" in red otherwise (objectui#11815). (objectui `f34e877b6`) + - **minor** — Gallery cards and the compact list honour a deadline's `settledWhen` (objectui#11815): a done record's past due date reads neutral, an open one's reads "Overdue Nd" in red. Galler… (objectui `f34e877b6`) + - **minor** — A tree row's declared deadline reads neutral once the row's record settles it through the field's `settledWhen`, and "Overdue Nd" in red otherwise (objectui#11815). (objectui `f34e877b6`) + - **minor** — `DetailViewField.dueLike` and its `DetailViewFieldSchema` row are removed, with no alias (objectui#11815). A deadline is declared on the object's field metadata — `DateFieldMetada… (objectui `f34e877b6`) + - **minor** — feat(react): `MetadataContextValue.readObjectScope`, the object definitions one app reads (objectui#12811) (objectui `5ff64efc7`) + - **minor** — Studio's Data and Automations pillars show their properties in the one right slot, as the Interfaces pillar does (objectui#12725, item 3: properties follow the selection in every… (objectui `8d6e43574`) + - **minor** — Studio has one right slot that shows either the open item's properties or the AI copilot, never both (objectui#12725, items 1, 2, 6 and the canvas half of item 3). (objectui `34354a4b6`) + - **minor** — `DateFieldMetadata` and `DateTimeFieldMetadata` take the deadline keys from `@objectstack/spec`'s `FieldSchema` (objectui#12297). (objectui `8106db1f9`) + - **minor** — The console splash no longer waits for requests the first screen does not need (objectui#12809). (objectui `84b8ffa4b`) + - **patch** — A metric tile's value now scales before it would clip. A long value, such as a seven-digit money figure with cents (`1,290,000.00`), steps down from 24 px to 14 px as the tile nar… (objectui `0707b4ae3`) + - **patch** — `@object-ui/plugin-detail` now declares `@objectstack/spec` `^18.0.0-next.3`, up from `^18.0.0-next.1` (objectui#12946). (objectui `03f1d6528`) + - **patch** — Raise `@object-ui/app-shell`'s `@objectstack/spec` floor from `^18.0.0-next.2` to `^18.0.0-next.3` (objectui#12943). (objectui `641dad4b3`) + - **patch** — An `operation: 'update'` action no longer posts its declared `patch` as request `params` (objectui#12941). The platform action route applies the patch itself, from the action defi… (objectui `fb501bc89`) + - **patch** — Setup → Packages reads each package's Source from the server (objectui#12790, ruling B on objectstack#23892). (objectui `bc96bc34b`) + - **patch** — `record:details` draws a boolean the viewer cannot edit as the locale's word for its value (`common.yes` / `common.no`) instead of a disabled checkbox that read as a setting refus… (objectui `79cb8c03e`) + - **patch** — All ten language packs gain the panel titles the data-bound blocks now read for an unresolvable `dataSource.view` (objectui#12920): `element.KIND.sourceUnresolved` for `calendar`,… (objectui `1e6d83bc4`) + - **patch** — Data-bound blocks title their "data source could not be resolved" panel in the page's language (objectui#12920, the family of objectui#12910). The panel shows when a block's `data… (objectui `1e6d83bc4`) + - **patch** — A read-only `GridField` table shows a select cell's option label and a number or currency cell in the session's locale (objectui#12915). (objectui `8523e81db`) + - **patch** — The kanban swimlane that holds cards with no value in the swimlane field is headed by the pack's `kanban.uncategorized` instead of the English constant (objectui#12899). Under a n… (objectui `e1548b794`) + - **patch** — Studio's "Add region" writes only a region name the page's template places, and a page that names no template refuses an unplaced region on screen (objectui#12902). (objectui `bc281f4a1`) + - **patch** — The `element:*` blocks registered from `renderers/basic` draw their defaults in the page's language (objectui#12910, the close-out of objectui#12889). Each default reads `@object-… (objectui `2cfdc99df`) + - **patch** — All ten language packs gain the keys the `element:*` blocks in `@object-ui/components` now read for their defaults (objectui#12910): `element.number.sourceUnresolved`, `element.im… (objectui `2cfdc99df`) + - **patch** — A Studio item's title no longer disappears beside the header's actions on a narrow window (objectui#12897). (objectui `7bee002d2`) + - **patch** — `record:line_items` fills an untyped column from the child object's field (objectui#12854, the objectui half of objectstack-ai/objectstack#24140). (objectui `54e79f989`) + - **patch** — `ChatbotEnhanced`'s `onDesignBuiltApp` doc comment now describes what the finished build card draws since objectui#12724: "Open app" (打开应用) is the primary action and "Design in St… (objectui `c83e3fe53`) + - **patch** — Retire what the post-build transition left behind after objectui#12724 (objectui#12842). (objectui `c83e3fe53`) + - **patch** — The Console top bar no longer flashes a green "Connected" chip for two seconds after load (objectui#12891). (objectui `2727527b1`) + - **patch** — Page element blocks draw their loading, empty and error defaults in the page's language (objectui#12889). (objectui `97538115b`) + - **patch** — The console's boot session check now keeps the renewed session cookie (objectui#12892). Before the app renders, the console checks the stored sign-in token with one `get-session`… (objectui `285ddb22c`) + - **patch** — The kanban board's card lists and drag overlay are now named in the viewer's language. A lane's card list, a swimlane cell's card list and the drag overlay used English templates… (objectui `4ff5b6b3c`) + - **patch** — Studio's new-package dialog no longer offers a scope. Its Advanced section used to list `cloud`, `system` and `project`, and the server refuses the first two when a package is cre… (objectui `8e7597836`) + - **patch** — A formula field declared `returnType: 'currency'` renders as money, in a list cell, a form and a record detail (objectui#12111). (objectui `898bd2c06`) + - **patch** — The marketplace README renderer draws its inline Markdown with `@object-ui/components`' `renderInlineMarkdown`, the one `element:text` uses (objectui#12778), so a README link now… (objectui `d2b21a122`) + - **patch** — A focused kanban card opens on Enter and on Space (objectui#12883). Every card was already a keyboard tab stop, but its only activation was the pointer click, so a keyboard user c… (objectui `1d872f2f7`) + - **patch** — Metadata designer create flows now show the spec create seed's structural defaults before the first save. A new dataset's Measures section reads 1 (the seed's `count` measure) as… (objectui `2ec9e2a1a`) + - **patch** — The public form `/f/:slug` shows each section's description under its heading, as served (objectui#12333). (objectui `2b0ab9480`) + - **patch** — The OAuth consent page names the three agent scopes in words. An MCP client that asks for `data:read`, `data:write` or `actions:execute` used to show those as raw strings in the "… (objectui `b27901612`) + - **patch** — Kanban cards no longer tell screen-reader users to press the space bar. Each card's accessible description is now a sentence from the `kanban.dnd.instructions` bundle key, in the… (objectui `8a25feec5`) + - **patch** — A related list on a custom app page draws and runs its authored actions when its parent record is bound through `parentDataSource` (objectui#12870). (objectui `15b10655e`) + - **patch** — The approval decision panel's record summary honours a declared deadline's `settledWhen` (objectui#11815), judged against the snapshot it shows: a done record's past due date read… (objectui `bf376c5b3`) + - **patch** — The approvals inbox's record summary honours a declared deadline's `settledWhen` (objectui#11815), judged against the submitted snapshot it shows: a done record's past due date re… (objectui `bf376c5b3`) + - **patch** — A list view that shows a declared deadline but not the field its `settledWhen` reads now fetches that field, so a done record's past due date reads neutral instead of overdue (obj… (objectui `bf376c5b3`) + - **patch** — The data table's header cells name their column: each carries `data-column-key` with the column's `accessorKey` (objectui#12725). Studio's canvas reads it to tell which column a h… (objectui `e058d7697`) + - **patch** — A settings row's chips read in the session's language (objectui#12821). The env-lock chip said "Locked by env" in every language, and its title, shown when the server names no rea… (objectui `64acb7e59`) + - **patch** — The command palette's recently viewed rows now show what each item is: the record's object label on the right (in the viewer's language) and the object's icon on the left, in plac… (objectui `0e989914e`) + - **patch** — A refused activity read now shows one neutral sentence, "You don't have permission to view activity.", on Home's activity card and in the header bell's Activity tab. Home used to… (objectui `bb6c38cb6`) + - **patch** — A long api name no longer cuts a Studio item's title to a few characters (objectui#12791). (objectui `0c511f7d9`) + - **patch** — perf(app-shell): a console open reads the current app's objects, not the whole object registry (objectui#12811) (objectui `5ff64efc7`) + - **patch** — The flow designer names a schedule flow "Schedule", not "Schedule (cron)" (objectui#12845). (objectui `955dbaf8d`) + - **patch** — The workspace switcher, avatar menu, organisations page, create-organisation dialog and home screens fall back to the same "Organization" / "Environment" wording as the language p… (objectui `473e5a0a8`) + - **patch** — The sign-in page's tenant hint falls back to "You are signing in to this environment", matching the language packs (objectui#12851). (objectui `473e5a0a8`) + - **patch** — The console no longer calls two different things "workspace" (objectui#12851). (objectui `473e5a0a8`) + - **patch** — Dataset designer: picking a new dataset's base object no longer drops the template's `count` measure, so a dataset created by opening it, picking an object and saving keeps its ro… (objectui `fb1700372`) + - **patch** — A declared action `icon` is now drawn beside the label on the row's inline primary button, the row "⋮" menu item and the record-header quick action (`record:quick_actions`). The n… (objectui `1aa622a7a`) + - **patch** — Home's "Activity feed" card now shows the record activity panel's permission sentence ("You don't have permission to view activity…") when the server refuses its `sys_activity` re… (objectui `024d44694`) + - **patch** — In the collapsed (icon-mode) console sidebar, the hidden Area, Recent and Starred group labels are no longer announced by screen readers (objectui#12795). The `SidebarGroupLabel`… (objectui `e43a16207`) + - **patch** — In the collapsed (icon-mode) sidebar, a hidden group label is no longer a keyboard focus stop, and screen readers no longer announce it (objectui#12795). Before, Tab from the row… (objectui `e43a16207`) + - **patch** — A metric widget with no `layout` on a dashboard that declares `columns` now spans one grid row instead of two, the same height the dashboard designer gives a new widget. The tile… (objectui `db4417e21`) + - **patch** — Approvals Inbox: on a narrow screen the inline Approve / Reject column stays pinned to the table's right edge, with a shadow on its left edge while the table scrolls under it, ins… (objectui `4b3d6edae`) + - **patch** — Studio Interfaces names a package that has no app yet by its display name, the one the top bar prints, instead of its id (objectui#12718). The rail heading read `com.qa.recheck ·… (objectui `e1351a8e3`) + - **patch** — Metadata designer create flows no longer save the spec create seed's placeholder identity. The seed's `label` and its placeholder object binding are taken from the author or not a… (objectui `b921f9090`) + - **patch** — In the collapsed (icon-mode) console sidebar, the rail now scrolls vertically when its rows overflow, as the expanded sidebar does. Before, the rows below the fold could not be re… (objectui `0812a190f`) + + ⚠️ 2 of these carry a breaking change: 2 by the author's own breaking annotation in the changeset body — objectui declares no `major` inside a launch window (`scripts/check-changeset-no-major.mjs`). Each is marked **BREAKING** in the list above — read them before compiling the release record. + + **In this console build, declared nowhere** — objectui merged 1 commit in this range with no `.changeset/*.md`. The code is inside the pin above and ships here, but nothing upstream declared it, so it appears in no objectui CHANGELOG and in no entry above. Listed by subject rather than counted, because a count cannot tell a dependency bump from a form-behaviour change (objectstack#6174); the upstream gate that would prevent this is objectui#3387. + + - _(no changeset)_ docs(adr-0056): AdminScopeSchema target shape lists only the live keys (#12903) (objectui `5ec2175fd`) + + Clause-②: no + + + + objectui range: `5e15928b4631...0707b4ae3712` + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/console/package.json b/packages/console/package.json index 7650f3d84d9..ce2554c186e 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/console", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "description": "Prebuilt Console SPA pinned to this framework release, installed as a dependency of @objectstack/cli. Source of truth: @object-ui/console (https://github.com/objectstack-ai/objectui).", "license": "Apache-2.0", "homepage": "https://github.com/objectstack-ai/objectstack/tree/main/packages/console", diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index fb990185c35..d054bebd2b6 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,126 @@ # @objectstack/core +## 18.0.0-next.4 + +### Minor Changes + +- b2e7870: fix(security): under the `single` tenancy posture the first-user platform-admin grant is owned by the Default Organization, and every reader of platform standing reads that row + + Clause-②: yes (narrowing) + + + + **BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + - **FROM** (`@objectstack/plugin-security`): a `single` deployment holding several organizations, none slugged `default`, promoted its first user with an organization-less `admin_full_access` grant. **TO:** it is not promoted; the bootstrap logs one `error` line, returns `reason: 'default_organization_unresolved'` and writes no grant row. **Fix:** give the intended organization the slug `default` (or keep one organization, or declare a walled posture); the next `kernel:ready` or sign-up pass promotes. + - **FROM** (`@objectstack/plugin-auth`): a `sys_organization` `slug` change or delete was never refused. **TO:** it is refused with `403 PERMISSION_DENIED` when that organization is the Default Organization whose `admin_full_access` grant is the last unbanned administrator's anchor. **Fix:** give another account administrator standing first, then make the change. + - **FROM** (`@objectstack/plugin-auth`): `ensureDefaultOrganization` under `single`, with a sole organization of another slug, created a second, `default`-slugged organization and bound the administrator there. **TO:** it reuses the sole organization; no second organization is created. **Fix:** none needed; an install that wants a `default`-slugged organization re-slugs its existing one or creates it explicitly. + - **Widening** (`@objectstack/core`): under `single` only an organization-less `admin_full_access` grant conferred `PLATFORM_ADMIN`. Now a grant owned by the Default Organization confers it too and applies in every organization; a pre-existing Default-Organization-scoped `admin_full_access` grant gains the rung on upgrade; and `sys_organization` becomes a standing-deriving table (the resolver reads it only under `single` for a holder of an organization-owned `admin_full_access` grant — one or two small reads per uncached request; the last-administrator guard adds three to six small reads per guarded write). + + - **The promotion writes the Default Organization (`@objectstack/plugin-security`).** A `single` deployment with no configuration still promotes its first user that can sign in. The `admin_full_access` grant row that promotion writes now carries the Default Organization as its `organization_id` (ADR-0131 D5), never NULL. The Default Organization is the one with `slug = 'default'`, else the only organization: the organization the auth plugin creates at boot. A deployment that names no Default Organization (several organizations, none with the slug `default`) is not promoted. The bootstrap logs one `error` line saying why and returns `reason: 'default_organization_unresolved'`. It writes no grant without an owner. `OS_PLATFORM_OWNER_EMAIL` is still the walled-posture channel and is not required under `single`. + - **The authorization resolver reads that row (`@objectstack/core`).** Under `single`, `PLATFORM_ADMIN` derives from an `admin_full_access` grant owned by the Default Organization, and still from the organization-less row an earlier release wrote. The anchoring grant applies in whichever organization the user acts in, as the organization-less row always did. A grant of the set owned by any other organization confers the set in that organization, never platform standing. Under a walled posture no grant anchors standing (unchanged). One predicate decides both postures, with the posture as its parameter. The resolver reads `sys_organization` only for a user who holds an organization-owned `admin_full_access` grant under `single`. + - **An upgraded database keeps its administrator.** `os migrate organization-ownership --apply` gives a `single` database's organization-less grants to the Default Organization, the platform admin's grant included. Before this change the resolver then stopped grading that admin `PLATFORM_ADMIN`, and every later boot found no organization-less holder and promoted again. The holder scan now reads the Default Organization's grant, so a migrated database keeps its admin and promotes nobody else. A user who holds both rows counts as one holder. + - **The last-administrator guard and the default-organization bootstrap read it too (`@objectstack/plugin-auth`).** The break-glass guard counts the holder of the Default Organization's grant as a platform admin and treats that grant as evidence that the environment once had one. It also judges the two organization writes that move the anchor off that grant: changing the Default Organization's `slug` while another organization exists, and deleting it. Either is refused when it would leave no administrator who can sign in. `ensureDefaultOrganization` finds the admin through the same grant. Under `single` it also reuses the Default Organization when that is a sole organization with another slug, where it used to create a second, `default`-slugged organization. +- eebaf3b: feat(objectql,core): the driver's tenant read scope is the organization wall's verdict — one predicate for Layer 0 and every driver (ADR-0131 D8) + + Clause-②: yes (widening) + + - **What changed.** The engine no longer derives `DriverOptions.tenantId` / `tenantIds` beside the organization wall. `buildDriverOptions` projects the wall's own verdict onto them: the verdict plugin-security records on the operation (`OperationContext.tenantLayer0Verdict`) where its wall ran, and otherwise the same function over the engine's own facts (the posture in force, the context's organization and membership set). The function, `computeTenantLayer0Verdict` with its input type `TenantLayer0Input`, moves to `@objectstack/core` (new export); `@objectstack/plugin-security`'s `tenant-layer.ts` re-exports it and its Layer 0 is byte-identical. + - **FROM → TO, per posture, for a read, count, aggregate, update or delete on an object with an organization column:** + + | posture / caller | driver scope before | driver scope now | + |:--|:--|:--| + | `single`, any caller naming an organization | that organization (plus organization-less rows) | **none** | + | `isolated`, member | the active organization | unchanged | + | `group`, member | the membership set | unchanged | + | walled, exempt `PLATFORM_ADMIN` on a posture-permitting object, or the verified platform owner's read | the active organization | **none** — what Layer 0 already admitted | + | walled, system context naming an organization | that organization | unchanged | + + - **Not changed.** Inserts and upserts keep the active organization as `tenantId` in every posture: the row is stamped with it, its counter scopes by it, and an upsert that would merge into another organization's row is still refused. Both `orWhereNull` compatibility arms in `SqlDriver.applyTenantScope` stay (their retirement is a later stage of ADR-0131 D13). + - **Who notices.** A `single` deployment that holds more than one organization was kept apart by the driver only by accident; postures now differ only in enforcement (ADR-0131 D11), and `single` enforces no wall. To keep organizations apart, run the `isolated` or `group` posture. Code that relied on a context organization to narrow a read on `single` names the organization in its own `where`. + - **Migration.** ADR-0087 semantic entry `driver-read-scope-follows-tenant-wall`. A deployment's `platformGlobalObjects` carve-out already reaches the driver through the column plan (ADR-0131 D7): a declared object carries no organization column, so neither wall has anything to scope. +- f5ef054: feat(core): export `resolveRequestExecutionContext`, the one composition that turns an inbound request into the caller's `ExecutionContext` + + Clause-②: yes (widening) + + - **New export from `@objectstack/core`'s `.` entry.** `resolveRequestExecutionContext(input)` reads the session through the auth service (the in-process rule: a request carrying a session cookie reads without renewal), classifies the tenancy posture with `classifyAdmissionTenancyPosture`, resolves the principal and its grants with `resolveAuthzContext`, resolves the reference localization, decides the ADR-0069 authentication-policy gate and assembles the envelope with `assembleExecutionContext` or `assembleExecutionContextOrGuest`. Its input type `RequestExecutionContextInput` and the types `RequestAuthService`, `EstablishedRequestSession` and `RequestAdmission` are exported with it. + - **Two choices, made once.** `anonymous: 'fail-closed'` yields no context for a request that resolves no principal (the door answers 401); `anonymous: 'guest'` serves it as the guest principal with the guest anchor's grants. `authGate: 'withhold' | 'resolved' | 'reread-when-active'` says whether the gate's posture rides the envelope, and how it is read. + - **Every other difference between doors is a named, required parameter:** `credentials`, `sessionApi`, `session`, `tenancy`, `localization`, `requestLocale`, `accessToken`, `oauth` and `onAdmission`. A door with no value passes `undefined` or the withholding literal, so no door can drop an input by omitting it. + - **First caller.** The runtime dispatcher's identity door (`@objectstack/runtime`'s `resolveExecutionContext`) now calls it. What that door answers is unchanged: the same guest envelope, the same 503 for a tenancy service that failed to build, the same OAuth agent principal on `/mcp`. + - **Faults propagate.** It catches nothing around the resolver: an unreadable permission store or tenancy service (`AuthzStoreUnavailableError`, 503) and an API key its own lifecycle refuses (`ApiKeyLifecycleRefusalError`, 403) reach the caller, which answers them at its own net. +- 2ac0616: feat(core): export the emptiness rule a `required` declaration is judged by — `isEmptyForRequired(def, value)` and `isMissing(value)` + + Clause-②: yes (widening) + + - **New exports from `@objectstack/core`'s `.` entry.** `isMissing(value)` is `true` for `undefined`, `null`, and a string that is empty or only whitespace. `isEmptyForRequired(def, value)` is `isMissing`, plus an empty list `[]` on a field whose DECLARED value is a set (`@objectstack/spec/data`'s `isMultiValueField`: `tags`, `multiselect`, `checkboxes`, or a `select` / `radio` / `lookup` / `user` / `file` / `image` field flagged `multiple: true`). `def` is a `ValueShapeFieldDef` (`{ type, multiple? }`). A structured-JSON field's `[]` is a document, not an empty value. + - **Why it moved.** It was private to `@objectstack/objectql`'s record validator, where the object-level `required` check runs. `@objectstack/rest`'s anonymous public-form submit route now judges a FORM's `required` with it, and rest does not depend on the engine at runtime. So the one rule lives in the package both depend on, and the two doors call the same field missing. + - **Unchanged:** the rule itself, byte for byte — the engine's object-level `required` refusal answers exactly what it did. +- 62e2b6d: fix(core)!: an API key whose `sys_api_key` read fails is answered as the store outage (`503 SERVICE_UNAVAILABLE`), never as "no key", so the request no longer falls on to a cookie sent beside the key + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + `resolveApiKeyAdmission` (and `resolveApiKeyPrincipal` and `resolveAuthzContext`, which call it) read the key's row in a bare `catch` that answered "this request carries no key", and logged nothing. Two wrong answers followed on every door that admits API keys: + + - **A key-only caller got `401 UNAUTHENTICATED`** during the outage, where a session caller got `503 SERVICE_UNAVAILABLE` for the same fault. + - **A cookie sent beside the key answered for the request.** "No key" sent the request on to the session arm, so a request that led with a key was served as the cookie's user. Measured on the REST data door and the runtime dispatcher (served as the cookie's user) and on the settings door (`200`). + + The read now raises the branded `AuthzStoreUnavailableError` (object `sys_api_key`, the driver's error kept as `cause`), the answer the same function already gave when its lifecycle-policy or owner-posture source failed. Every door relays it as `503 SERVICE_UNAVAILABLE`, and the session is never read in the key's place. + + - **Unchanged.** A `sys_api_key` table that was never provisioned is still "no key": no key can exist there. This is the boundary `resolveAuthzContext` already draws for its permission reads (the shared `isMissingTableError`). No key header, no engine, no matching row and a row with no `user_id` also still answer "no key". + + What to do. Nothing, unless a client treated `401` during a database outage as "the key is bad": it now sees `503` and should retry, not rotate the key. + +### Patch Changes + +- b036ac5: feat(spec): `AuthSessionApi.getSession`'s result declares the fields its in-process readers read + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains five optional members**, each declared for a named reader and typed as that reader takes it: `user.email` and `session.id` / `session.token` (read by `resolveAuthzContext`'s session arm in `@objectstack/core`), `user.authGate` as `unknown` (the ADR-0069 posture, which `normalizeAuthGate` validates against the closed posture vocabulary), and `session.impersonatedBy` as `string | null` (read by the print door's `impersonatorOf` in `@objectstack/service-print`). Before this, those reads went through `any` or a cast, so a field better-auth dropped or renamed compiled and read `undefined`. For `impersonatedBy`, that would have silently turned off the impersonation refusal. Every member is optional, so every implementer and caller typechecks unchanged. + - **`@objectstack/core`**: `resolveAuthzContext`'s session arm reads the session through the declared type instead of `any`. `ResolveAuthzInput.getSession`'s own type is unchanged. Runtime behaviour is unchanged. + - **`@objectstack/service-print`**: `impersonatorOf` reads `session.impersonatedBy` through the declared type instead of a cast. Runtime behaviour is unchanged. +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/core/package.json b/packages/core/package.json index 096dc60f762..71490b3e7ba 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/core", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Microkernel Core for ObjectStack", "type": "module", diff --git a/packages/create-objectstack/CHANGELOG.md b/packages/create-objectstack/CHANGELOG.md index ab1d763809b..ac19ab7c18d 100644 --- a/packages/create-objectstack/CHANGELOG.md +++ b/packages/create-objectstack/CHANGELOG.md @@ -1,5 +1,9 @@ # create-objectstack +## 18.0.0-next.4 + +No changes in this release. + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/create-objectstack/package.json b/packages/create-objectstack/package.json index b93cb2c4c45..6f50f8f8b74 100644 --- a/packages/create-objectstack/package.json +++ b/packages/create-objectstack/package.json @@ -1,6 +1,6 @@ { "name": "create-objectstack", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "description": "Create a new ObjectStack project — npx create-objectstack", "bin": { "create-objectstack": "./bin/create-objectstack.js" diff --git a/packages/drivers/driver-memory/CHANGELOG.md b/packages/drivers/driver-memory/CHANGELOG.md index c1959074e5e..40041333c74 100644 --- a/packages/drivers/driver-memory/CHANGELOG.md +++ b/packages/drivers/driver-memory/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/driver-memory +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/drivers/driver-memory/package.json b/packages/drivers/driver-memory/package.json index d2c98f15a11..c86c9324fad 100644 --- a/packages/drivers/driver-memory/package.json +++ b/packages/drivers/driver-memory/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/driver-memory", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "In-Memory Driver for ObjectStack (Reference Implementation)", "main": "dist/index.js", diff --git a/packages/drivers/driver-mongodb/CHANGELOG.md b/packages/drivers/driver-mongodb/CHANGELOG.md index 3b8e5e9bb38..5f35c07e146 100644 --- a/packages/drivers/driver-mongodb/CHANGELOG.md +++ b/packages/drivers/driver-mongodb/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/driver-mongodb +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/drivers/driver-mongodb/package.json b/packages/drivers/driver-mongodb/package.json index 0ae329cb735..34cdbe26394 100644 --- a/packages/drivers/driver-mongodb/package.json +++ b/packages/drivers/driver-mongodb/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/driver-mongodb", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "MongoDB Driver for ObjectStack - Native document database driver via official mongodb client", "main": "dist/index.js", diff --git a/packages/drivers/driver-sql/CHANGELOG.md b/packages/drivers/driver-sql/CHANGELOG.md index ef75c43b765..8502894c45e 100644 --- a/packages/drivers/driver-sql/CHANGELOG.md +++ b/packages/drivers/driver-sql/CHANGELOG.md @@ -1,5 +1,53 @@ # @objectstack/driver-sql +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/observability@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/drivers/driver-sql/package.json b/packages/drivers/driver-sql/package.json index 9d3f6ac40df..edcc243f208 100644 --- a/packages/drivers/driver-sql/package.json +++ b/packages/drivers/driver-sql/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/driver-sql", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "SQL Driver for ObjectStack - Supports PostgreSQL, MySQL, SQLite via Knex", "main": "dist/index.js", diff --git a/packages/drivers/driver-sqlite-wasm/CHANGELOG.md b/packages/drivers/driver-sqlite-wasm/CHANGELOG.md index 07e016f2ce6..7413a5529b1 100644 --- a/packages/drivers/driver-sqlite-wasm/CHANGELOG.md +++ b/packages/drivers/driver-sqlite-wasm/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/driver-sqlite-wasm +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/driver-sql@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/drivers/driver-sqlite-wasm/package.json b/packages/drivers/driver-sqlite-wasm/package.json index 6c2396faaf5..38246fabc50 100644 --- a/packages/drivers/driver-sqlite-wasm/package.json +++ b/packages/drivers/driver-sqlite-wasm/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/driver-sqlite-wasm", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "WASM SQLite Driver for ObjectStack — runs in browser/WebContainer (StackBlitz) without native bindings", "keywords": [ diff --git a/packages/drivers/driver-turso/CHANGELOG.md b/packages/drivers/driver-turso/CHANGELOG.md index 957c49f364e..ffc77771f0b 100644 --- a/packages/drivers/driver-turso/CHANGELOG.md +++ b/packages/drivers/driver-turso/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/driver-turso +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/driver-sql@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/drivers/driver-turso/package.json b/packages/drivers/driver-turso/package.json index c2c07f4bbb5..4278e76eb82 100644 --- a/packages/drivers/driver-turso/package.json +++ b/packages/drivers/driver-turso/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/driver-turso", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Turso/libSQL Driver for ObjectStack — Edge-first SQLite with embedded replicas", "keywords": [ diff --git a/packages/formula/CHANGELOG.md b/packages/formula/CHANGELOG.md index 455627a2cf6..21d5ba632dc 100644 --- a/packages/formula/CHANGELOG.md +++ b/packages/formula/CHANGELOG.md @@ -1,5 +1,59 @@ # @objectstack/formula +## 18.0.0-next.4 + +### Minor Changes + +- d537a31: feat(formula)!: `validateExpression` judges relationship traversal in the `value` role when the caller opts in, with formula wording + + Clause-②: yes (narrowing) + + + + - `validateExpression('value', source, { fieldTypes, traversalHydration: true, … })` now refuses a second hop through a reference (`traversal-multi-hop`) and a reference read both through and as a plain value (`traversal-bare-and-traversed`). The messages say the formula reads null, and the repair for an empty reference is the optional read `record.LOOKUP.?FIELD.orValue('…')`. Before, the `value` role ignored the flag. + - `findTraversalConflicts` takes an optional fourth argument, `subject` (`'predicate'`, the default, or `'formula'`), which selects that wording. The `TraversalSubject` type is exported. + + Without the flag, every role behaves as before. + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/formula/package.json b/packages/formula/package.json index 9af238d73c8..e71a4dd6007 100644 --- a/packages/formula/package.json +++ b/packages/formula/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/formula", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack canonical expression engine — CEL (cel-js) + ObjectStack stdlib + dialect registry", "main": "dist/index.js", diff --git a/packages/lint/CHANGELOG.md b/packages/lint/CHANGELOG.md index 7db279973be..e1e203e7d25 100644 --- a/packages/lint/CHANGELOG.md +++ b/packages/lint/CHANGELOG.md @@ -1,5 +1,133 @@ # @objectstack/lint +## 18.0.0-next.4 + +### Minor Changes + +- d537a31: feat(lint)!: a formula field's `expression` is judged on the one-hop line — one hop through a reference passes, a second hop is refused + + Clause-②: no (narrowing) + + + + The field-formula pass now passes `traversalHydration` for a formula `expression`, because the engine's formula read path loads one hop through a reference field. + + - `record.book.title + ' · ' + record.reader.name` passes, as before. It now also computes at run time. + - `record.book.shelf.code` (two hops) is refused. The refusal names the field and says a formula resolves one hop. + - `record.reader == null ? '—' : record.reader.name` (the reference read both through and as a plain value) is refused. The refusal names `record.reader.?name.orValue('—')`, the read that gives an empty reference a value. + + What to do. Read a field stored on the related object instead of a second hop, and replace a null test on a reference with the optional read. +- 69147ab: fix(objectql)!: the one-hop read through a reference never reads a stored-metadata-body table, and the lint refuses that read where it is written + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The tables of the stored-metadata-body family (`STORED_METADATA_BODY_OBJECTS`) are projected or refused at every door that serves them. The one-hop read a formula field and a validation rule share (`resolveOneHopRelated`) was an ordinary engine read, which the family's projection does not reach. + + - **`@objectstack/objectql`.** A hop whose target is a family table is not read, whichever authority the hop is read under. A formula field reading through it is `null` with its reason, on every read, write response and `serveFormulaField` call (the seam a hook body's `ctx.title()` reads through). A validation rule reading through it has no verdict, so the write it judges is refused. The answer is the one an unreadable related record gets, so it says nothing about the row. A hop into any other object is unchanged. + - **`@objectstack/lint`.** A formula field's `expression`, or a validation rule's hydrated `condition`, that reads through a reference into a family table is an error, with the prescription. `os validate`, `os build` and the object save door run the same pass, so the author learns it at save rather than from a `null`. + + What to do. Remove the read through the reference. Stored metadata is read through the metadata API (`GET /api/v1/meta/:type/:name`), where its body is projected. Reading the reference field itself (the stored id) is unchanged. + +### Patch Changes + +- 9c74d40: fix(lint): the last eight fenced author-time rule ids print a `fix:` line of one remedy sentence, and `os explain RULE_ID` carries what those lines used to explain + + Clause-②: no + + - **Shorter fix lines.** Each finding of these 8 rule ids now carries a `hint` (the CLI's `fix:` line, the runtime finding's `hint`) of one remedy sentence of at most 200 characters on every variant the lint suite fires (lengths are the `hint` alone; the CLI prints it after `fix: `). The longest of each, before → after: + - flow template holes (`validate-flow-template-paths.ts`): `flow-template-field-unprovisioned` (362 → 175) and `flow-template-lookup-traversal` (316 → 139); + - page bindings (`validate-page-field-bindings.ts`): `page-field-unprovisioned` (362 → 175) and `page-section-group-unknown` (279 → 151); + - `print-page-block-unprintable` (362 → 170), `form-section-group-unknown` (275 → 147), `filter-token-unknown` (259 → 170) and `component-props-invalid` (258 → 188; its value-rejection line 248–258 → 157–167, while its retired-key line, 164–181, and its node-binding line, 188, were one remedy already and are unchanged). + - **Examples.** `print-page-block-unprintable` FROM ``Replace or remove the block. The printable blocks are `page:section`, `page:card`, … `object-metric`. If the page is not a document, delete its `print` declaration instead.`` (362) TO ``Replace the block with a printable one (`os explain print-page-block-unprintable` lists them) or remove it; if the page is not a document, delete its `print` declaration.`` (170). `flow-template-lookup-traversal` on a flow variable FROM ``A record read into a flow variable is not expanded either — config.expand is the START node's opt-in and covers the trigger record alone. Reference the foreign-key id directly ('{{ currentCase.owner_id }}'), project the value via a formula field on 'crm_case', or add a get_record node that reads the related record.`` (316) TO ``Write '{{ currentCase.owner_id }}' (the id) or a formula field on 'crm_case', or read the related record with a get_record node.`` (128). + - **Lists are no longer echoed.** The two section-group ids no longer append the object's declared field groups: the line names the nearest declared group ("Did you mean") instead. An object with no field groups gets one line: declare `fieldGroups: [{ key, label }]` and `group` on the member fields, or enumerate the section's `fields`. `print-page-block-unprintable` no longer lists the thirteen printable blocks; `os explain` does. `filter-token-unknown`'s line for a placeholder with no near miss names the two vocabularies briefly, and its `os explain` entry in full. + - **The anchor ids converge.** `page-field-unprovisioned` and `flow-template-field-unprovisioned` now print the shared one-line anchor fix the other unprovisioned-anchor ids print (`If the remote table carries 'owner_id', declare it in ext_customer's own fields (mapped through columnMap); otherwise drop the reference or opt the object out of the injection`, 175). + - **`os explain RULE_ID` carries the rest.** Each of the 8 ids gains a `RULE_EXPLANATIONS` entry in `@objectstack/lint`, so its `rule:` line now carries the `os explain` pointer. What the fix lines stopped saying is there: how to vouch for an unprovisioned anchor through `columnMap` or opt the object out of the injection (`ownership: 'none'`, `systemFields: { audit: false }`); what the start node's `config.expand` does (a one-hop re-read of the triggering record as the run's identity) and why it covers the trigger record alone; how a section inherits a field group's members, and that membership is declared on the field; the printable block list, held equal to the spec's `PRINTABLE_PAGE_COMPONENT_TYPES`, and the three reasons a block is refused; the context tokens and date macros a filter value resolves, where `{record_id}` resolves, and that a literal shaped like a placeholder must be renamed; and where a page component's props schema lives, why the page and not the schema is what to fix in almost every case, and the retired-key and node-binding cases. + - **At the runtime publish gate** (Studio, REST `PUT /api/v1/meta`, and the MCP and AI authors that write through that door), FROM → TO measured at the door. On a `page` write, as 422 `issues[].hint`: `print-page-block-unprintable` 362 → 170, `component-props-invalid`'s value-rejection line 248 → 157 (`page:tabs`), 250 → 159 (`object-grid`), 253 → 162 (`record:details`) and 258 → 167 (`record:related_list`). On a `flow` write, as 2xx `advisories[].hint`: `flow-template-field-unprovisioned` 362 → 175, `flow-template-lookup-traversal` 249 → 131 (trigger root) and 314 → 126 (flow variable), on a probe hopping through `crm_case.account`. The two page-binding ids do not fire on a `page` write, and `filter-token-unknown` and `form-section-group-unknown` run at the CLI only. No write changes channel. + - **Unchanged:** every `message` (the verdict), rule id, severity and `path`, and what each rule accepts or refuses. +- 5e97907: feat(plugin-sharing,cli)!: declared sharing rules are read from the registry — the boot writes no `sys_sharing_rule` row for them, and the 17 → 18 ownership ceremony deletes the copies the retired seeder wrote + + Clause-②: no (narrowing) + + + + The narrowings, FROM → TO, each with its one-line remedy: + + - `DELETE /api/v1/sharing/rules/:idOrName` on a declared rule — FROM `204`, the seeded row deleted (and written again by the next boot), TO `403 PERMISSION_DENIED`. Remedy: change or remove the rule in its declaration in the package; an organization's own rule is deleted as before. + - `sys_sharing_rule` — FROM one `managed_by: package` row per declared rule, TO none: the boot writes none and the ownership ceremony deletes the copies the seeder wrote (Setup's Sharing Rules object list, which lists rows, no longer shows declared rules). Remedy: read declared rules through `GET /api/v1/sharing/rules`, which lists them beside the organization's own; on an upgraded database run `os migrate organization-ownership --apply` with a fresh plan. + + The retirement note for the seeder is the ADR-0087 semantic entry `sharing-rule-declared-mirror-retired`. + + **BREAKING**, shipped as `minor` under the repo's launch-window convention for breaking changes. + + `@objectstack/plugin-sharing` no longer copies a stack's declared `sharingRules` into `sys_sharing_rule` at boot. ADR-0131 D2 retires that seeder (`bootstrapDeclaredSharingRules`). The sharing-rule service reads each declared rule from the registry whenever it reads the rule set: listing, getting, evaluating, the per-record hooks, the boot backfill and the orphaned-grant sweep. Rules an organization authors keep their `sys_sharing_rule` rows. + + - **A declared rule still grants, and stops granting when its declaration goes.** It is translated from the registry's copy exactly as the seeder translated it: the CEL `condition` is lowered to a `criteria` filter, and `sharedWith` becomes the recipient. A condition that does not lower is not served, and never as a match-all. It says so in one WARN line per rule per process. A declared rule's id is its NAME, and the grants it materialises carry the name as `sys_record_share.source_id`. Remove the declaration and the next boot's orphaned-grant sweep revokes those grants. + - **Scopes follow the posture, as the seeder's passes did.** Under `single` a declared rule is served once, organization-less, so every organization reads it. Under a wall it is served once per organization — every organization, read page by page. An organization created after boot is served from its creation on. If the organizations cannot be read at boot, no declared rule is served until the next boot, and the boot's orphaned-grant sweep revokes none of their grants meanwhile: a failed read is never a revocation. + - **A stored row of the same name answers for it.** Under `single` that is a row of that name in any organization, and under a wall that organization's own row. Such a row is an organization's own rule, a seeded copy an administrator customized, or a seeded copy the ceremony has not deleted yet. The declared rule is not served beside it, the same as while the seeder ran: it never overwrote an admin-authored or customized row. + - **`DELETE /api/v1/sharing/rules/:idOrName` refuses a declared rule** with `403 PERMISSION_DENIED`. It has no row: it is changed or removed in its declaration. Before this release a platform administrator could delete the seeded row, and the next boot wrote it again. An organization's own rule is deleted as before. + - **Setup's Sharing Rules object list no longer shows declared rules.** It lists `sys_sharing_rule` rows. `GET /api/v1/sharing/rules` lists both. + + `@objectstack/cli` — **`os migrate organization-ownership --apply` deletes the declared-rule mirrors.** The `declared-rule-mirrors` rows on `sys_sharing_rule` are the seeder's uncustomized copies (`managed_by` `package` or `platform`, `customized` not true). They now block certification until the ceremony's `mirror-deletion` step removes them. It works in the ruled order. It renames every grant naming a mirror by row id (`sys_record_share.source_id`, `source = 'rule'`) to the rule's name and verifies that no grant names a mirror any more. Only then does it delete the mirrors. Under a wall it deletes the organization-stamped copies too. + - A mirror whose rule no installed package declares, and that grants still name, blocks with nothing written. The blocker names the rows and the remedy. A mirror no grant names is deleted. + - The step also runs on a database whose ceremony already completed while an earlier release still seeded the rows, where they were advisory. Re-run `--apply` with a fresh plan. + - `DECLARED_SHARING_RULE_SEEDER_RUNS` is now `false`. A customized copy and an organization's own rule stay. Under `single` they take the Default Organization, as before. + + `@objectstack/lint` — the sharing-rule enforceability verdicts and `os explain` say what now happens to a declared rule whose condition does not lower. It is "never served", not "never seeded", because it is never written to `sys_sharing_rule`. The rule ids, severities, paths and what each rule accepts or refuses are unchanged. +- 85be049: fix(lint): `filter-token-unknown` states what the runtime does with an unresolvable filter placeholder: the data engine refuses the query with `FILTER_TOKEN_UNKNOWN` / 400 + + Clause-②: no + + The finding's message said the value "is sent to the data engine as a literal string, matches no record, and the surface renders empty". The runtime does not do that. The data engine refuses a query that carries an unresolvable placeholder, `FILTER_TOKEN_UNKNOWN` with HTTP 400, rather than compare it as a literal string. The message now reads: + + ```text + Filter value "{current_user}" is not a resolvable placeholder. The data engine refuses a query that carries it (`FILTER_TOKEN_UNKNOWN`, 400), so the surface never shows the data the filter meant. + ``` + + Unchanged: the rule id, its severity (`error`), the finding's `path`, its `hint` (the `fix:` line), and which values the rule reports. The message's first sentence is unchanged as well. `os explain filter-token-unknown` already stated the refusal and is unchanged. +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/sdui-parser@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/lint/package.json b/packages/lint/package.json index f2d1219a506..12d6acc2ca8 100644 --- a/packages/lint/package.json +++ b/packages/lint/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/lint", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Static, build-time validation for an ObjectStack metadata graph — dashboard widget bindings, CEL/predicate expressions, and more. Pure (stack) => Issue[] functions shared by the CLI's `os validate` and any other consumer (e.g. AI authoring). Depends on @objectstack/spec; never on a runtime.", "type": "module", diff --git a/packages/mcp/CHANGELOG.md b/packages/mcp/CHANGELOG.md index a1d3b531009..5c86d641850 100644 --- a/packages/mcp/CHANGELOG.md +++ b/packages/mcp/CHANGELOG.md @@ -1,5 +1,83 @@ # @objectstack/plugin-mcp-server +## 18.0.0-next.4 + +### Patch Changes + +- 3a734e0: feat(spec)!: a page region named `main` takes only `width: 'full'` — `'small'`, `'medium'` and `'large'` there are refused at publish (#24326) + + Clause-②: yes (narrowing) + + + + **BREAKING** — an accept-set narrowing on a published authoring surface, shipped as `minor` under the repo's launch-window convention. + + `PageRegionSchema.width` is one enum shared by every region, so a `main` region accepted `'small'`, `'medium'` and `'large'`. The console never read them: every layout draws `main` at the width the layout leaves it (the flexible column beside `sidebar` and `aside`; one grid cell on `dashboard`), and the only reader of `main.width` is the page itself, for `'full'`, which lifts the page's max-width cap. A three-column page that asked for a `large` main got the space the two rails left, and every door reported success. + + - `PageSchema` now refuses `width: 'small' | 'medium' | 'large'` on a region named `main` (matched case-insensitively, as the console matches it), at `regions.N.width`, on every page kind and template. The message names the region and `'full'` as the one width it takes. The doors are `definePage`, `defineStack` (so `os validate` and `os build`, `STACK_SCHEMA_INVALID` 422) and the metadata save door (422 `INVALID_METADATA`). + - `'full'` on `main` is unchanged, with its page-wide meaning. Every other region accepts the same widths as before. + - The check is exported as `checkPageMainRegionWidth` from `@objectstack/spec/ui`, so a mirror built from `PageSchema.shape` can attach the same rule. + - Data at rest keeps loading: the retired ADR-0087 conversion `page-main-region-width-removed` deletes those widths on read: a stored page row, a compiled artifact inside the window its declared protocol range opens, and a package body registered with its declared range. The page renders exactly as before. + - `@objectstack/mcp` (the Connect an Agent page) and `@objectstack/cloud-connection` (the Cloud Connection page) drop `width: 'large'` from their `main` region. Both render the same. + + ### Migration: FROM → TO + + | Wrote | Write instead | + | :--- | :--- | + | `width: 'small' \| 'medium' \| 'large'` on a region named `main` | drop it; `'full'` stays | + + Deleting the key changes nothing on screen. Write `width: 'full'` only when you want a full-bleed page, which drops the page's max-width cap. +- 71fa166: Clause-②: no + + fix(mcp): the known-partial listing warning and the Connect-an-Agent bundle description say "this system" + + - **What changed.** Inside the running system, user-facing copy names the system the caller is in as "this system". The warning an MCP client receives with a known-partial object listing (the `objectstack://objects` resource and the `list_objects` tool) now says "this system declares at least that many objects, possibly more", and the Connect-an-Agent UI bundle's description ends "for connecting MCP clients to this system". + - **Unchanged.** The `SERVICE_UNAVAILABLE` code that marks a partial listing, every payload shape, the portable `SKILL.md` (which names the deployment an outside agent connects to), and server log lines. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 9e9872f5282..3a0906c2296 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/mcp", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack as an MCP server — exposes your app's objects (and AI tools) over the Model Context Protocol (stdio + Streamable HTTP)", "type": "module", diff --git a/packages/metadata-core/CHANGELOG.md b/packages/metadata-core/CHANGELOG.md index bc077f83cbf..c75a05fe8ef 100644 --- a/packages/metadata-core/CHANGELOG.md +++ b/packages/metadata-core/CHANGELOG.md @@ -1,5 +1,58 @@ # @objectstack/metadata-core +## 18.0.0-next.4 + +### Patch Changes + +- 31e7d0b: feat(spec): an object can declare its default row scope, `defaultRowScope`, the row-level security policy shape without `object` + + Clause-②: yes (widening) + + `ObjectSchema` gains one optional key, `defaultRowScope`: one row-level security policy, written like a permission set's `rowLevelSecurity` entry without `object`, which the declaring object implies. The shape is exported as `DefaultRowScopeSchema` from `@objectstack/spec/security` (types `DefaultRowScope` and `DefaultRowScopeParsed`). + + - **What it parses.** Every member of `RowLevelSecurityPolicySchema` except `object`, the same member schemas: `name`, `label`, `description`, `operation`, `using`, `check`, `positions` and `enabled` (default `true`). The policy's two rules hold as written: at least one of `using` / `check`, and no `check` on a `select` or `delete` policy. + - **What it refuses.** `object` is refused by name, with a prescription to delete it: the target is implied. Any other unknown key is refused too, the policy's retired `priority` and `tags` included. + - **What it does today: nothing.** No server reads the key in this release, so declaring it filters no rows. The describe states the contract the security plugin's row-policy collection is to meet once it reads the key: the default is to apply when no permission set the caller holds carries a row policy for the object; a policy an authored set carries is to be used as authored; a superuser-bypass principal is to be unaffected; an object without the key keeps its behaviour. No object declares the key yet; the platform's identity objects are to declare it for their own tables. Authoring the key raises the liveness lint's `liveness-planned-property` warning until then. Restrict rows with a permission set's `rowLevelSecurity` policy in the meantime. + - **`composeStacks` with `objectConflict: 'merge'`** now refuses two stacks that declare a different `defaultRowScope` on the same object, the way it refuses every other fixed-shape config object (`access`, `enable`, `publicSharing`, …). The refusal message's list of those objects names `defaultRowScope` too. Nothing composed before is affected, because no stack declared the key. + - **`@objectstack/metadata-core`: the field-level-security projection classifies the new key.** When an object document is served to a caller who cannot read some of its fields, `defaultRowScope` is treated like a validation rule: the whole policy is dropped from the served document when its `using`, `check` or prose names a field that caller cannot read, and served unchanged otherwise. No behaviour moves until an object declares the key, and no object declares it yet. + - Additive: every object that parsed before still parses, and its parsed output is unchanged. +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/metadata-core/package.json b/packages/metadata-core/package.json index 8565c2675b8..c0056a58eb0 100644 --- a/packages/metadata-core/package.json +++ b/packages/metadata-core/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/metadata-core", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Metadata Repository contracts: types, canonicalization, errors, interface (ADR-0008).", "type": "module", diff --git a/packages/metadata-fs/CHANGELOG.md b/packages/metadata-fs/CHANGELOG.md index 1ee9fb8840b..178cd8d8182 100644 --- a/packages/metadata-fs/CHANGELOG.md +++ b/packages/metadata-fs/CHANGELOG.md @@ -1,5 +1,12 @@ # @objectstack/metadata-fs +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [31e7d0b] + - @objectstack/metadata-core@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/metadata-fs/package.json b/packages/metadata-fs/package.json index be5f59211c7..cdcb04af652 100644 --- a/packages/metadata-fs/package.json +++ b/packages/metadata-fs/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/metadata-fs", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "FileSystemRepository: Node-only Repository implementation backed by JSON files and a JSONL change log (ADR-0008).", "type": "module", diff --git a/packages/metadata-protocol/CHANGELOG.md b/packages/metadata-protocol/CHANGELOG.md index 719392b2979..9469bb450e2 100644 --- a/packages/metadata-protocol/CHANGELOG.md +++ b/packages/metadata-protocol/CHANGELOG.md @@ -1,5 +1,174 @@ # @objectstack/metadata-protocol +## 18.0.0-next.4 + +### Minor Changes + +- f3174ef: fix(metadata-protocol)!: `installPackage` judges the whole manifest by the install door's declaration, so the primitive refuses a manifest `POST /api/v1/packages` refuses + + Clause-②: no (narrowing) + + + + **BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + `ObjectStackProtocolImplementation.installPackage` parsed only `manifest.id`, so an in-process caller stored manifests the install door answers `400 VALIDATION_ERROR` for. The primitive now parses the manifest through `PackageInstallManifestSchema` (`@objectstack/spec/api`, `ManifestSchema` with `scope` narrowed to `project`), the declaration the door parses, after its version default and before any write. + + - **FROM:** `protocol.installPackage({ manifest })` with `type: 'application'`, `scope: 'environment'`, `scope: 'cloud'` / `'system'`, no `type`, or a key the manifest does not declare (such as a metadata collection, `pages`) registered the package and wrote its `sys_packages` row. + - **TO:** the call throws with `statusCode: 400` (an HTTP boundary answers `VALIDATION_ERROR`), the message names each refused key path (`manifest.type`, `manifest.scope`, …) with the declaration's own sentence, and neither the registry nor `sys_packages` is written. A malformed `manifest.id` still answers first, with its own sentence and repair. + - **Unchanged:** a manifest with no `version` still installs with `version: '0.1.0'`, a missing `namespace` is still derived from the id, and the manifest is stored as sent (the parse's `scope` / `defaultDatasource` defaults are not copied onto it). + - **Rows already stored** with `type: 'application'` / `scope: 'environment'` keep hydrating at boot, as stored. A new write of the same manifest, including a `duplicatePackage` of such a base, is refused. + - **The fix for a caller:** write a declared `type` (`app` for an application package) and leave `scope` out, or write `scope: 'project'`. +- a06f0d4: fix(metadata-protocol)!: the by-name `/meta` read refuses an object of a disabled package with `404 OBJECT_PACKAGE_DISABLED`, as the object list and the data plane already do + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + Disabling a package already took its objects out of `GET /meta/objects` and refused their data with `404 OBJECT_PACKAGE_DISABLED`. The by-name read still served them. It now gives the same answer. + + - **Refused now:** `getMetaItem({ type: 'object', name })` (`GET /api/v1/meta/object/:name`, the plural spelling included) for an object whose owning package is disabled. The refusal applies to every state the read serves: the active read, `state: 'draft'` and `previewDrafts`. `getMetaItemLayered` (`?layers=true`) refuses it too. The answer is `404` with `code: 'OBJECT_PACKAGE_DISABLED'` and `object`, built by the same constructor as the data plane's refusal, so the two cannot drift. + - **Unchanged:** objects of enabled packages, other metadata types, and the registry's resolution reads (`registry.getObject`, `registry.listItems('object')`), which keep serving a disabled package's objects for migrations and cross-package references. + - **Still checked:** saving an object of a disabled package still runs the destructive-change check. A save that drops a field still answers `409 DESTRUCTIVE_CHANGE` until it is confirmed with `force`. The check reads the object's prior schema through the protocol's internal read, not the refusing door. + + What changes for you: a client that read a disabled package's object by name now gets `404 OBJECT_PACKAGE_DISABLED`. Re-enable the package to read it again. +- 2bcd406: fix(metadata-protocol)!: the by-name `/meta` read answers a disabled package's item of every type but `object` and `package` as absent, as the list already omits it + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + Disabling a package already took its apps, views, pages, dashboards, flows and every other item out of `GET /meta/:type`. The by-name read `GET /meta/:type/:name` still served them. It now answers such an item the way it answers a name with nothing behind it. + + - **Absent now:** `getMetaItem({ type, name })` for an item whose `_packageId` names a disabled package, for every type except `object` and `package`. The active read and `previewDrafts` serve no `item`, which both transports answer `404 RESOURCE_NOT_FOUND`, byte for byte the answer for a missing name. The strict draft read (`state: 'draft'`) throws the missing draft's `404 NO_DRAFT`. The list read and the by-name read ask one predicate, so they hide the same items. + - **Kept:** the protection envelope (`lock`, `editable`, `deletable`, `version` and the rest) still reports the write doors' verdicts about the address, because a disable does not change how the lock gate judges a save there. Only `item` and a flow's `registration` are withheld. + - **Unchanged:** objects, which keep their `404 OBJECT_PACKAGE_DISABLED`; packages, which are never hidden, so a disabled package can still be read and re-enabled; items of enabled packages; and `getMetaItemLayered` (`?layers=true`, `/layers`) and `/published`, which still serve a disabled package's non-object item. + + What changes for you: a client that read a disabled package's app, view, page or other item by name now gets `404 RESOURCE_NOT_FOUND`, as for a missing name. Re-enable the package to read it again. +- e74ec56: fix(metadata-protocol)!: `listDrafts` omits a disabled package's drafts, as the list read omits its items + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + `GET /meta/_drafts` (both transports) listed a disabled package's pending drafts, while the by-name draft read answers them `404 NO_DRAFT` and the list read omits the package's items. + + - **Omitted now:** every draft whose `packageId` names a disabled package, with or without `?packageId=`. The predicate is the one the list and by-name reads ask. Drafts of the `package` type are never hidden. + - **Unchanged:** the package batch doors (`POST /packages/:id/publish-drafts`, `/discard-drafts`) still publish and discard a disabled package's drafts; they read the store directly. Other packages' drafts are listed as before. + + What changes for you: re-enable the package to see its pending drafts in the list again. +- 1e001c7: feat(metadata-protocol): `publishPackageDrafts` takes an opt-in `backfill` that writes the literal `defaultValue` of fields the publish adds into the object's existing rows, as the caller + + Clause-②: yes (widening) + + - **New request keys.** `backfill?: Array<{ object, field }>` names fields this publish adds; `context` is the caller's execution context the writes run as. Absent or `[]`, no row is touched. + - **Judged before anything is published.** Every entry must name an object drafted in the package, a field its draft declares, a field the published object does not have yet, and a literal `defaultValue` (a runtime token such as `NOW()` or `current_user`, or an expression, is resolved per new record and is refused). A malformed entry, a duplicate, or a backfill without a caller context is refused too. All problems come back in one `400 VALIDATION_ERROR`, one `issues[]` entry per problem with its `backfill.N` path, and nothing is promoted or written. + - **Written after the publish, as the caller.** Once the drafts are live and the new column exists, rows whose value is absent are read and written 200 at a time through `updateManyData` with the caller's context, so permissions, RLS/FLS, validation and record hooks run as on that caller's own edit. A value already stored is never overwritten. + - **New response key `backfillApplied`:** `{ success, fields: [{ object, field, updated, error?, code? }] }`, one element per requested field. `updated` counts the rows this publish wrote. A field stops at its first failing row and reports the rows written so far with the error; the publish is not undone. On the SQL driver the new column's DEFAULT already fills existing rows, so `updated` is 0 there. +- 41c7405: fix(metadata-protocol)!: an organization-scoped seed load refuses a row whose natural key a row with no organization already holds, by name in the seed result, instead of inserting a twin of it + + Clause-②: no (narrowing) + + + + **BREAKING** (a seed load writes fewer rows), shipped as `minor` under the launch-window convention for breaking changes. + + An organization-scoped seed load (`SeedLoaderService` with `config.organizationId`: the per-organization replay, the marketplace walled install and the sample-data reseed) looked up existing rows by natural key among that organization's rows only. A row holding the same natural key with `organization_id` NULL, as a database whose rows predate organization stamping holds them, was invisible to that lookup, so the upsert inserted a twin beside it that carried the organization. Nothing was reported: 0 errors, nothing logged. On a HotCRM 4.0.1 → 5.0.0 upgrade this doubled every seeded event, task, attendee and line item. + + - **FROM:** in `upsert` or `ignore` mode, with `config.organizationId` set, a seed record whose natural key no row of that organization holds, but a row with no organization does, was inserted as a new row for the organization. + - **TO:** that record is refused. The load reports it in `errors` with the existing `ReferenceResolutionError` shape: `field: 'organization_id'`, `targetObject` the seeded object itself, `targetField` the natural key, `attemptedValue` its value, and a message that names the row holding the key and the remedy. It counts as errored, the load answers `success: false`, and the refusal is logged at `error`, once per dataset. The row with no organization is left exactly as it is: the loader never adopts it. Every other record still loads. + - **Unchanged:** a key the organization's own row holds is matched as before, even when an unowned row also holds it. Another organization's rows stay invisible to the lookup. A fully stamped database loads with 0 inserts. `update` mode (which never inserts) and the `insert` / `replace` modes are unchanged, and so is a load with no `config.organizationId` (the single-posture boot), which still matches the unowned row. + + What to do. Attribute the rows that have no organization with `os migrate organization-ownership` (plan, then `--apply`), then run the seed again: each row is then the organization's own, and the upsert matches it. + +### Patch Changes + +- e46ce7b: Clause-②: no + + fix(metadata-protocol): the global search palette titles a hit by its `nameField` before the deprecated `titleFormat` + + - **What changed.** `searchAll` (`GET /api/v1/search`) now resolves a hit's `title` in this order: the primary-title pointer (`nameField`, or the deprecated alias `displayNameField`), then the `titleFormat` template, then the conventional name fields, then first/last name, then the record id. Before, `titleFormat` came first. ADR-0079 D3 and the `titleFormat` schema description both say an explicit `nameField` takes precedence. + - **What it means for a deployment.** An object that declares both keys is now titled by its `nameField` value. For example, an approval request shows `Discount Sign-off · opp_45` instead of `flow:deal_desk · opp_45`. The registry also designates a `nameField` for any object that has a title-eligible field (a text-like field, or a formula returning text). So an object that declares only `titleFormat` is now titled by that designated field too. The template still titles a row when the object has no pointer, or when the pointer's value is empty on that row. + - **Fix for an author who wants the composite title back:** add a `formula` field with `returnType: 'text'` that builds the composite, set `nameField` to it, and drop `titleFormat`. This is the migration ADR-0079 prescribes (`os explain title-format-retired`). + - **`@objectstack/spec`:** the description of `SearchAllHitSchema.title` now states the same order. No schema shape changed. +- ec749fb: fix(metadata-protocol): global search judges an object's searchable set without its `internal: true` fields, the set the engine scans + + Global search (`searchAll`, `GET /api/v1/search`) passes over an object with nothing to scan before it queries. It judged that from the object's fields including `internal: true` ones, which the engine never searches. An object whose only text field is internal therefore passed the skip and reached the engine, which now refuses a search over an empty set, so the whole sweep would have failed. The skip now leaves internal fields out, so such an object is passed over and the sweep answers `200` with the other objects' hits. + + What to do. Nothing. +- cf540a7: Clause-②: no + + fix(metadata-protocol): the save door's lock refusals and its default-record-page refusal say "authored in this system" + + - **What changed.** Three refusal texts said an item saved through the metadata API is "authored in this environment". They now say "authored in this system": the `protection`-block refusal, the lock-family-key refusal, and the holder text a default-record-page conflict prints for a page no package ships (`page 'NAME', authored in this system`). Inside the running system, user-facing copy names the system the user is in as "this system". + - **Unchanged.** Every error code (`VALIDATION_ERROR`, `DEFAULT_RECORD_PAGE_CONFLICT`), status and `issues[]` entry. +- 1827571: Clause-②: no + + perf(metadata-protocol): the by-name item read reads the item's stored active rows once, so a `GET /api/v1/data/:object` makes one `sys_metadata` read of the object instead of five + + - **What changed.** `getMetaItem` (the read the API-exposure gate makes on every `/data` request) used to send its three active-row selections to the store one at a time: which package the item belongs to, the served row once per spelling of the type (`object`, then `objects`), and the rows its lock is chosen from, once per spelling. Now it reads the item's active rows once, for both spellings (`type IN ('object', 'objects')`), and each selection picks its rows from that one read with the same conditions it used to send the store. The item read's `version` reuses the package binding the read already selected. + - **Measured.** On Postgres behind a 50 ms round-trip proxy, the median `GET /api/v1/data/todo_task` (example todo app, signed-in admin) went from 551 ms with 5 `sys_metadata` statements to 340 ms with 1. + - **Unchanged.** The served item, its lock and `version`, and every refusal stay the same. Nothing is cached across calls, so a metadata write is seen by the next request. An object with a stored row still has one more read behind `version` (the repository's write-address head). +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [9c74d40] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [5e97907] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [85be049] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/lint@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + - @objectstack/sdui-parser@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/metadata-protocol/package.json b/packages/metadata-protocol/package.json index ee2a4915652..9d6b1013ce4 100644 --- a/packages/metadata-protocol/package.json +++ b/packages/metadata-protocol/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/metadata-protocol", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack metadata management protocol: sys_metadata CRUD, draft/publish, locks, package ownership, diagnostics (ADR-0076).", "type": "module", diff --git a/packages/metadata/CHANGELOG.md b/packages/metadata/CHANGELOG.md index 85f5c1c9d41..2c48f168f9f 100644 --- a/packages/metadata/CHANGELOG.md +++ b/packages/metadata/CHANGELOG.md @@ -1,5 +1,54 @@ # @objectstack/metadata +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + - @objectstack/metadata-fs@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/metadata/package.json b/packages/metadata/package.json index f8039894d61..c4073d1178f 100644 --- a/packages/metadata/package.json +++ b/packages/metadata/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/metadata", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Metadata loading, saving, and persistence for ObjectStack", "type": "module", diff --git a/packages/objectql/CHANGELOG.md b/packages/objectql/CHANGELOG.md index 88f0de52312..4a750b98a13 100644 --- a/packages/objectql/CHANGELOG.md +++ b/packages/objectql/CHANGELOG.md @@ -1,5 +1,186 @@ # @objectstack/objectql +## 18.0.0-next.4 + +### Minor Changes + +- eebaf3b: feat(objectql,core): the driver's tenant read scope is the organization wall's verdict — one predicate for Layer 0 and every driver (ADR-0131 D8) + + Clause-②: yes (widening) + + - **What changed.** The engine no longer derives `DriverOptions.tenantId` / `tenantIds` beside the organization wall. `buildDriverOptions` projects the wall's own verdict onto them: the verdict plugin-security records on the operation (`OperationContext.tenantLayer0Verdict`) where its wall ran, and otherwise the same function over the engine's own facts (the posture in force, the context's organization and membership set). The function, `computeTenantLayer0Verdict` with its input type `TenantLayer0Input`, moves to `@objectstack/core` (new export); `@objectstack/plugin-security`'s `tenant-layer.ts` re-exports it and its Layer 0 is byte-identical. + - **FROM → TO, per posture, for a read, count, aggregate, update or delete on an object with an organization column:** + + | posture / caller | driver scope before | driver scope now | + |:--|:--|:--| + | `single`, any caller naming an organization | that organization (plus organization-less rows) | **none** | + | `isolated`, member | the active organization | unchanged | + | `group`, member | the membership set | unchanged | + | walled, exempt `PLATFORM_ADMIN` on a posture-permitting object, or the verified platform owner's read | the active organization | **none** — what Layer 0 already admitted | + | walled, system context naming an organization | that organization | unchanged | + + - **Not changed.** Inserts and upserts keep the active organization as `tenantId` in every posture: the row is stamped with it, its counter scopes by it, and an upsert that would merge into another organization's row is still refused. Both `orWhereNull` compatibility arms in `SqlDriver.applyTenantScope` stay (their retirement is a later stage of ADR-0131 D13). + - **Who notices.** A `single` deployment that holds more than one organization was kept apart by the driver only by accident; postures now differ only in enforcement (ADR-0131 D11), and `single` enforces no wall. To keep organizations apart, run the `isolated` or `group` posture. Code that relied on a context organization to narrow a read on `single` names the organization in its own `where`. + - **Migration.** ADR-0087 semantic entry `driver-read-scope-follows-tenant-wall`. A deployment's `platformGlobalObjects` carve-out already reaches the driver through the column plan (ADR-0131 D7): a declared object carries no organization column, so neither wall has anything to scope. +- d537a31: feat(objectql)!: a formula field reads one hop through a reference field — `record.book.title` on a loan now reads the book's title instead of `null` + + Clause-②: no (narrowing) + + + + The read path (`find`, `findOne`, and the record a create or update hands back) now preloads the related fields a formula names ONE hop through a lookup or master-detail field, before it computes the formula. A junction object's composed title, `record.book.title + ' · ' + record.reader.name`, shows both names. + + - **How it reads.** One batched read per reference field per page of rows, never one per row, naming only `id` and the related fields the formulas name. The loader is the one validation rules already use for `record.LOOKUP.FIELD`. The row a read returns still holds the foreign key; only the formula sees the related record. + - **As whom.** The hop is read under the reader's own permissions (row-level and field-level security, the organization wall). A related record the reader may not see reads as not found, and a related field it may not see reads as empty, so a formula never shows the reader a value they could not read directly. + - **When there is no related record.** A reference to a record that is not found, or that the reader may not read, leaves the formula null and reports why, naming the related object and field. An empty reference is evaluated: write `record.reader.?name.orValue('—')` to give the formula a value while the reference is empty. + - **One hop only.** A second hop (`record.book.shelf.code`) and a reference read both through and as a plain value are not evaluated: the field reads null and the reported reason uses the same wording the lint uses. A formula on the related object that reads a hop of its own reads null on a hop read, so two objects whose titles read each other terminate. + - **Sorting and search.** A formula value is stored nowhere, so a formula title still cannot be sorted by (`400 INVALID_SORT`) or searched, as before. + + What to do. Nothing for a formula that reads its own fields. A formula that reads through a reference now shows a value. One that reads two hops should read a field stored on the related object instead. +- 1bf5f6e: feat(objectql): evaluate a formula field over a record in hand the way a read serves it, one hop through a reference included + + A formula title over lookups (`record.book.title + ' · ' + record.reader.name`) is computed on every read since the formula read path reads one hop. A record no read returned, such as the state a hook fires on, had no way to get the same value: the synchronous `evaluateFormulaField` reads no hop, so it answered `null`. + + - **`ScopedContext.serveFormulaField(objectName, record, field)`** evaluates one declared formula field over `record` with the read path's own evaluation, under that context: the one hop it reads through a reference (one batched read per reference field, through the reader's own permissions, on the context's transaction when it carries one), the `can()` permission map, and the fault report. A related record the reader may not read, a missing one and a second hop leave the field `null` with the read's reason, as a read does. `ObjectQL.serveFormulaField(objectName, record, field, context)` is the engine method behind it. + - **`resolveServedRecordTitle(schema, record, channel)`** is the async sibling of `resolveRecordTitle`: this record's title, with a formula title served on `channel` (a `ScopedContext`). `resolveRecordTitle` is unchanged and still reads no hop. + - **`servedRecordTitle(schema, row)`** reads the title a read already served off the row it returned, instead of re-deriving it. + + What to do. Nothing. Code that names a record it holds in hand and wants the title a read shows calls `resolveServedRecordTitle` with the caller's `ScopedContext`. +- ec749fb: feat(objectql)!: a `search` over an object with no searchable field is refused with `400 INVALID_FIELD` instead of answering every row + + Clause-②: no (narrowing) + + + + A search term is matched against the object's resolved search fields: its `searchableFields` declaration (entries that name a stored field), or else the default set (the name/title field and the text, email, phone, url, autonumber, textarea, markdown and select fields). On an object where that set is empty, such as a junction object made of two lookups and a formula title, the term used to add no filter, so `?search=zzzz-nothing` and `?search=Dune` returned the same rows, and a user read every row as a match. + + - **What changes.** `find`, `findOne`, `count` and `aggregate` answer a non-blank `search` over an empty resolved set with `400 INVALID_FIELD` (`param: 'search'`). That covers every door that reaches the engine: the REST list and query routes (and the list's `total`), the grouped read, the dispatcher, the SDK and a hook's `ctx.api`. The message names the object, says why the set is empty, and gives the two remedies. + - **Unchanged.** A blank or whitespace term is not a search and still answers every row. An object with a searchable field filters as before. An explicit `searchFields` naming a field outside the set keeps its own `400 INVALID_FIELD` at the data door. Global search (`GET /api/v1/search`) still passes over an object with nothing to scan and answers `200`. + + What to do. If a read now answers `400 INVALID_FIELD` with "has no searchable field", declare `searchableFields` on that object naming a stored field, or mirror the composed title onto a stored text field and search that. A client that offers a search box on such an object should hide it, or show the refusal. +- 2f569a9: fix(objectql)!: an `objectExtensions` entry no longer adds platform system columns to the object it extends, so an extension of `sys_organization` or `sys_user` no longer hides the record from its own organisation + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The registry ran the injected-system-columns plan on every contributor, an extension body included. An extension body carries none of the keys the plan reads (`managedBy`, `systemFields`, `tenancy`, `ownership`), so it was planned as an ordinary object, and the merge added `organization_id`, `created_by` and `updated_by` (and on a non-`sys_` target, `owner_id` and `owning_business_unit_id`) to a target whose own declaration withholds them. On a multi-tenant deployment an extension of `sys_organization` or `sys_user` (`managedBy: 'better-auth'`) therefore created an `organization_id` column that is NULL on every row, and the tenant wall then answered a member's list of their organisation and of the users with `[]`, and their own records `404 RECORD_NOT_FOUND` by id. The injected definition also replaced a column the target declares itself. + + Now only a base layer (the owner, or a tenant overlay) is planned. The extended object's system columns are its base layer's, in whichever order the two packages register. + + - **Served less now:** an extended object whose declaration withholds a system column no longer reports or stores it. This covers `managedBy: 'better-auth'`, `systemFields: false`, `tenancy.enabled: false` and an `ownership` other than `user`. For example, `sys_sso_provider` (which plugin-auth extends with `oidc_client_secret`) no longer carries `created_by` or `updated_by`, and its `organization_id` is again the `text` column the object declares instead of the platform's read-only lookup. + - **Unchanged:** the extension's own fields, labels, validations and indexes merge exactly as before. An audit field an extension declares (`created_at`, `created_by`, `updated_at`, `updated_by`) is still forced read-only wherever the target carries the audit family. An extended ordinary object carries the same system columns, which come from its owner. No existing column is dropped from a database: a column an earlier release created stays in the table, and the registry no longer declares it. + + What changes for you: nothing to rewrite. If code read `organization_id`, `created_by` or `updated_by` on an extended `sys_*` identity object, it read a NULL column that the object never declared. Read the owner's own columns instead. +- 69147ab: fix(objectql)!: the one-hop read through a reference never reads a stored-metadata-body table, and the lint refuses that read where it is written + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The tables of the stored-metadata-body family (`STORED_METADATA_BODY_OBJECTS`) are projected or refused at every door that serves them. The one-hop read a formula field and a validation rule share (`resolveOneHopRelated`) was an ordinary engine read, which the family's projection does not reach. + + - **`@objectstack/objectql`.** A hop whose target is a family table is not read, whichever authority the hop is read under. A formula field reading through it is `null` with its reason, on every read, write response and `serveFormulaField` call (the seam a hook body's `ctx.title()` reads through). A validation rule reading through it has no verdict, so the write it judges is refused. The answer is the one an unreadable related record gets, so it says nothing about the row. A hop into any other object is unchanged. + - **`@objectstack/lint`.** A formula field's `expression`, or a validation rule's hydrated `condition`, that reads through a reference into a family table is an error, with the prescription. `os validate`, `os build` and the object save door run the same pass, so the author learns it at save rather than from a `null`. + + What to do. Remove the read through the reference. Stored metadata is read through the metadata API (`GET /api/v1/meta/:type/:name`), where its body is projected. Reading the reference field itself (the stored id) is unchanged. +- cf16a0d: fix(objectql)!: `expand` and the master-detail `parent` root never hydrate a stored-metadata-body table + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The tables of the stored-metadata-body family (`STORED_METADATA_BODY_OBJECTS`) are projected or refused at every door that serves them, but only for the object a request names. Two relationship hydrations in the engine read a family row as an ordinary row, which that projection does not reach. + + - **`expand`.** A reference field whose target is a family table is not expanded, at any depth and for any caller: the field keeps its stored id, the answer a reference the caller may not expand already gets. This covers `expand` / `$expand` on the data read, `findOne`, the nested query form and a hook body's `ctx.api` read. Expanding a reference into any other object is unchanged. + - **The master-detail `parent` root.** A detail whose master is a family table never reads its header. `parent` is unbound, so a `requiredWhen` or `readonlyWhen` that reads it fails closed exactly as it does for a header the caller cannot read, on insert, update and the `validate()` preview, and the verdict never depends on the row. A master outside the family is unchanged. + + What to do. Read stored metadata through the metadata API (`GET /api/v1/meta/:type/:name`), where its body is projected, rather than by expanding a reference to it or reading it as a master-detail header. The stored id on the reference field is unchanged. + +### Patch Changes + +- 2ac0616: refactor(objectql): the record validator reads `isMissing` / `isEmptyForRequired` from `@objectstack/core` instead of private copies + + Clause-②: no + + - **No behaviour change.** The object-level `required` check, the `required_cleared` PATCH check, the blank-to-`null` normalisation of typed columns and the value-shape scan judge "missing" exactly as before; the private copies of the two predicates are deleted and `@objectstack/core`'s exports, moved there unchanged, are imported at every read site. + - **Why:** `@objectstack/rest`'s public-form submit route now judges a form's `required` by the same rule, and it cannot import the engine. +- aa31569: Clause-②: no + + fix(objectql): withdrawing a package that extends another package's object puts the owner's definition back at `GET /api/v1/meta/object/NAME` at once, not at the next restart + + - **What was wrong.** When a package whose content is an `objectExtensions` field on another package's object is registered on a started kernel, the ObjectQL bridge rewrites the OWNER's copy of that object in the `metadata` service with the merged body, extension included. `DELETE /api/v1/marketplace/install-local/:manifestId` of the extender withdrew its contribution from the registry, so the object listing and the data plane dropped the field, but nothing rewrote the owner's copy. The by-name read asks that copy first, so `GET /api/v1/meta/object/NAME` kept serving the withdrawn field until the runtime restarted. + - **What changes.** The ObjectQL bridge remembers which owners' copies each package's extensions rewrote, and the `manifest` service gains `restoreExtendedObjectCopies(packageId)`: once the registry no longer holds the package, it re-registers each such copy with the registry's current definition. The install-local DELETE calls it right after the registry withdrawal. The by-name read then serves the owner's body without the extension's fields, as the listing and the data plane already did. + - **Not touched.** Only a copy stamped with the registry owner's id is rewritten, the same rule the bridge overwrites by, so an authored copy stays. The DELETE answer is unchanged. A copy that cannot be restored is logged once at `error`, naming the objects and the remedy: restart the runtime. +- cf540a7: Clause-②: no + + fix(objectql): the default-record-page and security-catalog refusals name an item made here, not shipped by a package, as "authored in this system" + + - **What changed.** The holder text a `DEFAULT_RECORD_PAGE_CONFLICT` refusal prints for a page no package ships now reads `page "NAME", authored in this system`, and the holder text a `NAMESPACE_CONFLICT` refusal prints for the environment catalog reads `the environment catalog (an item authored in this system, not shipped by a package)`. Both said "authored in this environment". Inside the running system, user-facing copy names the system the user is in as "this system". + - **Unchanged.** Every error code, status and envelope field (`existing`, `incoming`, `existingHolder: { kind: 'environment' }`). "The environment catalog" is the documented name of that holder (ADR-0048), so it stays. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [f3174ef] +- Updated dependencies [e46ce7b] +- Updated dependencies [a06f0d4] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [ec749fb] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [41c7405] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [1827571] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/metadata-protocol@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/objectql/package.json b/packages/objectql/package.json index a219b723657..079fdf290c6 100644 --- a/packages/objectql/package.json +++ b/packages/objectql/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/objectql", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Isomorphic ObjectQL Engine for ObjectStack", "main": "dist/index.js", diff --git a/packages/observability/CHANGELOG.md b/packages/observability/CHANGELOG.md index 7c18404a0fc..9c5bb34f826 100644 --- a/packages/observability/CHANGELOG.md +++ b/packages/observability/CHANGELOG.md @@ -1,5 +1,46 @@ # @objectstack/observability +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/observability/package.json b/packages/observability/package.json index 92f17de4afc..9e93f8312e2 100644 --- a/packages/observability/package.json +++ b/packages/observability/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/observability", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Observability contracts and exporters for ObjectStack — MetricsRegistry, ErrorReporter, Logger plus noop/console/OTLP-HTTP exporters. Deployment-target neutral; runtime and services depend on this so the same instrumentation works on Cloudflare Workers, Node, and self-hosted Kubernetes.", "type": "module", diff --git a/packages/platform-objects/CHANGELOG.md b/packages/platform-objects/CHANGELOG.md index 35a8256bff6..26b4fd51706 100644 --- a/packages/platform-objects/CHANGELOG.md +++ b/packages/platform-objects/CHANGELOG.md @@ -1,5 +1,137 @@ # @objectstack/platform-objects +## 18.0.0-next.4 + +### Minor Changes + +- 1173017: `sys_organization` now declares a `rename_organization` action ("Rename Organization") at `record_header` and `list_item`, so an organization record page can rename the organization. It collects `name` (required) and `logo`, both pre-filled from the record, and no `slug`. It calls better-auth's `/api/v1/auth/organization/update` exactly as `update_organization` does (the record's id as `organizationId`, the fields under `data`), and it is gated on `multiOrgEnabled` like the other organization actions. Because the request carries no slug, the organization keeps its slug. It is declared before `change_slug`, so on the record header it takes the third inline slot and `change_slug` moves into the `⋯` overflow menu. + + `update_organization` (name, slug and logo, list row only) and `change_slug` are unchanged. + + `@objectstack/spec` books the new action in `PUBLIC_AUTH_FEATURES.multiOrgEnabled.gatedInputs`, beside `update_organization`. + + Clause-②: yes +- f573976: `sys_organization`'s organisation-admin actions are now offered only to the membership grades whose requests the door accepts. Before, they were gated on `multiOrgEnabled` alone, so a plain member saw them, and better-auth refused every click with 403: + + - `update_organization` ("Edit Organization", list row) and `rename_organization` ("Rename Organization", record header and list row) declare `requiresMembershipReach: 'update_organization'`: owner and admin. + - `delete_organization` ("Delete Organization", list row) declares `requiresMembershipReach: 'delete_organization'`: the owner alone. + + The `multiOrgEnabled` gate stays as the last term of each predicate. `invite_user` is unchanged, and `set_active_organization`, `leave_organization`, `create_organization` and `change_slug` stay as they were. + + Clause-②: yes (widening) + +### Patch Changes + +- f7af839: Clause-②: no + + Setup's Diagnostics group has one audit entry, "Audit Log", and it opens the audit browser + + - **What changed.** `@objectstack/plugin-audit` contributed two Setup entries onto the same rows: "Audit Logs" (the `sys_audit_log` record list) and "Audit Log Browser" (the console's `audit:log` page). It now contributes one, "Audit Log", which opens the browser. The browser filters by action, object, actor and date, offers the record list's views (Writes, Auth, Record Views, …) as presets, and links to the record list ("Open as list"). Those two parts of the page ship in the console this release pins. + - **Ids.** The entry keeps the id `nav_audit_logs`, so a sidebar pin a user set on "Audit Logs" stays on the new entry. The id `nav_audit_log_browser` is no longer served; a pin set on it no longer shows, and the user can pin "Audit Log" instead. + - **Labels.** `@objectstack/platform-objects` serves the entry's label in the four shipped locales (Audit Log, 审计日志, 監査ログ, Registro de auditoría), and drops the "Audit Log Browser" label. + - **Unchanged.** The `sys_audit_log` object, its list views, its routes and its data API answer as before. The record list is no longer an entry in Setup's menu, so the ⌘K palette's record search in Setup no longer searches audit rows. +- a237d7f: feat(cloud-connection)!: Setup's "Installed Apps" entry opens Setup › Packages filtered to marketplace installs, and the separate Installed Apps page is no longer shipped + + Clause-②: no (narrowing) + + + + - `MARKETPLACE_INSTALLED_UI_BUNDLE.pages` -> delete (the bundle carries no page; its entry is read from `navigationContributions`) + + Setup › Packages is now the one list of what is installed: every row says where the package came from (Source: code, marketplace or authored), and the rows whose Source is marketplace carry the actions the Installed Apps page had, Details and Uninstall. + + - **What changes.** The "Installed Apps" entry in Setup's Apps group, which the install-local plugin (`MarketplaceInstallLocalPlugin`) contributes, now opens Packages with the Source filter set to marketplace. Its label and icon are unchanged. In `MARKETPLACE_INSTALLED_UI_BUNDLE` the entry `nav_marketplace_installed` is a `component` entry on `developer:packages` with `params: { source: 'marketplace' }`, which the console opens as `/apps/setup/component/developer/packages?source=marketplace`. + - **What is no longer shipped.** The bundle no longer carries the `marketplace_installed` page, so `GET /api/v1/meta/page` stops listing it and `MARKETPLACE_INSTALLED_UI_BUNDLE` has no `pages` key. In the console that page only redirected to the filtered Packages list. The `pages.marketplace_installed` entries leave the Setup translation bundle in `@objectstack/platform-objects` (en, zh-CN, ja-JP, es-ES). The Setup nav label `nav_marketplace_installed` keeps its translations. + - **Unchanged.** The entry still appears only when the install-local plugin is mounted, so a runtime without it shows no Installed Apps entry. "Browse Marketplace" and its route are not touched. The marketplace's own package page still carries Update and the sample-data Re-seed / Purge menu, and Details on a Packages row opens it. + + What to do. Nothing in a normal deployment. If you linked to the `marketplace_installed` page directly, link to Setup › Packages with `?source=marketplace` instead. +- b0a1df0: `sys_organization` now declares an `invite_user` action at `record_header`, so an organization record page can offer "Invite User" as its primary header button. It calls `/api/v1/auth/organization/invite-member` with the record's id as `organizationId`, asks for `email` and `role` (the role picker offers only the roles the caller may grant), and is gated like the other three invite mirrors: the `organization` capability and the owner / admin / delegated_admin grades. Its `order: -1` puts it first in the header. + + `leave_organization` keeps `variant: 'danger'` and now declares `order: 1`, so it sorts last among the header actions and folds into the `⋯` overflow menu instead of taking the third inline slot. Its confirmation now names what is lost: the organization's environments and members (en, zh-CN, ja-JP and es-ES). + + `@objectstack/spec` books the new action in `PUBLIC_AUTH_FEATURES.organization.gatedInputs`, beside the other three invite mirrors. + + Clause-②: no +- a19b2a7: Clause-②: no + + fix(platform-objects): the System Overview dashboard's six metric tiles declare `layout.h: 1`, one row tall, where they declared `2` + + The `system_overview` dashboard the Setup app ships (`SystemOverviewDashboard`) authored every one of its six `type: 'metric'` tiles (Total Users, Organizations, Active Sessions, Packages Installed, Login Events, Config Changes) at `layout.h: 2`. The console draws a widget over the grid rows its `h` names, each row floors at 5rem and grows to its content, and a metric card fills its box, so each tile drew a box about twice the height of its content: the title and the number in the top half, the lower half empty. The platform draws what the metadata declares, so the authored size is what is corrected. + + - **FROM:** the six metric tiles declare `layout: { …, h: 2 }`. + - **TO:** the six metric tiles declare `layout: { …, h: 1 }`; each now draws at content height. + - **Unchanged:** every `x`, `y` and `w`, the chart and table widgets (`h: 4`), every widget id, filter and binding, and every export. + - **An org that already published an overlay of `system_overview`** is served that overlay as stored (ADR-0126 Regime O), so it keeps the tile heights it was saved with. +- 99e9d29: feat(plugin-auth): an OAuth consent binds the consenting user's active organization, the access token carries it as `organization_id`, and `verifyMcpAccessToken` returns it as `organizationId` + + Clause-②: yes (widening) + + - **What changed.** When a user consents to an OAuth client, the embedded authorization server records the consenting session's active organization as the consent's `reference_id`. The value rides the authorization code onto the refresh family it starts (`sys_oauth_refresh_token.reference_id`), and every access token minted from either carries it as the `organization_id` claim. It is written through the installed `@better-auth/oauth-provider`'s own seams (`postLogin.consentReferenceId` and `customAccessTokenClaims`); no new configuration key or environment variable. + - **The session's organization, never the request's.** The value is read from the consenting session only. Nothing in the authorize request, the consent body or the client's registration can choose it. + - **No organization, no claim.** A user with no active organization at consent gets a consent with no reference, and tokens with no `organization_id`. There is no guessed or default organization; such a token's principal resolves with global grants only. + - **A refresh keeps the consent's organization.** A refresh token mints access tokens in the organization its consent bound, even after the user switches the browser session to another organization. Authorizing again while another organization is active asks for a second consent, for that organization. + - **Existing consents re-confirm once.** A consent stored before this change has no reference, so the next authorization made while the user has an active organization shows the consent page again, once. Until the user re-confirms, a refresh family started before this change keeps minting tokens with no `organization_id`, so they carry global grants only. + - **`AuthManager.verifyMcpAccessToken`.** Its result type gains `organizationId?: string`, matching `IAuthService.verifyMcpAccessToken` in `@objectstack/spec/contracts`, and it returns the token's `organization_id` claim there. A token whose `organization_id` is present but is not a non-empty string is refused, as every other malformed token is: this server never mints one. The organization is a record, not an authorization. The caller vets it on every request by the rule that vets a session's active organization (`vetOrganizationClaim`, `@objectstack/core`). + - **`@objectstack/platform-objects`.** The `reference_id` field description on `sys_oauth_consent`, `sys_oauth_refresh_token` and `sys_oauth_access_token` now names the bound organization, instead of "Caller-supplied correlation identifier". Prose only; no field, type or schema change. +- 8baeda5: fix(platform-objects): `sys_member`'s `transfer_ownership` row action says the selected member becomes an owner and the caller keeps their role + + The action sends better-auth's `POST /organization/update-member-role` with `role: 'owner'`. That endpoint writes the selected member's role and nothing else, so the caller stays an owner and the organization ends with two. The action's confirm used to promise "You will be demoted to admin and lose owner-only privileges"; nobody was demoted. + + The copy now says what happens, in all four locales: + + - label: FROM "Transfer Ownership" TO "Make Owner"; + - confirm: the selected member becomes an owner with every owner privilege, the caller stays an owner, and to step down the caller uses Change Role on their own membership; + - success message: FROM "Ownership transferred" TO "Member is now an owner". + + To hand ownership over, run Make Owner on the new owner's row, then Change Role on your own row to `admin`. better-auth refuses that second step only while you are the organization's last owner (`YOU_CANNOT_LEAVE_THE_ORGANIZATION_WITHOUT_AN_OWNER`). + + The action's `name`, `target`, request body, parameters, `requiresMembershipReach` and `variant` are unchanged, so a page or test that names `transfer_ownership` keeps working. A test that expected the caller to be `admin` after the action expected a demotion the endpoint never performed: it now reads `owner`, and reads `admin` only after the caller's own Change Role. + + Clause-②: no (provisional: copy only; if the measurement forces another shape, the dev reports before building it; owes the seat's review of record at `TIER_DEFAULT` before the queue) +- cf540a7: Clause-②: no + + fix(platform-objects): `sys_metadata_activation`'s description and its `package_id` field description say "authored in this system" + + - **What changed.** The activation ledger's object description and the description of its `package_id` field described an item no package ships as "authored in this environment". Both now say "authored in this system". Inside the running system, user-facing copy names the system the user is in as "this system". + - **Unchanged.** The object's fields, their types and every stored value. +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/platform-objects/package.json b/packages/platform-objects/package.json index a0b23bf6167..0c98e4c7bc5 100644 --- a/packages/platform-objects/package.json +++ b/packages/platform-objects/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/platform-objects", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Core platform object schemas for ObjectStack — identity, security, audit, tenant, and metadata objects", "main": "dist/index.js", diff --git a/packages/plugins/embedder-openai/CHANGELOG.md b/packages/plugins/embedder-openai/CHANGELOG.md index 94903b2399d..0b061fea2b8 100644 --- a/packages/plugins/embedder-openai/CHANGELOG.md +++ b/packages/plugins/embedder-openai/CHANGELOG.md @@ -1,5 +1,46 @@ # @objectstack/embedder-openai +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/embedder-openai/package.json b/packages/plugins/embedder-openai/package.json index 39b2fdfc66f..68212e57cea 100644 --- a/packages/plugins/embedder-openai/package.json +++ b/packages/plugins/embedder-openai/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/embedder-openai", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "OpenAI-compatible embedder for ObjectStack — works against OpenAI, 阿里通义 DashScope, 智谱 BigModel, 硅基流动 SiliconFlow, 火山引擎 Doubao, MiniMax, Ollama, and any drop-in OpenAI-shape endpoint.", "main": "dist/index.js", diff --git a/packages/plugins/knowledge-memory/CHANGELOG.md b/packages/plugins/knowledge-memory/CHANGELOG.md index 1bc97975bcb..e9e9f410821 100644 --- a/packages/plugins/knowledge-memory/CHANGELOG.md +++ b/packages/plugins/knowledge-memory/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/knowledge-memory +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/service-knowledge@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/knowledge-memory/package.json b/packages/plugins/knowledge-memory/package.json index bfe4eaebda0..43173600105 100644 --- a/packages/plugins/knowledge-memory/package.json +++ b/packages/plugins/knowledge-memory/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/knowledge-memory", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "In-memory knowledge adapter for ObjectStack (dev / test reference implementation).", "main": "dist/index.js", diff --git a/packages/plugins/knowledge-ragflow/CHANGELOG.md b/packages/plugins/knowledge-ragflow/CHANGELOG.md index 05fee12761c..801178c905f 100644 --- a/packages/plugins/knowledge-ragflow/CHANGELOG.md +++ b/packages/plugins/knowledge-ragflow/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/knowledge-ragflow +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/service-knowledge@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/knowledge-ragflow/package.json b/packages/plugins/knowledge-ragflow/package.json index 7c608462f26..d2ee7818721 100644 --- a/packages/plugins/knowledge-ragflow/package.json +++ b/packages/plugins/knowledge-ragflow/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/knowledge-ragflow", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "RAGFlow knowledge adapter for ObjectStack — production-grade RAG via the Apache 2.0 RAGFlow REST API.", "main": "dist/index.js", diff --git a/packages/plugins/organizations/CHANGELOG.md b/packages/plugins/organizations/CHANGELOG.md index dc44a0fa927..07c073604b1 100644 --- a/packages/plugins/organizations/CHANGELOG.md +++ b/packages/plugins/organizations/CHANGELOG.md @@ -1,5 +1,63 @@ # @objectstack/organizations +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [03ac4ec] +- Updated dependencies [b0a1df0] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [8a88901] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [9040945] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [83dfbc0] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [d65826f] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [44a5011] +- Updated dependencies [f573976] +- Updated dependencies [e22defb] + - @objectstack/core@18.0.0-next.4 + - @objectstack/plugin-auth@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/organizations/package.json b/packages/plugins/organizations/package.json index 2de7aaf975c..9c505243840 100644 --- a/packages/plugins/organizations/package.json +++ b/packages/plugins/organizations/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/organizations", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Multi-organization runtime for ObjectStack — registers the `org-scoping` service that turns single-database row-level Organization isolation on: `organization_id` auto-stamp on insert, per-org seed replay, default-organization bootstrap, and the walled-posture membership-policy gate.", "main": "dist/index.js", diff --git a/packages/plugins/plugin-approvals/CHANGELOG.md b/packages/plugins/plugin-approvals/CHANGELOG.md index 4004058c387..8eeef8f74fb 100644 --- a/packages/plugins/plugin-approvals/CHANGELOG.md +++ b/packages/plugins/plugin-approvals/CHANGELOG.md @@ -1,5 +1,82 @@ # @objectstack/plugin-approvals +## 18.0.0-next.4 + +### Patch Changes + +- e15897c: fix(plugin-approvals): the approval request page's highlights strip names the flow by its label, not `flow:NAME` + + Clause-②: no + + - **The strip's first chip is the flow's label.** `sys_approval_request_detail` lists `process_label` (the "Flow" field) where it listed `process_name`, so a request opened by the Deal Desk flow reads `Deal Desk` there instead of `flow:deal_desk`. The request lists already showed this label. + - **The object's `highlightFields` makes the same swap** (`process_label` in place of `process_name`, `record_id` kept). The page's Details tab is a bare `record:details` node, which draws the object's `highlightFields` minus the strip. A change to the strip alone would have moved `process_name` into that tab. Changed together, the tab still shows only the target record card. Other surfaces that read the object's list show the flow's label too: a request's record preview card, Studio's object settings, and the default columns of a list, kanban, related list or lookup picker that declares none. No first-party request list uses the default columns: each request list view declares its own. + - **When no label is available.** The console reads a request through the approvals get, which serves `process_label` as the stored label, else the flow label in the request's node-config snapshot, else the humanized machine name (`flow:deal_desk` → `Deal Desk`). A request the boot backfill has not labelled still shows a Flow chip. Only when all three are empty does the field have no value: the strip then draws no Flow chip, and the record title still names `process_name`. + + A patch: one entry in one platform object's field list and the same entry in its page. No export, type, accepted value or stored column changes. +- 5da2e88: fix(plugin-approvals): the approval request page's highlights strip names the current step by its label, not the node id + + Clause-②: no + + - **The approvals get serves `current_step_label`.** `ApprovalRequestRow` declares it as the step's label under the `sys_approval_request` column's own name. Every door that serves a request now serves it with exactly `step_label`'s value: the get, the list, and the request that a decision, recall, send-back, resubmit, reassign, remind, info request or comment echoes back. `step_label` stays, with the same value, for the inbox and the emailed action-link pages. `current_step` stays the Approval node's machine id. + - **The strip's step chip is the step's label.** `sys_approval_request_detail` lists `current_step_label` (the "Approval Step" field) where it listed `current_step`, so a request waiting on the `approve_step` node reads `Manager Review` there instead of `approve_step`. The request lists already showed this label. + - **The object's `highlightFields` makes the same swap.** The page's Details tab is a bare `record:details` node, which draws the object's `highlightFields` minus the strip. A change to the strip alone would have moved `current_step` into that tab. + - **When no label is stored.** The get serves the stored label, else the node label in the request's node-config snapshot, else the humanized node id (`approve_step` → `Approve Step`). A request the boot backfill has not labelled still shows a step chip. A request with no step serves neither field, and the strip draws no step chip. + + A patch: one served key that the contract already declares, and one entry in one platform object's field list and in its page. No export, type, accepted value or stored column changes. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/plugins/plugin-approvals/package.json b/packages/plugins/plugin-approvals/package.json index 90c6ef2d207..778af5c2c2f 100644 --- a/packages/plugins/plugin-approvals/package.json +++ b/packages/plugins/plugin-approvals/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-approvals", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Multi-step approval engine for ObjectStack — sys_approval_process + sys_approval_request + sys_approval_action + IApprovalService.", "main": "dist/index.js", diff --git a/packages/plugins/plugin-audit/CHANGELOG.md b/packages/plugins/plugin-audit/CHANGELOG.md index e3fb327ba69..50faa4c69be 100644 --- a/packages/plugins/plugin-audit/CHANGELOG.md +++ b/packages/plugins/plugin-audit/CHANGELOG.md @@ -1,5 +1,113 @@ # @objectstack/plugin-audit +## 18.0.0-next.4 + +### Minor Changes + +- d65826f: fix: framework writes on tenant-column objects name the organization they can derive (ADR-0131 D9) + + Clause-②: yes (widening) + + A write on a tenant-column object with no organization is derived under `single` and refused under a wall (ADR-0131 D9). Before the engine stops letting such writes through, each of these producers now names the organization it can derive itself. Under `single` that is the Default Organization, which is the owner the engine would derive anyway, so no reader changes. Under a wall the row names an organization only where the producer has one. The rows it cannot attribute are written exactly as before. + + - **`@objectstack/plugin-auth`.** The self-registration grant (`sys_user_permission_set`) and the auth mails about a user (verification, password reset, account invitation, magic link, change-email notice → `sys_email`) read one derivable organization: the `tenancy` service's `defaultOrgId()`. Under `single` that is the Default Organization. Under a wall it is none, because the registrant belongs to no organization yet. The verification and reset mails used to persist with no organization even under `single`. + - **`@objectstack/plugin-audit`.** An activity row (`sys_activity`) about a record with no organization of its own (a `sys_user`, a platform setting), written with no session organization (a sign-up), takes the Default Organization under `single`. The record's own organization and then the session's still win. The ledger's `tenant_id` attribution is unchanged. New optional `AuditWriterOptions.getDefaultOrganizationId()`; `AuditPlugin` wires it to the `tenancy` service. + - **`@objectstack/service-settings`.** The `sys_secret` ciphertext and the `sys_setting_audit` history row of a `tenant` or `user` value carry that value's organization, which is the same owner as its `sys_setting` row. A `global` value's rows name none. New optional `organization_id` on `SettingsSecretStore.insert`'s row and `organizationId` on `SettingsAuditWriter.write`'s entry; the built-in writers persist them. A failed `sys_setting_audit` write is now reported at `error`, not `warn`: the settings write succeeds while its history row is lost. The first report names the consequence and the fix. `buildSettingAuditWriter`'s optional logger argument keeps every sink it accepted (none, `{}`, or one with an optional `warn`) and additionally takes a sink that carries `error`. It reports through `error` when the sink has it, else through `warn`. + - **`@objectstack/verify`.** The RLS probe and position personas' grants (`sys_user_permission_set`, `sys_user_position`) belong to the organization the persona is a member of (the Default Organization under `single`). They used to be written with an explicit `organization_id: null`. A persona that belongs to no organization, or to several, names none. + +### Patch Changes + +- f7af839: Clause-②: no + + Setup's Diagnostics group has one audit entry, "Audit Log", and it opens the audit browser + + - **What changed.** `@objectstack/plugin-audit` contributed two Setup entries onto the same rows: "Audit Logs" (the `sys_audit_log` record list) and "Audit Log Browser" (the console's `audit:log` page). It now contributes one, "Audit Log", which opens the browser. The browser filters by action, object, actor and date, offers the record list's views (Writes, Auth, Record Views, …) as presets, and links to the record list ("Open as list"). Those two parts of the page ship in the console this release pins. + - **Ids.** The entry keeps the id `nav_audit_logs`, so a sidebar pin a user set on "Audit Logs" stays on the new entry. The id `nav_audit_log_browser` is no longer served; a pin set on it no longer shows, and the user can pin "Audit Log" instead. + - **Labels.** `@objectstack/platform-objects` serves the entry's label in the four shipped locales (Audit Log, 审计日志, 監査ログ, Registro de auditoría), and drops the "Audit Log Browser" label. + - **Unchanged.** The `sys_audit_log` object, its list views, its routes and its data API answer as before. The record list is no longer an entry in Setup's menu, so the ⌘K palette's record search in Setup no longer searches audit rows. +- 62c4441: Clause-②: no + + fix(plugin-audit): a read of the activity stream, the compliance ledger or comments that names its parent objects writes one parent-record branch per named object, not one per registered object + + - **What changed.** The parent-record read gate on `sys_activity`, `sys_audit_log` and `sys_comment` ANDs one branch per parent object into every read: a `ReadableIds` leaf, or the bounded probe for an on-behalf-of caller, a middleware-narrowed parent or a parent on another datasource. It wrote one for every registered object, whatever the read asked for. When the read's own `where` names the objects its rows are about (`object_name` as a plain value, `$eq` or `$in` of machine names, at the top level or inside a top-level `$and`), the gate now writes branches for those objects only, and the bounded probe pre-scans only the named objects of its classes, or does not run. The activity stream's withheld-update rule, which wrote one branch per enumerated object too, narrows the same way. + - **What it costs now.** Measured on SQLite with 148 registered objects: a newest-first 20-row activity read filtered to 3 objects went from a 711.8 KB statement with 2,629 bindings to 14.9 KB with 60, for the rows and again for the total, and its two probe pre-scans no longer run. + - **Unchanged:** the rows, the totals and the grouped counts every reader is served. A read of any other shape (no `object_name` filter, an `$or`, a `$not`, another operator, a value that is not a machine name) keeps every branch, as before. No export, option or stored shape changes. + - **One failure mode is gone:** a leaf or a probe for an object the read does not name is no longer composed or run, so a refusal composing it, or a failing pre-scan for it, no longer fails or denies that read. +- 77e9f79: fix(plugin-audit): an activity summary written right after a workspace language change is stored in the new language, not the old one + + Clause-②: no (no export, option key, signature or schema changes; the held write locale is retired sooner, and the option docs say when) + + - **What was wrong.** The audit writer held the write locale for each tenant/user scope for 30 seconds, and nothing retired it when the `localization.locale` setting changed. A `sys_activity.summary` line is stored once, at write time, so every audited write in that window (a user's create, a seed publish) kept the old language for good. + - **What changes.** A write to `sys_setting` or `sys_platform_setting` through the engine now retires every held write locale. That covers a change made through the settings API, which persists through the engine, and a direct row write such as a seeder's, which emits no settings event. These are the same writes that retire `resolveLocalizationContext`'s own cache. A lookup still in flight when such a write lands is not held. The 30-second bound stays for what the writer cannot see: the storage behind a host `getLocale` resolver, and a settings write on another node. + - **Unchanged:** the host `getLocale` resolver is still asked first, and a write with no settings change between it and the last one is still answered from the held locale, without a settings read. +- 538f923: fix: an activity row is written only when an organization is derivable, never with a NULL organization, and the v18 ownership ceremony deletes the existing NULL activity rows it cannot attribute (ADR-0131 D9) + + Clause-②: no + + - **`@objectstack/plugin-audit`.** The audit writer's activity mirror (`sys_activity`) takes its organization from the record, then from the acting session, then from the `tenancy` service's Default Organization under `single`. When none of them names one, the mirror now writes no activity row at all. Before, it wrote a row with `organization_id` NULL. Under a wall that row was readable by no organization member. The change is still recorded: the compliance ledger row (`sys_audit_log`) is written as before, with its `tenant_id` attribution. Under `single` the Default Organization is always derivable, so nothing changes there. A record that names its organization keeps its activity row, with that organization. + - **`@objectstack/cli`.** `os migrate organization-ownership` still attributes a NULL `sys_activity` row when its record carries an organization. On a walled database (any posture but `single`), the mirror-deletion step now deletes the NULL rows no anchor attributes, such as activity about a sign-up's `sys_user`. Before, those rows were reported as unattributable and held certification. They are 14-day telemetry that the mirror no longer writes, and `sys_audit_log` keeps each change. The plan counts them as the departure `underivable-activity`, and warns before the first write that `--apply` deletes them. The apply report lists the count as the table's `deleted`. Under `single` the Default Organization takes these rows, as before, and nothing is deleted. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [ec749fb] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/plugins/plugin-audit/package.json b/packages/plugins/plugin-audit/package.json index e904349919c..64c526aa39e 100644 --- a/packages/plugins/plugin-audit/package.json +++ b/packages/plugins/plugin-audit/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-audit", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Audit Plugin for ObjectStack — System audit log object and audit trail", "main": "dist/index.js", diff --git a/packages/plugins/plugin-auth/CHANGELOG.md b/packages/plugins/plugin-auth/CHANGELOG.md index aa6daf44bfa..b94aa2850ec 100644 --- a/packages/plugins/plugin-auth/CHANGELOG.md +++ b/packages/plugins/plugin-auth/CHANGELOG.md @@ -1,5 +1,231 @@ # Changelog +## 18.0.0-next.4 + +### Minor Changes + +- b2e7870: fix(security): under the `single` tenancy posture the first-user platform-admin grant is owned by the Default Organization, and every reader of platform standing reads that row + + Clause-②: yes (narrowing) + + + + **BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + - **FROM** (`@objectstack/plugin-security`): a `single` deployment holding several organizations, none slugged `default`, promoted its first user with an organization-less `admin_full_access` grant. **TO:** it is not promoted; the bootstrap logs one `error` line, returns `reason: 'default_organization_unresolved'` and writes no grant row. **Fix:** give the intended organization the slug `default` (or keep one organization, or declare a walled posture); the next `kernel:ready` or sign-up pass promotes. + - **FROM** (`@objectstack/plugin-auth`): a `sys_organization` `slug` change or delete was never refused. **TO:** it is refused with `403 PERMISSION_DENIED` when that organization is the Default Organization whose `admin_full_access` grant is the last unbanned administrator's anchor. **Fix:** give another account administrator standing first, then make the change. + - **FROM** (`@objectstack/plugin-auth`): `ensureDefaultOrganization` under `single`, with a sole organization of another slug, created a second, `default`-slugged organization and bound the administrator there. **TO:** it reuses the sole organization; no second organization is created. **Fix:** none needed; an install that wants a `default`-slugged organization re-slugs its existing one or creates it explicitly. + - **Widening** (`@objectstack/core`): under `single` only an organization-less `admin_full_access` grant conferred `PLATFORM_ADMIN`. Now a grant owned by the Default Organization confers it too and applies in every organization; a pre-existing Default-Organization-scoped `admin_full_access` grant gains the rung on upgrade; and `sys_organization` becomes a standing-deriving table (the resolver reads it only under `single` for a holder of an organization-owned `admin_full_access` grant — one or two small reads per uncached request; the last-administrator guard adds three to six small reads per guarded write). + + - **The promotion writes the Default Organization (`@objectstack/plugin-security`).** A `single` deployment with no configuration still promotes its first user that can sign in. The `admin_full_access` grant row that promotion writes now carries the Default Organization as its `organization_id` (ADR-0131 D5), never NULL. The Default Organization is the one with `slug = 'default'`, else the only organization: the organization the auth plugin creates at boot. A deployment that names no Default Organization (several organizations, none with the slug `default`) is not promoted. The bootstrap logs one `error` line saying why and returns `reason: 'default_organization_unresolved'`. It writes no grant without an owner. `OS_PLATFORM_OWNER_EMAIL` is still the walled-posture channel and is not required under `single`. + - **The authorization resolver reads that row (`@objectstack/core`).** Under `single`, `PLATFORM_ADMIN` derives from an `admin_full_access` grant owned by the Default Organization, and still from the organization-less row an earlier release wrote. The anchoring grant applies in whichever organization the user acts in, as the organization-less row always did. A grant of the set owned by any other organization confers the set in that organization, never platform standing. Under a walled posture no grant anchors standing (unchanged). One predicate decides both postures, with the posture as its parameter. The resolver reads `sys_organization` only for a user who holds an organization-owned `admin_full_access` grant under `single`. + - **An upgraded database keeps its administrator.** `os migrate organization-ownership --apply` gives a `single` database's organization-less grants to the Default Organization, the platform admin's grant included. Before this change the resolver then stopped grading that admin `PLATFORM_ADMIN`, and every later boot found no organization-less holder and promoted again. The holder scan now reads the Default Organization's grant, so a migrated database keeps its admin and promotes nobody else. A user who holds both rows counts as one holder. + - **The last-administrator guard and the default-organization bootstrap read it too (`@objectstack/plugin-auth`).** The break-glass guard counts the holder of the Default Organization's grant as a platform admin and treats that grant as evidence that the environment once had one. It also judges the two organization writes that move the anchor off that grant: changing the Default Organization's `slug` while another organization exists, and deleting it. Either is refused when it would leave no administrator who can sign in. `ensureDefaultOrganization` finds the admin through the same grant. Under `single` it also reuses the Default Organization when that is a sole organization with another slug, where it used to create a second, `default`-slugged organization. +- 8a88901: fix(plugin-auth,plugin-email): the organization invitation mail names the invitee's role by its label in the mail's language (`成员`, `メンバー`, `Miembro`, `Member`), not by the stored value `member` + + Clause-②: yes (widening) + + - **What changed.** The zh-CN, ja-JP and es-ES invitation mails printed the stored membership role inside a translated sentence: 「邀请您以 member 的身份加入」. The sender now passes `{{role}}` as the `sys_member.role` option label in the locale the mail is written in, the same label the console shows for that option. It reads the label through the runtime's i18n service and the console's own object translation path, so a tenant's translation of the option, made in Studio, reaches the mail too. The four bodies keep interpolating the one `role` variable; its name and type are unchanged, and its declaration now says it carries the label. + - **The plain-text part names the role too.** The four text bodies gain the role clause their HTML bodies carry, so both parts of the mail name the same facts. For example, the English text is now `Dana (dana@example.com) invited you to join Northwind on Acme as Member.` + - **Stated fallbacks.** A role list (`admin,member`) is labelled role by role and joined as a list in the mail's language (`管理员和成员`). A value with no option label is named as stored, and one warning names it. With no i18n service, or one that fails, the mail names the option's authored English label, never an empty string. + - **New option (widening).** `AuthManagerOptions.getI18n?: () => II18nService | undefined` is a lazy accessor for the `i18n` service, read when the invitation is sent. `AuthPlugin` wires it from the kernel. An embedder that builds `AuthManager` itself and omits it gets the authored labels. + - **What changes for you.** If you have overridden `auth.invitation` and interpolate `{{role}}`, it now receives the label, not the stored value. +- 99e9d29: feat(plugin-auth): an OAuth consent binds the consenting user's active organization, the access token carries it as `organization_id`, and `verifyMcpAccessToken` returns it as `organizationId` + + Clause-②: yes (widening) + + - **What changed.** When a user consents to an OAuth client, the embedded authorization server records the consenting session's active organization as the consent's `reference_id`. The value rides the authorization code onto the refresh family it starts (`sys_oauth_refresh_token.reference_id`), and every access token minted from either carries it as the `organization_id` claim. It is written through the installed `@better-auth/oauth-provider`'s own seams (`postLogin.consentReferenceId` and `customAccessTokenClaims`); no new configuration key or environment variable. + - **The session's organization, never the request's.** The value is read from the consenting session only. Nothing in the authorize request, the consent body or the client's registration can choose it. + - **No organization, no claim.** A user with no active organization at consent gets a consent with no reference, and tokens with no `organization_id`. There is no guessed or default organization; such a token's principal resolves with global grants only. + - **A refresh keeps the consent's organization.** A refresh token mints access tokens in the organization its consent bound, even after the user switches the browser session to another organization. Authorizing again while another organization is active asks for a second consent, for that organization. + - **Existing consents re-confirm once.** A consent stored before this change has no reference, so the next authorization made while the user has an active organization shows the consent page again, once. Until the user re-confirms, a refresh family started before this change keeps minting tokens with no `organization_id`, so they carry global grants only. + - **`AuthManager.verifyMcpAccessToken`.** Its result type gains `organizationId?: string`, matching `IAuthService.verifyMcpAccessToken` in `@objectstack/spec/contracts`, and it returns the token's `organization_id` claim there. A token whose `organization_id` is present but is not a non-empty string is refused, as every other malformed token is: this server never mints one. The organization is a record, not an authorization. The caller vets it on every request by the rule that vets a session's active organization (`vetOrganizationClaim`, `@objectstack/core`). + - **`@objectstack/platform-objects`.** The `reference_id` field description on `sys_oauth_consent`, `sys_oauth_refresh_token` and `sys_oauth_access_token` now names the bound organization, instead of "Caller-supplied correlation identifier". Prose only; no field, type or schema change. +- 51ad8e4: Clause-②: yes (widening) + + fix(cli,runtime): the 17 → 18 organization-ownership upgrade of a single-posture database that holds no organization leaves no row without one, the boot the ceremony prescribes writes nothing before it is refused, and a ceremony that did not verify is never served (ADR-0131 D3, D10) + + A 17.x single-posture install whose seed ran before any organization existed holds rows of organization-owned objects with `organization_id` NULL and an empty `sys_organization`. Upgrading it took this path: the ceremony's attribution checkpointed itself done at zero rows; the boot its rewrite step asks for created the Default Organization and let the inline seed stamp some rows before it refused; the second `--apply` did not run attribution again, dropped the columns and recorded discrepancies with `verified_at` NULL; and `os start` then served that database. + + - **The ceremony creates the Default Organization (`@objectstack/cli`).** Under the `single` posture, on a database that holds no organization and rows no anchor names, `os migrate organization-ownership` plans those rows as the Default Organization's and says so in a plan warning (`defaultOrganizationCreatedByApply: true` in the plan file). `--apply` creates it before its first write, through plugin-auth's boot invariant (the row a single-posture boot creates), and gives it every such row. A run that cannot create it — a boot without the auth family that declares `sys_organization` — blocks at attribution, naming why, instead of attributing nothing and certifying. Nothing is created under a walled posture, or when the database holds organizations. + - **The ceremony names the Default Organization as the boot does (`@objectstack/cli`).** Under `single` it is the organization with `slug = 'default'`, else the sole organization whatever its slug — the boot invariant's and the tenancy service's definition (ADR-0093 D3). Before, the plan required `slug = 'default'`, so a database whose only organization had another slug kept its unanchored rows NULL through the ceremony while v18 derives every owner from that organization. Several organizations, none of them `default`, still name none, and their rows are reported. In the plan file (the command's own output, not a published schema) `defaultOrganization.slug` is now that organization's own slug, no longer always `'default'`. + - **A checkpointed attribution runs again (`@objectstack/cli`)** whenever the plan derives an owner for rows still NULL: a row the operator made attributable since, or one an earlier run found no Default Organization for. It writes only where `organization_id` is NULL, so no owner already written is derived again. A database left by an earlier release's ceremony in that state is completed by re-running `--apply`. + - **The boot guard judges before any plugin writes (`@objectstack/runtime`, `@objectstack/plugin-auth`).** `OrganizationOwnershipBootGuardPlugin` judges in its own `start()`, after the engine's schema sync (the column the rewrite step waits for is still added) and before every plugin that writes rows at start: `AppPlugin` (the inline seed), `AuthPlugin` and the identity-objects plugin (the Default Organization) name it (`com.objectstack.organization-ownership-boot-guard`) in `optionalDependencies`. A refused boot leaves every row as it found it. + - **A ceremony that did not verify is refused (`@objectstack/runtime`).** With no table left in the v17 shape, a boot whose `adr-0131-organization-ownership` marker exists and is not verified — the post-check found rows the ceremony has not carried — is refused with `ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED`, naming the re-run. A database with no marker (a fresh one) boots as before. + - **The refusal now arrives as a `cause` (`@objectstack/runtime`).** A failed `start()` is reported by the kernel as its own error ("Plugin … failed to start"), with the plugin's error as its `cause`. A host that composes the guard and inspects the boot error asks `isOrganizationOwnershipBootRefusal` of each `cause` down the chain: asked of the kernel's own error it now answers `false`. `assertOrganizationOwnershipMigrated(engine)` still throws the refusal itself. `os serve` and `os dev` read it from the chain. + - **One new export (`@objectstack/plugin-auth`, `minor`).** `ensureDefaultOrganizationExists(ql, options?)` — the Default Organization's boot invariant, find-or-create under `single` — for the ceremony, so the organization it creates is the row a single-posture boot creates, from one producer. A host composing a kernel still never calls it: `AuthPlugin` and the identity-objects plugin keep the invariant on every boot. No other package entry changes. + + Unchanged: the ceremony's steps, fates and refusals otherwise; a walled posture's unattributable rows (reported, never assigned); the boot of a fresh or verified database; and every plugin's order on a kernel that does not compose the guard. Repairing a database whose organization-scoped seed load already inserted twins of NULL-organization rows is an operator's data repair. + +### Patch Changes + +- 03ac4ec: fix(plugin-auth): the invitation role cap's refusals on `POST /organization/invite-member` carry an error code, and a membership read that fails answers `503` instead of a refusal + + Clause-②: no + + The role cap in `beforeCreateInvitation` refused with `403` and a message but no `code`, while better-auth's own `owner` refusal on the same endpoint carries one. A client, the console's Invite User dialog among them, could not tell the refusals apart without reading English prose. Each refusal now carries the code the error-code ledger registers for it under `@objectstack/plugin-auth`, in the same `{ message, code }` body: + + - `INVITE_ROLE_ABOVE_ISSUER`: the requested role grades above the issuer's own, for example a `delegated_admin` inviting as `admin`. + - `INVITE_ISSUER_MEMBER_ONLY`: an issuer below admin grade asked for a role other than a plain `member`, for example a `delegated_admin` inviting as `delegated_admin`. + - `INVITE_ISSUER_UNVERIFIED`: fail-closed. The issuer's membership role was not established: the read found no usable `sys_member` row, or the `AuthManager` has no data engine to read from. + + A membership read that throws is a store outage, not a verdict about the issuer. It used to answer the same `403` as an unverified issuer. It now answers `503` with the code `SERVICE_UNAVAILABLE`, as the organization slug guard does for its own read faults. No invitation is created either way. + + Unchanged: which invitations are admitted, the status of every refusal (`403`), better-auth's own `owner` refusal, and the exports. +- f792290: Clause-②: no + + perf(plugin-auth, plugin-hono-server): an authenticated request reads 8 rows in 4 round trips before its handler, down from 11 in 7, and `/auth/me/*` resolves the caller's grants once + + Measured on a real engine (SQLite through `@objectstack/driver-sql`), for a signed-in user whose session has an active organization: + + | before the handler | before | after | + |:--|:--|:--| + | REST and dispatcher doors (`resolveAuthzContext` with the in-process session read) | 11 reads, 7 round trips | 8 reads, 4 round trips | + | `GET /auth/me/permissions`, `/auth/me/localization`, `/me/apps` | 17 reads, 9 round trips | 8 reads, 4 round trips | + | `GET /api/v1/auth/get-session` | 11 reads, 7 round trips | 10 reads, 6 round trips when it reads the signing key; 8 in 4 while the key is held | + + - **An in-process session read no longer signs a JWT.** better-auth runs `/get-session`'s after-hooks for a server-side `auth.api.getSession(...)` call too, so every door that asked "who is calling" had the jwt plugin read the signing key (two `sys_jwks` round trips) and sign a `set-auth-jwt` header the caller then discarded. The hook now runs only when the call answers an HTTP request. `GET /api/v1/auth/get-session` still carries `set-auth-jwt`, signed exactly as before. A server-side caller that wants the header dispatches the request through `AuthManager.handleRequest`. + - **The session token is not read back after the session was found by it.** `sys_session.token` is withheld from ordinary reads and recovered for better-auth through a privileged read. better-auth finds the session by its token, so that read had nothing to add. It was a second round trip between the session row and the user row on every session read. A read keyed on any other column still recovers the token. + - **`/auth/me/*` and `/me/apps` resolve the grants once.** These routes resolved the caller's grants twice per request: once inside the session read (for the payload's `positions[]`) and once more for their own context. They now resolve through `resolveAuthzContext`, whose request-scoped memo serves the second resolution from the first while no write has landed in between. The memo ends with the request. The routes still admit a session only, and the session's active organization stands as before. + - **An unreadable authorization store is an outage on `/auth/me/*` and `/me/apps`, not a signed-out caller.** Because these routes now authorize through `resolveAuthzContext`, they follow the rule every transport through it follows: a permission-store read that fails answers `503` with the code `SERVICE_UNAVAILABLE` (on `HonoHttpServer`'s error seam) instead of `{ authenticated: false }` or `{ apps: [] }`, which a client cannot tell from an anonymous caller. A store whose tables were never created is not an outage: those routes answer the signed-in caller with no grants, as before. + - **The signing key is held across requests, under two bounds.** The keyring the jwt plugin signs with is kept after a read. Any write to `sys_jwks` that passes this process's engine drops it at once, and it is never kept longer than 30 seconds, which bounds a write this process cannot see (another node minting a key). An engine without a middleware seam keeps nothing. On a host without Ed25519 the keyring is still filtered to keys the host can use. + + Unchanged: the session and user every door resolves, every grant, the `/get-session` body and its renewal, every option and environment variable, and the exports. No new configuration key. The one answer that changes is the outage answer above. +- 9040945: fix(plugin-auth): the MCP access-token verifier follows the signing keyring instead of holding its own copy of the key set for five minutes + + Clause-②: no + + - **What changed.** `AuthManager.verifyMcpAccessToken` no longer keeps its own copy of the signing key set for five minutes. It reads the key set on every call through the same keyring reader the token signer and `/jwks` use, so the set it checks a signature against is held only under both halves of the cross-request caching rule: a `sys_jwks` write that passes this process's data engine retires it in the same operation, and a 30-second bound covers a write the process cannot see, such as one made on another node. + - **A token signed by a key the set does not carry gets one fresh read.** Before such a token is refused, the keyring is read once more from the table, and the token is checked again. That read is shared by every token that arrives while it is in flight, and a new one starts no sooner than 30 seconds after the previous one, so tokens that name keys which do not exist cost at most one keyring read per 30 seconds. A token whose key is still absent after that read is refused, as before. + - **Unchanged.** Issuer, audience, expiry, the `client_credentials` refusal and the `organization_id` claim are checked exactly as before. Only the public key set `/jwks` publishes is used for verification. No export, configuration key or environment variable is added. +- af284b0: feat(spec): the remaining `getSession` readers read `AuthSessionApi`'s declared result, and none reads a top-level `activeOrganizationId` + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains three optional `user` members**, each declared for a named reader: `isPlatformAdmin` (`boolean`, the ADR-0068 D2 rung plugin-auth's `customSession` derives) and `role` (`string | null`, better-auth's `admin` plugin scalar), both read by `judgePlatformAdmin` in `@objectstack/plugin-auth`; and `name` (`string`), read by the runtime dispatcher's AI-route `req.user` resolver as the display name. Every member is optional, so every implementer and caller typechecks unchanged. + - **The readers read through the declared type instead of `any` or a cast** (types only, runtime behaviour unchanged): `judgePlatformAdmin` and the four platform-admin mounts in `@objectstack/plugin-auth` (better-auth's own session type now has to fit the declaration at those reads), the REST server's two session reads (`@objectstack/rest`), the dispatcher's posture read, `resolveSessionData` and both `HttpDispatcher` reads (`@objectstack/runtime`), the cloud-connection and install-local session reads (`@objectstack/cloud-connection`), the webhook redeliver door (`@objectstack/plugin-webhooks`), and the storage session bridge (`@objectstack/service-storage`). + - **Fix — the top-level `activeOrganizationId` fallback is removed** from the storage upload stamp (`@objectstack/service-storage`) and from install-local's `resolveActiveOrgId` (`@objectstack/cloud-connection`). The active organization is read from `session.activeOrganizationId` alone, where the contract declares it. No in-process producer answers a top-level `activeOrganizationId` (better-auth's read is `{ user, session }`), so for the shipped auth plugin nothing changes. An auth service that answered only the flattened shape now gets no organization: the upload is not stamped, and install-local reports no active organization. To fix it, answer the active organization on `session.activeOrganizationId`, as `AuthSessionApi` declares it. +- 83dfbc0: fix(plugin-auth): the organization, invitation-placement and phone-OTP hook refusals carry their ADR-0112 codes + + Clause-②: no + + Eight refusals thrown from plugin-auth's better-auth hooks answered with `message` alone, so a client could only show or match English prose. Each now carries a `code` it can branch on, and keeps its HTTP status. + + - `POST /auth/organization/create`: the reserved `__global__` id or slug answers 400 `VALIDATION_ERROR`. Where the deployment's effective tenancy posture enforces no organization wall (`/auth/config` publishes `features.multiOrgEnabled: false`), creating an organization answers 403 `ORGANIZATION_CREATION_DISABLED`. + - `POST /auth/organization/invite-member` with placement intent (`businessUnitId` and `positions`): with no delegated-administration runtime to authorize it, 403 `INVITE_PLACEMENT_UNSUPPORTED`; refused by the delegated-administration gate, 403 `INVITE_PLACEMENT_OUTSIDE_ISSUER_SCOPE`. + - `POST /auth/organization/update` with a new slug: while the organization has an active environment (any environment that is neither archived nor failed), 403 `ORGANIZATION_SLUG_LOCKED_BY_ACTIVE_ENVIRONMENTS`. Its message no longer says environments "still reference" the organization or tells you to rename their hostnames: the guard reads no hostname, and archiving those environments is the remedy. When the guard's own read cannot answer, 503 `SERVICE_UNAVAILABLE`. + - Phone OTP sends (`/auth/phone-number/send-otp`, `/auth/phone-number/request-password-reset`): the per-number limit and the deployment's daily SMS quota both answer 429 `RATE_LIMIT_EXCEEDED`. It is one code for both walls, so a caller still cannot tell which budget it hit. The body is better-auth's flat `code` and `message`, with no `Retry-After` header. + - **One behaviour change:** a placement check that fails before the gate reaches a verdict (the issuer's grants could not be read from the store) now answers 503 `SERVICE_UNAVAILABLE`, not a 403 scope refusal carrying the store's message. No invitation is created either way. +- d65826f: fix: framework writes on tenant-column objects name the organization they can derive (ADR-0131 D9) + + Clause-②: yes (widening) + + A write on a tenant-column object with no organization is derived under `single` and refused under a wall (ADR-0131 D9). Before the engine stops letting such writes through, each of these producers now names the organization it can derive itself. Under `single` that is the Default Organization, which is the owner the engine would derive anyway, so no reader changes. Under a wall the row names an organization only where the producer has one. The rows it cannot attribute are written exactly as before. + + - **`@objectstack/plugin-auth`.** The self-registration grant (`sys_user_permission_set`) and the auth mails about a user (verification, password reset, account invitation, magic link, change-email notice → `sys_email`) read one derivable organization: the `tenancy` service's `defaultOrgId()`. Under `single` that is the Default Organization. Under a wall it is none, because the registrant belongs to no organization yet. The verification and reset mails used to persist with no organization even under `single`. + - **`@objectstack/plugin-audit`.** An activity row (`sys_activity`) about a record with no organization of its own (a `sys_user`, a platform setting), written with no session organization (a sign-up), takes the Default Organization under `single`. The record's own organization and then the session's still win. The ledger's `tenant_id` attribution is unchanged. New optional `AuditWriterOptions.getDefaultOrganizationId()`; `AuditPlugin` wires it to the `tenancy` service. + - **`@objectstack/service-settings`.** The `sys_secret` ciphertext and the `sys_setting_audit` history row of a `tenant` or `user` value carry that value's organization, which is the same owner as its `sys_setting` row. A `global` value's rows name none. New optional `organization_id` on `SettingsSecretStore.insert`'s row and `organizationId` on `SettingsAuditWriter.write`'s entry; the built-in writers persist them. A failed `sys_setting_audit` write is now reported at `error`, not `warn`: the settings write succeeds while its history row is lost. The first report names the consequence and the fix. `buildSettingAuditWriter`'s optional logger argument keeps every sink it accepted (none, `{}`, or one with an optional `warn`) and additionally takes a sink that carries `error`. It reports through `error` when the sink has it, else through `warn`. + - **`@objectstack/verify`.** The RLS probe and position personas' grants (`sys_user_permission_set`, `sys_user_position`) belong to the organization the persona is a member of (the Default Organization under `single`). They used to be written with an explicit `organization_id: null`. A persona that belongs to no organization, or to several, names none. +- 44a5011: Clause-②: no + + Refusals and Setup help text that a user reads inside the running system no longer call that system + "this environment". They say "this system", or they drop the noun. + + - `@objectstack/plugin-auth`: the two self-registration refusals drop the noun ("Self-registration is + closed (audience posture invite_only).", "Self-registration is limited to approved email domains, …"). + So do the OAuth self-service `503` ("OIDC provider is not enabled") and the SSO `400` + `DOMAIN_VERIFICATION_DISABLED` ("Domain verification is not enabled (set + OS_SSO_DOMAIN_VERIFICATION)."). The last-administrator guard's refusals and its two warning lines say + "this system", including "nobody able to administer the system". The OIDC authorize-gate refusal + (`403 ENV_ACCESS_DENIED`) keeps "this environment". Only the cloud control plane installs that gate, + and there the noun names the environment a cloud user is signing in to. + - `@objectstack/plugin-security`: the diagnostic `message` of the packaged-permission-set lock now reads + "… is declared by package '…' and is locked — editing it here would …". The provenance-unknown + refusal says "this system could not determine …". Both `userMessage` texts are unchanged. + - `@objectstack/service-settings`: the Mail and SMS provider groups read "Choose how this system sends + outbound email." and "… outbound SMS.". The Authentication audience group reads "Who may become a user + of this system's apps.". The zh-CN, ja-JP and es-ES bundles change only the noun, to 本系统, + このシステム and este sistema. + + Only the human-readable text moves. Every HTTP status, error code, settings key and response shape + stays as it was. +- e22defb: fix(plugin-auth): a migrate-and-exit boot (`OS_MIGRATE_AND_EXIT=1`) no longer provisions the development admin, whatever `NODE_ENV` says + + Clause-②: no + + - **What changed.** `objectstack serve` reads `OS_MIGRATE_AND_EXIT` only after the kernel bootstrap, and the dev-admin seed runs on `kernel:ready`, inside that bootstrap. So a migration run with `NODE_ENV=development` seeded exactly as a dev boot does: run against an empty database, it left a known-credential login behind. `isDevAdminSeedArmed()` now answers "not armed" when `OS_MIGRATE_AND_EXIT` is `1`, the same spelling `serve` exits on, and the clause wins over an explicit `OS_SEED_ADMIN=1`. A migrate-and-exit boot of an empty database now creates no `sys_user` row and no `sys_account` row. + - **The walled owner check follows.** It reads the same function, so on a migration run the seed no longer counts as a way to verify a declared platform owner. A walled migrate-and-exit boot of an empty database completes its schema sync as before; it just seeds no login. + - **Unchanged.** The `objectstack dev` seed: still armed under `NODE_ENV=development` on a database with no login, and still off under `OS_SEED_ADMIN=0`. Any `OS_MIGRATE_AND_EXIT` value other than `1` is a boot that goes on to serve, and keeps its seed. A database that already holds a login was never seeded and is not touched. + - **If a pipeline relied on the seed.** Create the first account through the deployment's own provisioning path after the migration step, as a production deployment does. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [646303e] +- Updated dependencies [e9bc60b] +- Updated dependencies [52ff308] +- Updated dependencies [d1f082d] +- Updated dependencies [f5ef054] +- Updated dependencies [0d34066] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a06f0d4] +- Updated dependencies [a19b2a7] +- Updated dependencies [9975c26] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [aaaeeac] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8259ea0] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [58a2faa] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/rest@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/service-messaging@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/plugins/plugin-auth/package.json b/packages/plugins/plugin-auth/package.json index 8c77a5dfb6b..cfa5daacad3 100644 --- a/packages/plugins/plugin-auth/package.json +++ b/packages/plugins/plugin-auth/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-auth", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Authentication & Identity Plugin for ObjectStack", "main": "dist/index.js", diff --git a/packages/plugins/plugin-dev/CHANGELOG.md b/packages/plugins/plugin-dev/CHANGELOG.md index c14ffbf36ef..53de75e397d 100644 --- a/packages/plugins/plugin-dev/CHANGELOG.md +++ b/packages/plugins/plugin-dev/CHANGELOG.md @@ -1,5 +1,122 @@ # @objectstack/plugin-dev +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [6a49efb] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [646303e] +- Updated dependencies [e9bc60b] +- Updated dependencies [52ff308] +- Updated dependencies [d1f082d] +- Updated dependencies [f5ef054] +- Updated dependencies [0d34066] +- Updated dependencies [8d3dd6f] +- Updated dependencies [03ac4ec] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [a06f0d4] +- Updated dependencies [9975c26] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [aaaeeac] +- Updated dependencies [8eccf51] +- Updated dependencies [8a88901] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8259ea0] +- Updated dependencies [9040945] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [2bcd406] +- Updated dependencies [58a2faa] +- Updated dependencies [e74ec56] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [83dfbc0] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [ec749fb] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [d65826f] +- Updated dependencies [741aa85] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [d8e11b5] +- Updated dependencies [44a5011] +- Updated dependencies [f573976] +- Updated dependencies [2a1e48a] +- Updated dependencies [e22defb] + - @objectstack/core@18.0.0-next.4 + - @objectstack/plugin-security@18.0.0-next.4 + - @objectstack/plugin-auth@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/rest@18.0.0-next.4 + - @objectstack/service-storage@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/plugin-hono-server@18.0.0-next.4 + - @objectstack/driver-memory@18.0.0-next.4 + - @objectstack/service-i18n@18.0.0-next.4 + - @objectstack/service-realtime@18.0.0-next.4 + - @objectstack/account@18.0.0-next.4 + - @objectstack/setup@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/plugin-dev/package.json b/packages/plugins/plugin-dev/package.json index de2b92e1dfb..aab28eabe39 100644 --- a/packages/plugins/plugin-dev/package.json +++ b/packages/plugins/plugin-dev/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-dev", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Development Assembly Plugin for ObjectStack — wires the real platform stack for zero-config local development", "main": "dist/index.js", diff --git a/packages/plugins/plugin-email/CHANGELOG.md b/packages/plugins/plugin-email/CHANGELOG.md index 1c81a6f978b..d035f1987f3 100644 --- a/packages/plugins/plugin-email/CHANGELOG.md +++ b/packages/plugins/plugin-email/CHANGELOG.md @@ -1,5 +1,70 @@ # @objectstack/plugin-email +## 18.0.0-next.4 + +### Patch Changes + +- 8a88901: fix(plugin-auth,plugin-email): the organization invitation mail names the invitee's role by its label in the mail's language (`成员`, `メンバー`, `Miembro`, `Member`), not by the stored value `member` + + Clause-②: yes (widening) + + - **What changed.** The zh-CN, ja-JP and es-ES invitation mails printed the stored membership role inside a translated sentence: 「邀请您以 member 的身份加入」. The sender now passes `{{role}}` as the `sys_member.role` option label in the locale the mail is written in, the same label the console shows for that option. It reads the label through the runtime's i18n service and the console's own object translation path, so a tenant's translation of the option, made in Studio, reaches the mail too. The four bodies keep interpolating the one `role` variable; its name and type are unchanged, and its declaration now says it carries the label. + - **The plain-text part names the role too.** The four text bodies gain the role clause their HTML bodies carry, so both parts of the mail name the same facts. For example, the English text is now `Dana (dana@example.com) invited you to join Northwind on Acme as Member.` + - **Stated fallbacks.** A role list (`admin,member`) is labelled role by role and joined as a list in the mail's language (`管理员和成员`). A value with no option label is named as stored, and one warning names it. With no i18n service, or one that fails, the mail names the option's authored English label, never an empty string. + - **New option (widening).** `AuthManagerOptions.getI18n?: () => II18nService | undefined` is a lazy accessor for the `i18n` service, read when the invitation is sent. `AuthPlugin` wires it from the kernel. An embedder that builds `AuthManager` itself and omits it gets the authored labels. + - **What changes for you.** If you have overridden `auth.invitation` and interpolate `{{role}}`, it now receives the label, not the stored value. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/plugin-email/package.json b/packages/plugins/plugin-email/package.json index 1e14da93585..9d516003e66 100644 --- a/packages/plugins/plugin-email/package.json +++ b/packages/plugins/plugin-email/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-email", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Email service plugin for ObjectStack — IEmailService + transport-pluggable outbound delivery with sys_email persistence.", "main": "dist/index.js", diff --git a/packages/plugins/plugin-hono-server/CHANGELOG.md b/packages/plugins/plugin-hono-server/CHANGELOG.md index 7e06b21d28d..33259a1f092 100644 --- a/packages/plugins/plugin-hono-server/CHANGELOG.md +++ b/packages/plugins/plugin-hono-server/CHANGELOG.md @@ -1,5 +1,78 @@ # @objectstack/plugin-hono-server +## 18.0.0-next.4 + +### Patch Changes + +- f792290: Clause-②: no + + perf(plugin-auth, plugin-hono-server): an authenticated request reads 8 rows in 4 round trips before its handler, down from 11 in 7, and `/auth/me/*` resolves the caller's grants once + + Measured on a real engine (SQLite through `@objectstack/driver-sql`), for a signed-in user whose session has an active organization: + + | before the handler | before | after | + |:--|:--|:--| + | REST and dispatcher doors (`resolveAuthzContext` with the in-process session read) | 11 reads, 7 round trips | 8 reads, 4 round trips | + | `GET /auth/me/permissions`, `/auth/me/localization`, `/me/apps` | 17 reads, 9 round trips | 8 reads, 4 round trips | + | `GET /api/v1/auth/get-session` | 11 reads, 7 round trips | 10 reads, 6 round trips when it reads the signing key; 8 in 4 while the key is held | + + - **An in-process session read no longer signs a JWT.** better-auth runs `/get-session`'s after-hooks for a server-side `auth.api.getSession(...)` call too, so every door that asked "who is calling" had the jwt plugin read the signing key (two `sys_jwks` round trips) and sign a `set-auth-jwt` header the caller then discarded. The hook now runs only when the call answers an HTTP request. `GET /api/v1/auth/get-session` still carries `set-auth-jwt`, signed exactly as before. A server-side caller that wants the header dispatches the request through `AuthManager.handleRequest`. + - **The session token is not read back after the session was found by it.** `sys_session.token` is withheld from ordinary reads and recovered for better-auth through a privileged read. better-auth finds the session by its token, so that read had nothing to add. It was a second round trip between the session row and the user row on every session read. A read keyed on any other column still recovers the token. + - **`/auth/me/*` and `/me/apps` resolve the grants once.** These routes resolved the caller's grants twice per request: once inside the session read (for the payload's `positions[]`) and once more for their own context. They now resolve through `resolveAuthzContext`, whose request-scoped memo serves the second resolution from the first while no write has landed in between. The memo ends with the request. The routes still admit a session only, and the session's active organization stands as before. + - **An unreadable authorization store is an outage on `/auth/me/*` and `/me/apps`, not a signed-out caller.** Because these routes now authorize through `resolveAuthzContext`, they follow the rule every transport through it follows: a permission-store read that fails answers `503` with the code `SERVICE_UNAVAILABLE` (on `HonoHttpServer`'s error seam) instead of `{ authenticated: false }` or `{ apps: [] }`, which a client cannot tell from an anonymous caller. A store whose tables were never created is not an outage: those routes answer the signed-in caller with no grants, as before. + - **The signing key is held across requests, under two bounds.** The keyring the jwt plugin signs with is kept after a read. Any write to `sys_jwks` that passes this process's engine drops it at once, and it is never kept longer than 30 seconds, which bounds a write this process cannot see (another node minting a key). An engine without a middleware seam keeps nothing. On a host without Ed25519 the keyring is still filtered to keys the host can use. + + Unchanged: the session and user every door resolves, every grant, the `/get-session` body and its renewal, every option and environment variable, and the exports. No new configuration key. The one answer that changes is the outage answer above. +- 2a1e48a: fix(plugin-hono-server): a request for port 0 no longer warns that port 0 is in use + + Clause-②: no (no export, option or type moves: a log line stops firing for a legal request) + + - **What changed.** `HonoServerPlugin`'s `kernel:listening` hook compared the requested port with the bound one and warned `Port 0 is in use, using port N instead` whenever they differed. Port `0` asks for a kernel-assigned port, so the two can never match and nothing was in use. Every `os serve --port 0` boot printed that warning under the banner's Boot diagnostics, at the default log level and at `info`. The warning now fires only for a non-zero request that the adapter moved to another port. + - **Unchanged.** A kernel-assigned boot prints the existing `HTTP server started successfully` line, which already names the bound port, and nothing else. A non-zero request whose port is held still warns, naming the requested and the bound port. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/observability@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/plugins/plugin-hono-server/package.json b/packages/plugins/plugin-hono-server/package.json index 56b2dd51096..c2670a1647e 100644 --- a/packages/plugins/plugin-hono-server/package.json +++ b/packages/plugins/plugin-hono-server/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-hono-server", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Standard Hono Server Adapter for ObjectStack Runtime", "main": "dist/index.js", diff --git a/packages/plugins/plugin-pinyin-search/CHANGELOG.md b/packages/plugins/plugin-pinyin-search/CHANGELOG.md index e7c0d09ee71..5cbc94a6c91 100644 --- a/packages/plugins/plugin-pinyin-search/CHANGELOG.md +++ b/packages/plugins/plugin-pinyin-search/CHANGELOG.md @@ -1,5 +1,28 @@ # @objectstack/plugin-pinyin-search +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [f5ef054] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [aa31569] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [ec749fb] +- Updated dependencies [62e2b6d] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] + - @objectstack/core@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/plugin-pinyin-search/package.json b/packages/plugins/plugin-pinyin-search/package.json index 085c0794f9b..2a03ffe62e8 100644 --- a/packages/plugins/plugin-pinyin-search/package.json +++ b/packages/plugins/plugin-pinyin-search/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-pinyin-search", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Pinyin search recall for ObjectStack — populates the hidden `__search` companion column (full pinyin + initials of the display/name field) so `$search` hits CJK names typed as pinyin. Locale-gated via OS_SEARCH_PINYIN_ENABLED (#2486).", "main": "dist/index.js", diff --git a/packages/plugins/plugin-security/CHANGELOG.md b/packages/plugins/plugin-security/CHANGELOG.md index 11335153fc5..20d43bec52a 100644 --- a/packages/plugins/plugin-security/CHANGELOG.md +++ b/packages/plugins/plugin-security/CHANGELOG.md @@ -1,5 +1,136 @@ # @objectstack/plugin-security +## 18.0.0-next.4 + +### Minor Changes + +- b2e7870: fix(security): under the `single` tenancy posture the first-user platform-admin grant is owned by the Default Organization, and every reader of platform standing reads that row + + Clause-②: yes (narrowing) + + + + **BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + - **FROM** (`@objectstack/plugin-security`): a `single` deployment holding several organizations, none slugged `default`, promoted its first user with an organization-less `admin_full_access` grant. **TO:** it is not promoted; the bootstrap logs one `error` line, returns `reason: 'default_organization_unresolved'` and writes no grant row. **Fix:** give the intended organization the slug `default` (or keep one organization, or declare a walled posture); the next `kernel:ready` or sign-up pass promotes. + - **FROM** (`@objectstack/plugin-auth`): a `sys_organization` `slug` change or delete was never refused. **TO:** it is refused with `403 PERMISSION_DENIED` when that organization is the Default Organization whose `admin_full_access` grant is the last unbanned administrator's anchor. **Fix:** give another account administrator standing first, then make the change. + - **FROM** (`@objectstack/plugin-auth`): `ensureDefaultOrganization` under `single`, with a sole organization of another slug, created a second, `default`-slugged organization and bound the administrator there. **TO:** it reuses the sole organization; no second organization is created. **Fix:** none needed; an install that wants a `default`-slugged organization re-slugs its existing one or creates it explicitly. + - **Widening** (`@objectstack/core`): under `single` only an organization-less `admin_full_access` grant conferred `PLATFORM_ADMIN`. Now a grant owned by the Default Organization confers it too and applies in every organization; a pre-existing Default-Organization-scoped `admin_full_access` grant gains the rung on upgrade; and `sys_organization` becomes a standing-deriving table (the resolver reads it only under `single` for a holder of an organization-owned `admin_full_access` grant — one or two small reads per uncached request; the last-administrator guard adds three to six small reads per guarded write). + + - **The promotion writes the Default Organization (`@objectstack/plugin-security`).** A `single` deployment with no configuration still promotes its first user that can sign in. The `admin_full_access` grant row that promotion writes now carries the Default Organization as its `organization_id` (ADR-0131 D5), never NULL. The Default Organization is the one with `slug = 'default'`, else the only organization: the organization the auth plugin creates at boot. A deployment that names no Default Organization (several organizations, none with the slug `default`) is not promoted. The bootstrap logs one `error` line saying why and returns `reason: 'default_organization_unresolved'`. It writes no grant without an owner. `OS_PLATFORM_OWNER_EMAIL` is still the walled-posture channel and is not required under `single`. + - **The authorization resolver reads that row (`@objectstack/core`).** Under `single`, `PLATFORM_ADMIN` derives from an `admin_full_access` grant owned by the Default Organization, and still from the organization-less row an earlier release wrote. The anchoring grant applies in whichever organization the user acts in, as the organization-less row always did. A grant of the set owned by any other organization confers the set in that organization, never platform standing. Under a walled posture no grant anchors standing (unchanged). One predicate decides both postures, with the posture as its parameter. The resolver reads `sys_organization` only for a user who holds an organization-owned `admin_full_access` grant under `single`. + - **An upgraded database keeps its administrator.** `os migrate organization-ownership --apply` gives a `single` database's organization-less grants to the Default Organization, the platform admin's grant included. Before this change the resolver then stopped grading that admin `PLATFORM_ADMIN`, and every later boot found no organization-less holder and promoted again. The holder scan now reads the Default Organization's grant, so a migrated database keeps its admin and promotes nobody else. A user who holds both rows counts as one holder. + - **The last-administrator guard and the default-organization bootstrap read it too (`@objectstack/plugin-auth`).** The break-glass guard counts the holder of the Default Organization's grant as a platform admin and treats that grant as evidence that the environment once had one. It also judges the two organization writes that move the anchor off that grant: changing the Default Organization's `slug` while another organization exists, and deleting it. Either is refused when it would leave no administrator who can sign in. `ensureDefaultOrganization` finds the admin through the same grant. Under `single` it also reuses the Default Organization when that is a sole organization with another slug, where it used to create a second, `default`-slugged organization. + +### Patch Changes + +- 6a49efb: fix(plugin-security): the `sys_user_position.position` refusal names the security catalog type it judged against, and reads no retired catalog table + + A `sys_user_position` write naming no position in the security catalog is still refused `400 VALIDATION_FAILED` with one `reference_not_found` entry at `position`. That entry's `constraint.target` now reads `position`, the security catalog type the value is judged against, instead of `sys_position`, a catalog table that is no longer registered. The sibling `sys_user_permission_set.permission_set` refusal already answers `target: 'permission'`. A client that read `constraint.target` should expect `position`. + + The refusal no longer looks a refused value up as a `sys_position` record id to suggest that row's name. That lookup could not succeed on a current deployment, so it never suggested a name. Every refused value now gets the same remedy: write the machine name of an existing position, or declare the position first. + + Also removed: the unused internal `per-organization-catalog.ts` module, which nothing imported. Nothing else changes. + + Clause-②: no +- eebaf3b: feat(objectql,core): the driver's tenant read scope is the organization wall's verdict — one predicate for Layer 0 and every driver (ADR-0131 D8) + + Clause-②: yes (widening) + + - **What changed.** The engine no longer derives `DriverOptions.tenantId` / `tenantIds` beside the organization wall. `buildDriverOptions` projects the wall's own verdict onto them: the verdict plugin-security records on the operation (`OperationContext.tenantLayer0Verdict`) where its wall ran, and otherwise the same function over the engine's own facts (the posture in force, the context's organization and membership set). The function, `computeTenantLayer0Verdict` with its input type `TenantLayer0Input`, moves to `@objectstack/core` (new export); `@objectstack/plugin-security`'s `tenant-layer.ts` re-exports it and its Layer 0 is byte-identical. + - **FROM → TO, per posture, for a read, count, aggregate, update or delete on an object with an organization column:** + + | posture / caller | driver scope before | driver scope now | + |:--|:--|:--| + | `single`, any caller naming an organization | that organization (plus organization-less rows) | **none** | + | `isolated`, member | the active organization | unchanged | + | `group`, member | the membership set | unchanged | + | walled, exempt `PLATFORM_ADMIN` on a posture-permitting object, or the verified platform owner's read | the active organization | **none** — what Layer 0 already admitted | + | walled, system context naming an organization | that organization | unchanged | + + - **Not changed.** Inserts and upserts keep the active organization as `tenantId` in every posture: the row is stamped with it, its counter scopes by it, and an upsert that would merge into another organization's row is still refused. Both `orWhereNull` compatibility arms in `SqlDriver.applyTenantScope` stay (their retirement is a later stage of ADR-0131 D13). + - **Who notices.** A `single` deployment that holds more than one organization was kept apart by the driver only by accident; postures now differ only in enforcement (ADR-0131 D11), and `single` enforces no wall. To keep organizations apart, run the `isolated` or `group` posture. Code that relied on a context organization to narrow a read on `single` names the organization in its own `where`. + - **Migration.** ADR-0087 semantic entry `driver-read-scope-follows-tenant-wall`. A deployment's `platformGlobalObjects` carve-out already reaches the driver through the column plan (ADR-0131 D7): a declared object carries no organization column, so neither wall has anything to scope. +- 2bcd406: fix(plugin-security): the security catalog echo keeps naming a disabled package's permission set or position as that package's + + The echo reads which package owns a stored overlay from the protocol's by-name read. That read now withholds a disabled package's item and keeps its protection envelope, so the echo reads the envelope's `packageId` instead of `item._packageId`. Before, a disabled package's set was echoed unowned, lost its ADR-0090 D5 audience-binding suggestion, and stayed unowned after the package was re-enabled until the next save, publish or boot. Enforcement is unchanged. +- 44a5011: Clause-②: no + + Refusals and Setup help text that a user reads inside the running system no longer call that system + "this environment". They say "this system", or they drop the noun. + + - `@objectstack/plugin-auth`: the two self-registration refusals drop the noun ("Self-registration is + closed (audience posture invite_only).", "Self-registration is limited to approved email domains, …"). + So do the OAuth self-service `503` ("OIDC provider is not enabled") and the SSO `400` + `DOMAIN_VERIFICATION_DISABLED` ("Domain verification is not enabled (set + OS_SSO_DOMAIN_VERIFICATION)."). The last-administrator guard's refusals and its two warning lines say + "this system", including "nobody able to administer the system". The OIDC authorize-gate refusal + (`403 ENV_ACCESS_DENIED`) keeps "this environment". Only the cloud control plane installs that gate, + and there the noun names the environment a cloud user is signing in to. + - `@objectstack/plugin-security`: the diagnostic `message` of the packaged-permission-set lock now reads + "… is declared by package '…' and is locked — editing it here would …". The provenance-unknown + refusal says "this system could not determine …". Both `userMessage` texts are unchanged. + - `@objectstack/service-settings`: the Mail and SMS provider groups read "Choose how this system sends + outbound email." and "… outbound SMS.". The Authentication audience group reads "Who may become a user + of this system's apps.". The zh-CN, ja-JP and es-ES bundles change only the noun, to 本系统, + このシステム and este sistema. + + Only the human-readable text moves. Every HTTP status, error code, settings key and response shape + stays as it was. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/plugins/plugin-security/package.json b/packages/plugins/plugin-security/package.json index a73cb353523..03641e72be3 100644 --- a/packages/plugins/plugin-security/package.json +++ b/packages/plugins/plugin-security/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-security", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Security Plugin for ObjectStack — RBAC, RLS, and Field-Level Security Runtime", "main": "dist/index.js", diff --git a/packages/plugins/plugin-sharing/CHANGELOG.md b/packages/plugins/plugin-sharing/CHANGELOG.md index 0d0d80361ba..40525e2138e 100644 --- a/packages/plugins/plugin-sharing/CHANGELOG.md +++ b/packages/plugins/plugin-sharing/CHANGELOG.md @@ -1,5 +1,128 @@ # @objectstack/plugin-sharing +## 18.0.0-next.4 + +### Minor Changes + +- 78652c0: feat(cli,plugin-sharing)!: in the 17 → 18 ownership ceremony, a row whose owner cannot be derived blocks certification, and a declared-rule mirror's grants are renamed only onto a rule plugin-sharing serves + + Clause-②: yes (narrowing) + + + + **BREAKING** (narrowings), shipped as `minor` under the launch-window convention for breaking changes. + + - **FROM** (`@objectstack/cli`): `os migrate organization-ownership --apply` on a database holding a row whose owner cannot be derived exited `0` with `blocking: 0`, counted the row in `advisory`, and verified the `sys_migration` marker. This covers a reported (fate 4) row, and a row whose anchor is missing under a wall (fate 3). **TO:** it exits `1`, counts the row in `blocking`, and leaves the marker unverified, so a v18 `os serve` / `os start` does not start on the database. **Fix:** give each row its organization (write its `organization_id`, or repair the anchor its reason names), or delete it by hand, then re-run `--apply` with the same plan file. + - **FROM** (`@objectstack/cli`): a database an earlier version of this command certified while it held such a row was certified again by the next `--apply`. **TO:** that run leaves its marker unverified, for the same rows. **Fix:** the same as above. + - **FROM** (`@objectstack/cli`): `os dev`'s in-place upgrade of a development database started the server again whenever every ceremony step finished. **TO:** it starts it only when the post-check also holds nothing (`blocking` 0); otherwise it prints each table that holds, with its count and remedy, and the command that resumes the run. **Fix:** resolve the rows it names, then run the printed command. + - **FROM** (`@objectstack/cli`): the `mirror-deletion` step renamed the grants of a declared-rule mirror onto its rule's name, and deleted the mirror, even when plugin-sharing does not serve that rule (a condition that does not translate, an unmappable recipient, a retired owner-based rule); the next boot's orphaned-grant sweep then revoked those grants. **TO:** while `sys_record_share` grants name such a mirror, the step blocks, naming the rule, its rows and the reason, and writes nothing. **Fix:** correct the rule's declaration in its package so it is served, or delete each mirror row and the grants that name it, then re-run `--apply`. + + `@objectstack/cli` — **unattributable rows hold certification (ADR-0131 D10 fate 4).** The ceremony still never guesses an owner for such a row and never deletes it. The post-check now holds each one against the verdict: + + - Each table that holds one carries `held: { rows, remedy }` in the apply report, and counts those rows in its `remaining`. The terminal output names each table, its row count and the remedy. + - `advisory` now counts only the rows of an advisory departure and the reported rows of a table whose `organization_id` is not the tenancy anchor (`sys_sso_provider`, the retired `sys_scim_provider`). A row there that is bound to no organization is legitimate, is reported, and does not block. + - So a verified marker means no organization-less row on any table that the tenancy anchor governs. + - The plan's summary line says that such rows will hold `--apply`'s certification until they are resolved. + + `@objectstack/cli` — **the ceremony asks plugin-sharing which declared sharing rules it serves.** The port the `mirror-deletion` step reads (`DeclaredSharingRulesPort.rules()`, formerly `names()`) judges each declared rule through plugin-sharing's own translation. A mirror's grants are renamed only onto a rule that is served. A mirror of a refused rule that no grant names is still deleted. + + `@objectstack/plugin-sharing` — **exports `translateDeclaredSharingRule`** with its `DeclaredSharingRule` and `DeclaredSharingRuleSkip` types. It is the one translation enforcement serves a declared rule through. It is pure, and it is unchanged. +- 5e97907: feat(plugin-sharing,cli)!: declared sharing rules are read from the registry — the boot writes no `sys_sharing_rule` row for them, and the 17 → 18 ownership ceremony deletes the copies the retired seeder wrote + + Clause-②: no (narrowing) + + + + The narrowings, FROM → TO, each with its one-line remedy: + + - `DELETE /api/v1/sharing/rules/:idOrName` on a declared rule — FROM `204`, the seeded row deleted (and written again by the next boot), TO `403 PERMISSION_DENIED`. Remedy: change or remove the rule in its declaration in the package; an organization's own rule is deleted as before. + - `sys_sharing_rule` — FROM one `managed_by: package` row per declared rule, TO none: the boot writes none and the ownership ceremony deletes the copies the seeder wrote (Setup's Sharing Rules object list, which lists rows, no longer shows declared rules). Remedy: read declared rules through `GET /api/v1/sharing/rules`, which lists them beside the organization's own; on an upgraded database run `os migrate organization-ownership --apply` with a fresh plan. + + The retirement note for the seeder is the ADR-0087 semantic entry `sharing-rule-declared-mirror-retired`. + + **BREAKING**, shipped as `minor` under the repo's launch-window convention for breaking changes. + + `@objectstack/plugin-sharing` no longer copies a stack's declared `sharingRules` into `sys_sharing_rule` at boot. ADR-0131 D2 retires that seeder (`bootstrapDeclaredSharingRules`). The sharing-rule service reads each declared rule from the registry whenever it reads the rule set: listing, getting, evaluating, the per-record hooks, the boot backfill and the orphaned-grant sweep. Rules an organization authors keep their `sys_sharing_rule` rows. + + - **A declared rule still grants, and stops granting when its declaration goes.** It is translated from the registry's copy exactly as the seeder translated it: the CEL `condition` is lowered to a `criteria` filter, and `sharedWith` becomes the recipient. A condition that does not lower is not served, and never as a match-all. It says so in one WARN line per rule per process. A declared rule's id is its NAME, and the grants it materialises carry the name as `sys_record_share.source_id`. Remove the declaration and the next boot's orphaned-grant sweep revokes those grants. + - **Scopes follow the posture, as the seeder's passes did.** Under `single` a declared rule is served once, organization-less, so every organization reads it. Under a wall it is served once per organization — every organization, read page by page. An organization created after boot is served from its creation on. If the organizations cannot be read at boot, no declared rule is served until the next boot, and the boot's orphaned-grant sweep revokes none of their grants meanwhile: a failed read is never a revocation. + - **A stored row of the same name answers for it.** Under `single` that is a row of that name in any organization, and under a wall that organization's own row. Such a row is an organization's own rule, a seeded copy an administrator customized, or a seeded copy the ceremony has not deleted yet. The declared rule is not served beside it, the same as while the seeder ran: it never overwrote an admin-authored or customized row. + - **`DELETE /api/v1/sharing/rules/:idOrName` refuses a declared rule** with `403 PERMISSION_DENIED`. It has no row: it is changed or removed in its declaration. Before this release a platform administrator could delete the seeded row, and the next boot wrote it again. An organization's own rule is deleted as before. + - **Setup's Sharing Rules object list no longer shows declared rules.** It lists `sys_sharing_rule` rows. `GET /api/v1/sharing/rules` lists both. + + `@objectstack/cli` — **`os migrate organization-ownership --apply` deletes the declared-rule mirrors.** The `declared-rule-mirrors` rows on `sys_sharing_rule` are the seeder's uncustomized copies (`managed_by` `package` or `platform`, `customized` not true). They now block certification until the ceremony's `mirror-deletion` step removes them. It works in the ruled order. It renames every grant naming a mirror by row id (`sys_record_share.source_id`, `source = 'rule'`) to the rule's name and verifies that no grant names a mirror any more. Only then does it delete the mirrors. Under a wall it deletes the organization-stamped copies too. + - A mirror whose rule no installed package declares, and that grants still name, blocks with nothing written. The blocker names the rows and the remedy. A mirror no grant names is deleted. + - The step also runs on a database whose ceremony already completed while an earlier release still seeded the rows, where they were advisory. Re-run `--apply` with a fresh plan. + - `DECLARED_SHARING_RULE_SEEDER_RUNS` is now `false`. A customized copy and an organization's own rule stay. Under `single` they take the Default Organization, as before. + + `@objectstack/lint` — the sharing-rule enforceability verdicts and `os explain` say what now happens to a declared rule whose condition does not lower. It is "never served", not "never seeded", because it is never written to `sys_sharing_rule`. The rule ids, severities, paths and what each rule accepts or refuses are unchanged. + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [ec749fb] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/plugin-sharing/package.json b/packages/plugins/plugin-sharing/package.json index 02e01253f97..6a74d80f531 100644 --- a/packages/plugins/plugin-sharing/package.json +++ b/packages/plugins/plugin-sharing/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-sharing", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Record-level sharing for ObjectStack — sys_record_share + middleware that enforces sharingModel + ISharingService.", "main": "dist/index.js", diff --git a/packages/plugins/plugin-webhooks/CHANGELOG.md b/packages/plugins/plugin-webhooks/CHANGELOG.md index 1fd98b873b5..8de8beba767 100644 --- a/packages/plugins/plugin-webhooks/CHANGELOG.md +++ b/packages/plugins/plugin-webhooks/CHANGELOG.md @@ -1,5 +1,68 @@ # @objectstack/plugin-webhooks +## 18.0.0-next.4 + +### Patch Changes + +- af284b0: feat(spec): the remaining `getSession` readers read `AuthSessionApi`'s declared result, and none reads a top-level `activeOrganizationId` + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains three optional `user` members**, each declared for a named reader: `isPlatformAdmin` (`boolean`, the ADR-0068 D2 rung plugin-auth's `customSession` derives) and `role` (`string | null`, better-auth's `admin` plugin scalar), both read by `judgePlatformAdmin` in `@objectstack/plugin-auth`; and `name` (`string`), read by the runtime dispatcher's AI-route `req.user` resolver as the display name. Every member is optional, so every implementer and caller typechecks unchanged. + - **The readers read through the declared type instead of `any` or a cast** (types only, runtime behaviour unchanged): `judgePlatformAdmin` and the four platform-admin mounts in `@objectstack/plugin-auth` (better-auth's own session type now has to fit the declaration at those reads), the REST server's two session reads (`@objectstack/rest`), the dispatcher's posture read, `resolveSessionData` and both `HttpDispatcher` reads (`@objectstack/runtime`), the cloud-connection and install-local session reads (`@objectstack/cloud-connection`), the webhook redeliver door (`@objectstack/plugin-webhooks`), and the storage session bridge (`@objectstack/service-storage`). + - **Fix — the top-level `activeOrganizationId` fallback is removed** from the storage upload stamp (`@objectstack/service-storage`) and from install-local's `resolveActiveOrgId` (`@objectstack/cloud-connection`). The active organization is read from `session.activeOrganizationId` alone, where the contract declares it. No in-process producer answers a top-level `activeOrganizationId` (better-auth's read is `{ user, session }`), so for the shipped auth plugin nothing changes. An auth service that answered only the flattened shape now gets no organization: the upload is not stamped, and install-local reports no active organization. To fix it, answer the active organization on `session.activeOrganizationId`, as `AuthSessionApi` declares it. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/service-messaging@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/plugins/plugin-webhooks/package.json b/packages/plugins/plugin-webhooks/package.json index 879d0500c7a..770244494fa 100644 --- a/packages/plugins/plugin-webhooks/package.json +++ b/packages/plugins/plugin-webhooks/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/plugin-webhooks", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Persistent, cluster-aware webhook dispatcher. Durable outbox + per-partition cluster.lock for exactly-once-ish delivery across nodes. See content/docs/concepts/webhook-delivery.mdx.", "type": "module", diff --git a/packages/qa/dogfood/CHANGELOG.md b/packages/qa/dogfood/CHANGELOG.md index 3d021a485b6..6f849c10d31 100644 --- a/packages/qa/dogfood/CHANGELOG.md +++ b/packages/qa/dogfood/CHANGELOG.md @@ -1,5 +1,133 @@ # @objectstack/dogfood +## 0.0.48-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [6a49efb] +- Updated dependencies [eebaf3b] +- Updated dependencies [78652c0] +- Updated dependencies [1eae21f] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [8d3dd6f] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [03ac4ec] +- Updated dependencies [5e97907] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [62c4441] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [8eccf51] +- Updated dependencies [8a88901] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [9040945] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [77e9f79] +- Updated dependencies [83dfbc0] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [e15897c] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [5da2e88] +- Updated dependencies [51ad8e4] +- Updated dependencies [ec749fb] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [538f923] +- Updated dependencies [d65826f] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [d8e11b5] +- Updated dependencies [44a5011] +- Updated dependencies [f573976] +- Updated dependencies [f573976] +- Updated dependencies [71fa166] +- Updated dependencies [e22defb] + - @objectstack/plugin-security@18.0.0-next.4 + - @objectstack/plugin-auth@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/plugin-sharing@18.0.0-next.4 + - @objectstack/verify@18.0.0-next.4 + - @objectstack/service-storage@18.0.0-next.4 + - @objectstack/plugin-audit@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/plugin-email@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/plugin-webhooks@18.0.0-next.4 + - @objectstack/plugin-approvals@18.0.0-next.4 + - @objectstack/mcp@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/example-showcase@0.3.22-next.4 + - @objectstack/connector-mcp@18.0.0-next.4 + - @objectstack/connector-openapi@18.0.0-next.4 + - @objectstack/connector-rest@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/plugin-pinyin-search@18.0.0-next.4 + - @objectstack/service-analytics@18.0.0-next.4 + - @objectstack/service-datasource@18.0.0-next.4 + - @objectstack/service-messaging@18.0.0-next.4 + - @objectstack/trigger-record-change@18.0.0-next.4 + - @objectstack/trigger-schedule@18.0.0-next.4 + - @objectstack/example-crm@4.0.100-next.4 + - @objectstack/example-multi-package@0.0.7-next.4 + - @objectstack/types@18.0.0-next.4 + ## 0.0.48-next.3 ### Patch Changes diff --git a/packages/qa/dogfood/package.json b/packages/qa/dogfood/package.json index cf0d6c920f0..f2047b5a60f 100644 --- a/packages/qa/dogfood/package.json +++ b/packages/qa/dogfood/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/dogfood", - "version": "0.0.48-next.3", + "version": "0.0.48-next.4", "private": true, "license": "Apache-2.0", "description": "Dogfood regression gate — hand-written golden tests that boot real example apps through @objectstack/verify's in-process HTTP stack, pinning historical runtime regressions (#2018 timezone bucketing, #1994 cross-owner RLS, #2004 field fidelity) that static checks miss.", diff --git a/packages/qa/downstream-contract/CHANGELOG.md b/packages/qa/downstream-contract/CHANGELOG.md index 95179673d63..23d5c807364 100644 --- a/packages/qa/downstream-contract/CHANGELOG.md +++ b/packages/qa/downstream-contract/CHANGELOG.md @@ -1,5 +1,46 @@ # @objectstack/downstream-contract +## 0.0.46-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + ## 0.0.46-next.3 ### Patch Changes diff --git a/packages/qa/downstream-contract/package.json b/packages/qa/downstream-contract/package.json index 61a5f0e0953..6ae8ecc6643 100644 --- a/packages/qa/downstream-contract/package.json +++ b/packages/qa/downstream-contract/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/downstream-contract", - "version": "0.0.46-next.3", + "version": "0.0.46-next.4", "description": "Frozen third-party consumer fixture — a backward-compatibility gate for @objectstack/spec. Authored the way an external project on a published release authors metadata; if a spec change breaks it, that change is breaking (#2035).", "license": "Apache-2.0", "private": true, diff --git a/packages/qa/http-conformance/CHANGELOG.md b/packages/qa/http-conformance/CHANGELOG.md index 0a6524fd803..3282dbb8828 100644 --- a/packages/qa/http-conformance/CHANGELOG.md +++ b/packages/qa/http-conformance/CHANGELOG.md @@ -1,5 +1,17 @@ # @objectstack/http-conformance +## 0.1.8-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [f5ef054] +- Updated dependencies [2ac0616] +- Updated dependencies [b036ac5] +- Updated dependencies [62e2b6d] + - @objectstack/core@18.0.0-next.4 + ## 0.1.8-next.3 ### Patch Changes diff --git a/packages/qa/http-conformance/package.json b/packages/qa/http-conformance/package.json index a4b8d788cca..c01aac35ca6 100644 --- a/packages/qa/http-conformance/package.json +++ b/packages/qa/http-conformance/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/http-conformance", - "version": "0.1.8-next.3", + "version": "0.1.8-next.4", "private": true, "license": "Apache-2.0", "description": "HTTP transport-port conformance gate (ADR-0076 D11/OQ#10, #2462) — a zero-dependency node:http reference implementation of IHttpServer plus a cross-adapter suite that boots the dispatcher bridge and REST generator on it AND on plugin-hono-server, pinning that the port stays free of framework-isms. Not published; validation instrument, not a product server.", diff --git a/packages/rest/CHANGELOG.md b/packages/rest/CHANGELOG.md index 152c73489f6..0a4521ec11a 100644 --- a/packages/rest/CHANGELOG.md +++ b/packages/rest/CHANGELOG.md @@ -1,5 +1,234 @@ # @objectstack/rest +## 18.0.0-next.4 + +### Minor Changes + +- a06f0d4: fix(rest)!: the `/data` API-exposure check treats an object of a disabled package as no object, so every operation on it answers `404 OBJECT_PACKAGE_DISABLED` again, with no fail-open warning + + Clause-②: no (narrowing) + + + + **BREAKING** (a refusal moves), shipped as `minor` under the launch-window convention for breaking changes. + + The by-name `/meta` read now refuses an object of a disabled package with `404 OBJECT_PACKAGE_DISABLED`. The API-exposure check reads that object by name. It now reads that refusal as "no object here": there is no exposure policy to judge, and nothing failed, so it logs nothing. The request then reaches the data plane, which refuses it with the same code. + + - **Changed back:** for an object whose package is disabled, a denied operation goes from `405 OBJECT_API_METHOD_NOT_ALLOWED` (an operation outside `apiMethods`) or `404 OBJECT_API_DISABLED` (an object with `apiEnabled: false`) back to `404 OBJECT_PACKAGE_DISABLED`. That is what it answered before the check read one object by name. An allowed operation still answers `404 OBJECT_PACKAGE_DISABLED`. + - **No warning now:** such a request no longer logs `api-exposure gate: object metadata read failed — failing open`. + - **Unchanged:** any other failed read still fails open and still logs that warning. Objects of enabled packages get the same exposure answers as before. + + What changes for you: nothing to rewrite. No accepted request is now refused, and no refused request is now accepted. +- aaaeeac: fix(rest)!: a schema-derived export skips the fields declared `hidden`, so the hidden `__search` search companion no longer adds an empty "Search Index" column + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + `GET /api/v1/data/:object/export` without `?fields=` builds its columns from the object schema. It took every field of the object, so the hidden `__search` companion, which the search-normalization seam declares on every object with an eligible name field whenever pinyin search is on, and whose value the data reads strip from every row, became a "Search Index" column that was empty on every row. The default columns are now the fields the object shows by default: a field declared `hidden` (`FieldSchema.hidden`, "Hidden from default UI") is not one. + + - **Served less now:** the CSV and XLSX default header, and its zero-row header, no longer carry a column for a field declared `hidden`. Measured on a dev boot of `examples/app-showcase`, over every object with a hidden field that the export serves: `Search Index` (`__search`, 64 objects, empty on every row), `Organization` (`organization_id`, 53 objects, the one that carries a value: the record's organization), `Owning Business Unit` (`owning_business_unit_id`, 24 objects, empty on every row because nothing stamps it yet), and `Session Token` (`sys_session`), `Previous Password Hashes` (`sys_account`) and `Hashed Key` (`sys_api_key`), all three empty because the read omits their values. No other column moved. + - **Unchanged:** an explicit `?fields=` list is answered as asked, a hidden field included: the header names it and its cells carry what the read returns (empty for `__search`, which the read strips). The field-level-security narrowing of the default columns is unchanged, and so are the zero-row header rules. `format=json` streams each row as the read returns it, has no column set, and is unchanged. `?template=true` keeps its own column rule. + + What changes for you: a consumer that wants a hidden field's column in an export names the field in `?fields=`. +- 8259ea0: fix(rest)!: `format=json` honours an explicit `?fields=`, so each exported row carries exactly the named keys, in list order + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + `GET /api/v1/data/:object/export` declares `fields=a,b,c` for every format. The CSV and XLSX arms wrote exactly the listed columns, but the JSON arm wrote each row as the read returned it, so `?format=json&fields=…` was ignored without any sign. objectui's list-view and grid export send `format=json` with the view's visible columns, so a JSON export of a list view carried every readable field instead of the view's columns. + + - **Served less now:** with `?fields=`, each JSON row carries exactly the named keys, in the order the list names them, with the same readable formatting as before. Measured on a dev boot of `examples/app-showcase`: `?format=json&fields=title` on `showcase_task` answered 25 keys per row (`id`, the audit and ownership columns, `organization_id`, `owning_business_unit_id` and every author field) and now answers `title` alone. A named key the read did not return (a field that field-level security removes from the row, or the `__search` companion the read strips) is absent from the JSON row. It is not written as `null`. The CSV and XLSX arms write an empty cell for it, as before. + - **Unchanged:** `format=json` without `?fields=` still writes each row as the read returns it. The CSV and XLSX arms, their default columns and their header rules are untouched. + + What changes for you: a caller that sends `format=json` with `fields` and reads a key it did not name, such as `id`, adds that key to `fields`. +- 2bcd406: fix(rest)!: `/history`, `/audit` and `/diff` answer a disabled package's item with the plain read's absence, never past the per-caller gate + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The by-name read now answers a disabled package's item as absent. The event and diff doors judge the item the plain read would judge, and they read "no item" as "nothing behind the name": events served whole, and a view's or dashboard's diff served whole, to a caller the item's own gate refuses. + + - **Absent now, `404 RESOURCE_NOT_FOUND`:** `GET /meta/:type/:name/history`, `/audit` and `/diff` for an `app`, `view`, `dashboard`, `doc` or `book` the plain read withholds while its layers still resolve, which is a disabled package's item. No events and no diff are read. + - **Unchanged:** an item that is really gone (every layer null, such as a deleted item) keeps its answer: its events, and its diff as before. Items of enabled packages keep the per-caller gate as before. + + What changes for you: re-enable the package to read its items' history, audit trail and diff again. +- 58a2faa: fix(rest)!: `/meta/:type/:name/layers` and the deprecated `?layers=true` answer a disabled package's non-object item as a missing name, on both transports + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + - **Absent now, byte for byte the missing name's answer (`404 RESOURCE_NOT_FOUND`):** the layered view of an item of every type but `object` and `package` whose owning package is disabled, on `GET /meta/:type/:name/layers` and on `GET /meta/:type/:name?layers=true`, to every caller, an author who may write the item included. One step in the shared layered chain (`createMetaLayeredAnswer`) answers it, so the runtime dispatcher's two spellings answer the same. The owner is the one the by-name read's protection envelope names (`packageId`), read under the request's `?package=` scope, asked of the engine registry's `isPackageDisabled`. Objects already answered `404 OBJECT_PACKAGE_DISABLED` here. + - **Unchanged:** items of enabled packages, a name with nothing behind it, and the protocol's `getMetaItemLayered`, which keeps serving every layer to the readers that resolve with it (the security catalog echo, the packaged-permission-set lock gate). + - **Added, optional:** `MetaItemReadGateSources.isItemOfDisabledPackage` and `MetaLayeredRequest.packageId`. A host that supplies neither answers as before. + + What changes for you: re-enable the package to read its items' layered views again. +- e74ec56: fix(rest)!: `/published` and `/meta/object/:name/state/:field` answer a disabled package's item as the by-name read does, and the event and diff doors' absence is the plain read's body + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + - **`GET /meta/:type/:name/published`, every type but `object` and `package`:** a disabled package's item is answered as a name with nothing behind it (`404 NOT_FOUND`, byte for byte), on both exits: the stored published row and the code/package snapshot. The owner is the one the by-name read's protection envelope names (`packageId`). Objects already answered `404 OBJECT_PACKAGE_DISABLED` here. + - **`GET /meta/object/:name/state/:field`:** a disabled package's object is refused `404 OBJECT_PACKAGE_DISABLED`, what `GET /meta/object/:name` answers, as the runtime dispatcher's twin already does. The schema is not read. + - **`/history`, `/audit` and `/diff`:** the `404 RESOURCE_NOT_FOUND` a disabled package's gated item gets is now the plain read's nested body, `{ "error": { "code": "RESOURCE_NOT_FOUND", "message": "Metadata item not found or access denied." } }`, instead of the flat `{ "error": "…", "code": "RESOURCE_NOT_FOUND" }`. Status and code are unchanged; read `body.error.code`. + - **Unchanged:** items of enabled packages, a deleted item, and a name with nothing behind it. + + What changes for you: re-enable the package to read its items' published versions and its objects' state machines again. +- 741aa85: fix(rest)!: the import doors honour `runAutomations: false` and `treatAsHistorical: true` only for the system principal, and refuse them `403 PERMISSION_DENIED` to every other caller + + Clause-②: no (narrowing) + + + + **BREAKING** (the import doors accept fewer request values: `runAutomations: false` and `treatAsHistorical: true` are refused from every caller but the system principal, administrators included), shipped as `minor` under the launch-window convention for breaking changes. Two known consumers break visibly: objectui's Import Wizard, whose two options now answer `403` for every signed-in user (#24381), and hotcrm's dev-only demo script, which sent the opt-out as the dev administrator and now exits by its own submitter check. + + Both flags take an app's declared business controls off every row an import writes. `runAutomations: false` writes with `skipAutomations`, so no metadata-bound hook and no record-change trigger runs (an approval flow and a closed-record freeze hook among them). `treatAsHistorical: true` writes with `skipStateMachine` and `preserveAudit`, so the object's `state_machine` rule is skipped and supplied audit and read-only values are kept. Until now, any caller who may import could send either. + + **What changes.** `POST /api/v1/data/:object/import` and `POST /api/v1/data/:object/import/jobs` admit either opt-out only from the system principal (`ExecutionContext.isSystem`), the predicate the doors that start an elevated self-triggered flow already ask. Every other caller, every signed-in user and administrator included, gets `403 PERMISSION_DENIED` in the ADR-0112 envelope, with `error.details: { object, refused }` naming the opt-outs it sent. The doors ask before the body is parsed: a refused request writes no row and creates no import job, on a dry run too. A hidden object keeps its `404` ahead of the refusal. + + **What stays as it was.** A request that omits both keys, or sends `runAutomations: true` / `treatAsHistorical: false`, imports exactly as before, with every control in force. `runImport` (`@objectstack/core`) is unchanged: in-process callers that run as the system principal, and the identity import in `@objectstack/plugin-auth`, keep their own settings. + + **If an import now answers 403.** Send it without `runAutomations: false` and `treatAsHistorical: true`: the rows are written with the object's automations, hooks, `state_machine` rule and audit stamps in force. A bulk load that must skip them is a migration, and runs in-process as the system principal. + +### Patch Changes + +- bb7ccde: The CRUD doors' registrar body lives in its own module, with no route, handler or answer changed + + Clause-②: no (a move: the six CRUD routes, their order, every handler and every response are unchanged, and the published declarations are byte-identical) + + - `RestServer#registerCrudEndpoints` keeps its name, docblock and place. The body that registers `GET`, `POST` and `POST …/query` on `{basePath}{crud.dataPrefix}/:object`, and `GET`, `PATCH` and `DELETE` on `…/:object/:id`, moved unedited to `packages/rest/src/crud-routes.ts`. The five `crud.operations` switches and `crud.dataPrefix` gate and shape the same mounts. + - Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved. +- df000d4: The `/meta` type list, whole-store maintenance and per-type list routes live in their own module, with no route, handler or answer changed + + Clause-②: no (a move: the seven routes per base, their place in the `/meta` registration order, every handler, every response and the anonymous-deny gate in front of them are unchanged, and the published declarations are byte-identical) + + - `RestServer#registerMetadataEndpointsInner` keeps its name, docblock, place and head. The statements that register `GET {metaPath}` and `GET {metaPath}/types`, `GET {metaPath}/diagnostics`, `GET {metaPath}/_drafts`, `POST {metaPath}/_migrate-stored` and `GET {metaPath}/:type` moved, re-indented, to `packages/rest/src/meta-collection-routes.ts`, and the method calls them at their old position, inside the umbrella's guarded registration. The `metadata.endpoints.types`, `.maintenance` and `.items` switches gate the same mounts. + - Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved. +- 646303e: The `/meta` per-item read routes live in their own module, with no route, handler or answer changed + + Clause-②: no (a move: the four routes per base, their place in the `/meta` registration order, every handler, every response and the anonymous-deny gate in front of them are unchanged, and the published declarations are byte-identical) + + - `RestServer#registerMetadataEndpointsInner` keeps its name, docblock, place and head. The statement that registers `GET {metaPath}/:type/:name/references`, `GET {metaPath}/:type/:name/layers`, `GET {metaPath}/book/:name/tree` and `GET {metaPath}/:type/:name` moved, re-indented, to `packages/rest/src/meta-item-read-routes.ts`, and the method calls it at its old position, inside the umbrella's guarded registration. The `metadata.endpoints.item` switch gates the same mounts. + - Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved. +- e9bc60b: The `/meta` per-item write routes live in their own module, with no route, handler or answer changed + + Clause-②: no (a move: the two routes per base, their place in the `/meta` registration order, every handler, every response and the anonymous-deny gate in front of them are unchanged, and the published declarations are byte-identical) + + - `RestServer#registerMetadataEndpointsInner` keeps its name, docblock, place and head, its per-item registrar included. The two statements that register `PUT {metaPath}/:type/:name` (the item save) and `DELETE {metaPath}/:type/:name` (the reset to the artifact default) moved, re-indented, to `packages/rest/src/meta-item-write-routes.ts`, and the method calls them at their old position, inside the umbrella's guarded registration. They still register through that per-item registrar, so the `metadata.endpoints.item` switch gates the same mounts. + - Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved. +- 52ff308: The `/meta` history family routes live in their own module, with no route, handler or answer changed + + Clause-②: no (a move: the five routes per base, their place in the `/meta` registration order, every handler, every response and the anonymous-deny gate in front of them are unchanged, and the published declarations are byte-identical) + + - `RestServer#registerMetadataEndpointsInner` keeps its name, docblock, place and head, its per-item registrar included. The five statements that register `GET {metaPath}/:type/:name/history` (the change log), `GET …/audit` (the audit trail), `POST …/publish` (the promotion of a pending draft), `POST …/rollback` (the restore of an earlier version) and `GET …/diff` (the comparison of two stored versions) moved, re-indented, to `packages/rest/src/meta-item-history-routes.ts`, and the method calls them at their old position, inside the umbrella's guarded registration. They still register through that per-item registrar, so the `metadata.endpoints.item` switch gates the same mounts. + - Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved. +- d1f082d: The `/meta` object state-machine read and the published read live in their own modules, with no route, handler or answer changed + + Clause-②: no (a move: the two routes per base, their place in the `/meta` registration order, every handler, every response and the anonymous-deny gate in front of them are unchanged, and the published declarations are byte-identical) + + - `RestServer#registerMetadataEndpointsInner` keeps its name, docblock, place and head, its per-item registrar included. The statement that registers `GET {metaPath}/object/:name/state/:field` (the legal next states of an object field's state machine) moved, re-indented, to `packages/rest/src/meta-object-state-routes.ts`, and the block that registers `GET {metaPath}/:type/:name/published` (an item's published version) moved to `packages/rest/src/meta-item-history-routes.ts`, beside the history family. The method calls each at its old position, inside the umbrella's guarded registration. The state read still answers to `api.enableMetadata` alone, and the published read still registers through the per-item registrar, so the `metadata.endpoints.item` switch gates the same mounts. + - Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved. +- 0d34066: REST's request identity door resolves through `@objectstack/core`'s `resolveRequestExecutionContext`, the one request-to-`ExecutionContext` composition + + Clause-②: no + + - `RestServer` resolves the caller of every data, metadata and package route by calling `resolveRequestExecutionContext` with this door's differences as named parameters: `anonymous: 'fail-closed'`, `authGate: 'reread-when-active'`, `credentials: 'api-key-and-session'`, `sessionApi: 'required'`, `session: 'request'`, its settings lookup for the localization, its own request-locale parse, `accessToken: 'withhold'`, and no OAuth provenance or admission channel. The environment and auth-service resolution, the header normalisation, the data engine, the tenancy wiring, the per-request memo, the `Server-Timing` disclosure and the fail-closed `catch` stay at the door. + - The answers are the ones the door gave before: an anonymous caller gets `401` with the shared body; a session the authentication policy holds gets `403` with its code and message while a gate feature is active; an API key is judged by its own owner's posture; an unreadable permission store, tenancy service or active gate re-read gets `503 SERVICE_UNAVAILABLE`; and an API key its own lifecycle refuses gets its `403` and code. + - One ordering moved, and it shows only when two faults coincide on the single-kernel wiring. The data engine is now resolved before the auth service's session read is asked for. If the configured engine provider fails AND the auth service exposes no session read (or its `getApi()` rejects), the answer is now `503 SERVICE_UNAVAILABLE` (the engine outage) instead of `401`. With a working auth service, the engine outage was already `503`. + - Nothing to do for a consumer: no export, route, configuration key or response shape changed. +- 9975c26: docs(rest): the public form routes' docstring says what authorizes an anonymous submit — the form's own declaration (`publicFormGrant`), not a `guest_portal` permission set — and that nothing falls open without one (#24130) + + Clause-②: no + + The docstring of `RestServer#registerFormEndpoints`, which ships in `@objectstack/rest`'s published declarations, said the submit route delegates security to a `guest_portal` permission set on its execution context, that a deployment without that set "falls open", and that secure-by-default deployments must pair the form routes with one. None of that is what runs. The submit hands the engine a `publicFormGrant` scoped to the form's target object, and the security plugin admits on that grant alone, before any permission set resolves: the create and the read-back on that object only, every other operation and object refused, the server-managed anchors stripped from the payload. The grant never falls open and admits the same operations whether or not a `guest_portal` set is registered. The docstring now says this, and says what the `guest_portal` name on the context does reach: where a deployment registers a set under it, that set is what the grant's result masker reads over the record the grant hands back, and the submit answers the created id only. + + Text only; nothing the form routes do changes. +- 2ac0616: fix(rest)!: an anonymous public-form submit that leaves a form-required field empty answers `400 VALIDATION_FAILED` naming the field, and stores nothing + + Clause-②: no (narrowing) + + + + - **What changed.** `POST /forms/:slug/submit` now enforces the form's own `required`: a section entry `{ field: 'email', required: true }` is judged before the write, with the rule the engine's object-level check uses (`isEmptyForRequired` from `@objectstack/core`). A submission whose value for such a field is absent, `null`, `''`, only whitespace, or `[]` on a multi-value field is refused, and no row is created. Before, the engine judged only the fields the OBJECT requires, so a field only the form required answered `201` and was stored empty — while the rendered form refused the same submission. + - **The refusal** is the object-level one's envelope on this route, key for key: `400` with top-level `code: 'VALIDATION_FAILED'`, `error` (the messages), and `fields[]` of `{ field, code: 'required', message, label }`, worded by the same validation catalog (for example `Email is required`). Every empty form-required field is named in one refusal, in the form's order. + - **BREAKING — a narrowing, and who it affects.** A script or integration that posts to a public form and omits a field the form marks `required: true` now gets `400` instead of `201`. The fix is to send that field. Nothing that was refused before is accepted now. + - **Judged only where the form asks.** An entry the form hides (`hidden: true`), or shows only conditionally (`visibleWhen` on the entry or on its section), is not judged — nothing server-side evaluates a form-view `visibleWhen`. A server-managed anchor (`owner_id`, `organization_id`, …) is never written by this route, so it is never judged. An object `defaultValue` or a `beforeInsert` hook does not satisfy a field the visitor omitted: the route judges what the visitor sent. +- af284b0: feat(spec): the remaining `getSession` readers read `AuthSessionApi`'s declared result, and none reads a top-level `activeOrganizationId` + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains three optional `user` members**, each declared for a named reader: `isPlatformAdmin` (`boolean`, the ADR-0068 D2 rung plugin-auth's `customSession` derives) and `role` (`string | null`, better-auth's `admin` plugin scalar), both read by `judgePlatformAdmin` in `@objectstack/plugin-auth`; and `name` (`string`), read by the runtime dispatcher's AI-route `req.user` resolver as the display name. Every member is optional, so every implementer and caller typechecks unchanged. + - **The readers read through the declared type instead of `any` or a cast** (types only, runtime behaviour unchanged): `judgePlatformAdmin` and the four platform-admin mounts in `@objectstack/plugin-auth` (better-auth's own session type now has to fit the declaration at those reads), the REST server's two session reads (`@objectstack/rest`), the dispatcher's posture read, `resolveSessionData` and both `HttpDispatcher` reads (`@objectstack/runtime`), the cloud-connection and install-local session reads (`@objectstack/cloud-connection`), the webhook redeliver door (`@objectstack/plugin-webhooks`), and the storage session bridge (`@objectstack/service-storage`). + - **Fix — the top-level `activeOrganizationId` fallback is removed** from the storage upload stamp (`@objectstack/service-storage`) and from install-local's `resolveActiveOrgId` (`@objectstack/cloud-connection`). The active organization is read from `session.activeOrganizationId` alone, where the contract declares it. No in-process producer answers a top-level `activeOrganizationId` (better-auth's read is `{ user, session }`), so for the shipped auth plugin nothing changes. An auth service that answered only the flattened shape now gets no organization: the upload is not stamped, and install-local reports no active organization. To fix it, answer the active organization on `session.activeOrganizationId`, as `AuthSessionApi` declares it. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/service-package@18.0.0-next.4 + - @objectstack/observability@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/rest/package.json b/packages/rest/package.json index 7b01cfd0b8c..373fac44278 100644 --- a/packages/rest/package.json +++ b/packages/rest/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/rest", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack REST API Server - automatic REST endpoint generation from protocol", "type": "module", diff --git a/packages/runtime/CHANGELOG.md b/packages/runtime/CHANGELOG.md index 3ccd98b1978..a7b958450f3 100644 --- a/packages/runtime/CHANGELOG.md +++ b/packages/runtime/CHANGELOG.md @@ -1,5 +1,350 @@ # @objectstack/runtime +## 18.0.0-next.4 + +### Minor Changes + +- a06f0d4: fix(runtime)!: the dispatcher's `GET /meta/object/:name` answers an object of a disabled package with the protocol's `404 OBJECT_PACKAGE_DISABLED`, never the registry's copy + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The dispatcher's object read had two registry reads. On an unscoped kernel it read `registry.getObject(name)` before the protocol. On a scoped kernel it fell back to `registry.getObject(name)` when the protocol read threw. The registry keeps a disabled package's objects on purpose, so both reads served such an object. + + - **Refused now:** `GET /meta/object/:name` on the dispatcher transport, for an object whose owning package is disabled. It answers the protocol's `404 OBJECT_PACKAGE_DISABLED`, on scoped and unscoped kernels alike. + - **Unchanged:** every other object read, including the registry fallback for an object the protocol misses for any other reason. + + What changes for you: re-enable the package to read the object again. +- 58a2faa: fix(runtime)!: the dispatcher's `/meta/:type/:name/layers` and `?layers=true` answer a disabled package's non-object item as a missing name + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + - **Absent now, `404 RESOURCE_NOT_FOUND` "Not found", byte for byte the missing name's answer:** `GET /meta/:type/:name/layers` and `GET /meta/:type/:name?layers=true` (with its `Deprecation` header) for an item of every type but `object` and `package` whose owning package is disabled, to every caller. The owner is the one the by-name read's protection envelope names (`packageId`) under the request's `?package=` scope, as `RestServer` asks it. Objects already answered `404 OBJECT_PACKAGE_DISABLED` here. + - **Unchanged:** items of enabled packages, and a name with nothing behind it. + + What changes for you: re-enable the package to read its items' layered views again. +- 2bcd406: fix(runtime)!: the dispatcher's `/meta` doors that read past the protocol answer a disabled package's object or item as the protocol's by-name read does + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + The registry keeps a disabled package's objects on purpose, and the metadata service knows nothing of a disable. Four dispatcher doors read one of them for a disabled package's object or item and served it. + + - **Refused now, `404 OBJECT_PACKAGE_DISABLED`:** `GET /meta/object/:name/state/:field`, and the legacy one-segment `GET /meta/:name` exit, for an object whose owning package is disabled. Both read `registry.getObject`; they now answer the protocol's refusal, the answer `GET /meta/object/:name` gives. + - **Refused now, `404 OBJECT_PACKAGE_DISABLED`:** `GET /meta/object/:name/published`. When the layered read refused the object, this door fell through to the published snapshot and served it. The refusal is now the answer, as on `RestServer`'s `/published`. + - **Absent now, `404 RESOURCE_NOT_FOUND`:** `GET /meta/:type/:name` for a non-object item of a disabled package. The protocol answers it as absent, and the door no longer serves the metadata service's copy in its place. The body is byte for byte the answer for a missing name. + - **Unchanged:** objects and items of enabled packages, and every other fall-through. + + What changes for you: re-enable the package to read the object or item again. +- e74ec56: fix(runtime)!: the dispatcher's `/meta/:type/:name/published` answers a disabled package's non-object item as a missing name + + Clause-②: no (narrowing) + + + + **BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + - **Absent now, `404 RESOURCE_NOT_FOUND` "Not found", byte for byte the missing name's answer:** `GET /meta/:type/:name/published` for an item of every type but `object` and `package` whose owning package is disabled, on the stored published row and the code/package snapshot alike. The owner is the one the by-name read's protection envelope names (`packageId`), as `RestServer`'s `/published` asks it. Objects already answered `404 OBJECT_PACKAGE_DISABLED` here. + - **Unchanged:** items of enabled packages, and a name with nothing behind it. + + What changes for you: re-enable the package to read its items' published versions again. +- 1e001c7: feat(runtime): `POST /packages/:id/publish-drafts` accepts a `backfill` body key and passes it to the protocol with the caller's execution context + + Clause-②: yes (widening) + + - The body's `backfill` (`[{ object, field }]`) reaches `publishPackageDrafts` as sent, together with the request's own execution context, so the backfill writes run as the signed-in caller, never as the system. A body without the key sends neither. + - The protocol's request refusal answers `400 VALIDATION_ERROR` with its `issues[]`, before anything is published, flipped or announced. The `backfillApplied` receipt rides the `200` body. +- d79dc48: fix(runtime)!: an `operation: 'update'` action that declares no `params` refuses every undeclared wire param at both action doors, instead of writing it to the row + + Clause-②: no (narrowing) + + + + **BREAKING** (the REST `/actions` door and the MCP `run_action` door now refuse, with a 400 `VALIDATION_ERROR`, a wire param that a declarative update action without `params` does not declare; measured consumers: objectstack-ai/hotcrm's `complete_task` called with such params; any console vendored before objectui `fb501bc894`, which posts the action's `patch` keys as `params`; and page action rows (`action:button` / `action:icon` with `operation: 'update'`) whose static object `params` the console forwards to a param-less declarative update action, none of which exist in this repo, with the row surface tracked in #24394), shipped as `minor` under the launch-window convention for breaking changes. + + A declarative update (`operation: 'update'` + `patch`) has no handler: the route merges the request's `params` over `patch` and writes the result to the current record. An action that declared no `params` skipped the ADR-0104 D2 param check altogether, so any key the caller sent was written too. An action declaring `patch: { status: 'completed' }` could rewrite any other field the caller may write, which is not what its author declared. + + Now an empty or absent `params` list on a declarative update is an empty accept set. Every key that is neither a declared param nor a dispatcher built-in (`recordId`, `objectName`, `_selectedIds`) is refused before anything is read or written, through the same D2 path a declared-params action already takes: the same `Invalid action params: Unknown action param "KEY"` message, the same 400, and the same `OS_ALLOW_LAX_ACTION_PARAMS=1` escape hatch. The action still writes its `patch` when the request carries no `params` or an empty bag, and a declared param still writes and still wins over `patch`. + + Unchanged: an action with a handler (`target`, `body`, or a flow) that declares no `params` keeps its pass-through. + + Also at the MCP `run_action` door: every ADR-0104 D2 param refusal, and the declarative update's own refusals (no `recordId`, or nothing to write), now carry `code: 'VALIDATION_ERROR'` and `status: 400`, so the tool error renders the coded envelope the REST door already answers with, rather than the message alone. + + What to do: declare the param on the action if the request's value should be written, or stop sending it, because the route applies `patch` from the declaration and never needs it on the wire. + +### Patch Changes + +- 6e43b7b: fix(runtime): the `keys` and `actions` dispatcher domains answer their `405 METHOD_NOT_ALLOWED` with an `Allow: POST` header + + Clause-②: no + + RFC 9110 requires a `405` to carry an `Allow` header naming the methods the target resource supports. The `mcp`, `meta` and `ai` dispatcher domains already send one. The `keys` and `actions` domains answered their `405` without it, so a client could see that its verb was refused but not which verb the route takes. + + Both domains serve one method, `POST`, on every path they claim, and their `405` now carries `Allow: POST`: + + - **`/api/v1/keys`.** A signed-in `GET`, `PUT`, `PATCH` or `DELETE` is answered `405` with `Allow: POST`, on the standalone server (`os dev`, `os serve`) and on a host built with `@objectstack/hono`'s `createHonoApp`. + - **`/api/v1/actions/...`.** A signed-in non-`POST` request that reaches the `actions` domain is answered `405` with `Allow: POST`. That is every such request on a `createHonoApp` host. The standalone server mounts these paths for `POST` only, so its HTTP adapter answers other verbs itself, and that answer already carried `Allow: POST`. + + The status, the body and its `error.code` are unchanged: `{ success: false, error: { code: 'METHOD_NOT_ALLOWED', message: 'Method not allowed', httpStatus: 405 } }`. Each domain still decides an anonymous caller before it looks at the method, so an anonymous `GET` (or any other verb the anonymous decision refuses) that reaches either domain is answered `401 UNAUTHENTICATED`, with no `Allow` header. +- 727091c: fix(runtime): every PUT and PATCH route the dispatcher mounts answers with an `X-Request-Id` and reports its 5xx to the error reporter, as its GET, POST and DELETE routes always did + + Clause-②: no + + The dispatcher wraps each route it mounts in its observability instrumentation. That wrapper echoes a valid inbound `X-Request-Id` (and mints a `req_`-prefixed hex id otherwise), sets `req.requestId`, hands a 5xx to the configured `observability.errorReporter`, and gives the `[5xx]` log line its method, route and request id. It wrapped routes registered through `get`, `post` and `delete` only, so every route mounted through `put` or `patch` answered without any of that: `PUT` and `PATCH /api/v1/keys`, the `PUT` and `PATCH` mounts of the three `/notifications` paths, `PATCH /packages/:id` (and its `/enable` and `/disable`), `PUT /automation/:name`, the `PUT` and `PATCH /ai/*` wildcards, and PATCH routes a service emits on `ai:routes`. It now wraps all five verbs. + + - **On the wire:** a `PUT` or `PATCH` answer from these routes now carries the `X-Request-Id` header (or the header `observability.requestIdHeader` names), as "Debugging with Request IDs" in the error-handling guide describes. + - **For an operator:** a 5xx on one of these routes now reaches `observability.errorReporter`, and its `[5xx]` log line now carries the method, route and request id. + - **Metrics:** on the standalone Hono server nothing changes. The transport already counted and timed these requests through `afterResponse`, and it still does, once per request. On a transport without `afterResponse`, `PUT` and `PATCH` requests on these routes are now counted in `http_requests_total` and timed in `http_request_duration_ms`, as the other three verbs were. + + What changes for you: nothing to rewrite. +- eda6f14: feat(spec)!: an install through `POST /api/v1/packages` may declare `manifest.scope` `project` only; `cloud` and `system` are refused, so an authored install can no longer lock itself + + `cloud` and `system` are the scopes of packages the platform ships, and the ADR-0070 D2 read-only rule (`isWritablePackage`) judges a registered package by its stored `manifest.scope`. An install through `POST /api/v1/packages` authors the package, yet the install declaration admitted all three scopes. So a manifest that copied `scope: 'cloud'` or `'system'` from a shipped package answered `201` and produced an authored package that `DELETE /packages/:id`, `PATCH /packages/:id/disable` and `POST /packages?overwrite=true` then refused with `422 WRITABLE_PACKAGE_REQUIRED`, and the stored row came back on every restart. The install declaration now narrows the install manifest's `scope` to `project` (new export `PackageInstallManifestSchema`, `ManifestSchema` with that one key narrowed), on both body forms of `PackageInstallBodySchema` and on `PackageInstallRequestSchema.manifest`. `ManifestSchema` itself is unchanged: platform packages still declare all three scopes, and they reach the registry by code boot, not over this door. The door already parses its body through that declaration, so it answers the refusal with no rule of its own; its bare-form refusal sentence now re-reads the union's own bare branch, so it carries the prescription too. + + Clause-②: no (narrowing) + + + + **BREAKING** (a published request declaration's accept set narrows), shipped as `minor` under the launch-window convention for breaking changes. + + - **FROM:** `POST /api/v1/packages` with a manifest declaring `scope: 'cloud'` or `scope: 'system'`, in the wrapped body `{ manifest }` or as a bare manifest, with or without `overwrite`, answered `201`; `PackageInstallBodySchema` and `PackageInstallRequestSchema` parsed it green. + - **TO:** the same request answers `400` with `error.code: 'VALIDATION_ERROR'`, names `scope` and the value sent, and installs nothing; both schemas refuse it with an `invalid_value` issue at `manifest.scope` (wrapped) or `scope` (bare). + - **Unchanged:** a manifest with `scope: 'project'` or no `scope` installs as before (`201`), and the door still stores the manifest as sent, so an absent `scope` stays absent. `ManifestSchema` and every other producer of it keep `cloud` / `system`. + - **The fix for an integrator:** remove `scope` from the manifest you install (it defaults to `project`), or write `scope: 'project'`. + - **A package that already locked itself this way** (an authored `sys_packages` row whose manifest declares `cloud` or `system`) stays read-only after this change, because the read-only rule reads the stored scope. No API door removes it; an operator deletes its row from the environment's database (`DELETE FROM sys_packages WHERE id = 'PACKAGE_ID'`) and restarts the server. +- d702b18: fix(cli,runtime): a dangling symlink standing as the SQLite database's directory is refused at once by `os dev` and `os serve`, naming the database file and the directory, instead of failing the boot later in a plugin's start + + Clause-②: no + + **What was wrong.** `os dev` (for its default database under `OS_HOME/data`) and the standalone host behind `os serve` and `os start` (for a `file:` or `wasm-sqlite://` database URL) make the database file's directory before the driver opens it. Both accepted any `EEXIST` answer while making a missing level. A symbolic link that points nowhere answers `EEXIST` to `mkdir`, so it was taken for a directory, and the boot went on until the driver could not open the file. `os dev` then exited 1 with `Plugin com.objectstack.auth failed to start - rollback complete: The database refused to run this query for object 'sys_organization'`, and `os serve` with `Plugin package-service failed to start - rollback complete: The database refused to run a raw statement`. Neither message named the file, the directory or the link. + + **What an operator sees now.** Both make the directory with the same bounded walk as the other `OS_HOME` paths, which accepts `EEXIST` only when a directory stands there. A dangling link as the directory is refused before the boot starts: `os dev` prints `The default database '…/data/objectstack.db' cannot be created: its directory '…/data' could not be made: EEXIST: …` and exits 1, and `os serve` prints `[StandaloneStack] SQLite database file '…' cannot be created: its directory '…' could not be made: EEXIST: …` and exits 1. Remove the link, or point it at a directory that exists. A dangling link higher up the path was already refused at once, with `ENOENT` at the level below the link; it is now refused with `EEXIST` at the link itself. + + Nothing else changes. A directory that can be made is still made, every missing level of it; a directory that exists, or a symbolic link to one, is used as it stands; and a path the operating system refuses for any other reason (`ENOENT`, `ENOTDIR`, a regular file holding the directory's name) is refused with the same code and text as before. +- 5a9a80b: fix(runtime): an OAuth MCP agent acts in the organization the user's consent bound, so the user's organization-scoped grants count for it + + Clause-②: no + + - **What was wrong.** On the `/mcp` door, `resolveExecutionContext` built a verified OAuth access token's principal with no organization. The agent's delegator (the user it acts for, ADR-0090 D10) was then resolved with no active organization and held only its global grants. Every grant an administrator assigns is scoped to an organization, so an ordinary user's agent was refused every object operation: `on-behalf-of principal may not 'find' … the delegator lacks that grant (ADR-0090 D10 intersection)`. + - **What changed.** The principal is resolved in the organization the consent bound: the `organizationId` that `IAuthService.verifyMcpAccessToken` returns from the token's `organization_id` claim. It is handed to the shared resolver where a session carries its active organization, so it is vetted on every request by the rule a browser session's organization gets (`vetOrganizationClaim`, `@objectstack/core`). The user's grants scoped to that organization count for the agent; grants scoped to the user's other organizations do not. It is never the union of the user's organizations, never the organization the user's browser session has selected now, and never a widened global grant. + - **A membership that is gone.** Under a walled tenancy posture (`isolated`, `group`), a token whose organization the user no longer belongs to resolves with no organization, so only global grants apply. Under `isolated`, a former member's agent gets an empty `200` read and a refused tenant write. The same holds for an organization the user never belonged to, whatever put it on the token. Under `single`, or with no `tenancy` service, the organization stands as claimed, exactly as a browser session's does there. + - **Unchanged.** A token with no `organization_id` (minted before consents bound one, or by a user with no active organization at consent) resolves with no organization, as before: global grants only, until the user re-confirms the consent once. A platform admin's global grant applies as before. The API-key and session tracks are untouched. +- 8eccf51: fix(runtime): on the standalone server, an anonymous request whose JSON body cannot be parsed gets the domain's `401`, not the transport's `400` + + Clause-②: no + + On the standalone server (`os dev`, `os serve`: `HonoServerPlugin` with the dispatcher plugin), the routes the dispatcher mounts read the request body before handing the request to `dispatch()`. The transport refuses a body it cannot parse when the body is read, so the refusal answered first: an anonymous `POST /api/v1/keys` with `{"name": "probe"` (the closing brace missing) got `400 INVALID_REQUEST`, while the same request with a well-formed body got the promised `401 UNAUTHENTICATED`. The anonymous answer depended on whether the body parsed. + + The mounts now hand the body on unread, and each domain reads it once its anonymous decision has admitted the caller. An anonymous caller gets the same answer whatever its body, on every mounted route that reads one, unscoped and `/environments/:environmentId`-scoped: + + - `401 UNAUTHENTICATED` on `POST /analytics/query` and `/analytics/sql`; `/keys` (every verb); `POST /notifications/read` and `/notifications/read/all`; `POST /automation`, `PUT /automation/:name` and the two `trigger` doors, `toggle`, `clone`, `resume`, `cancel` and `restore-suspension`; the three `POST /actions/...` shapes; the `/ai/*` routes; the `/packages` routes; `POST /security/_activation/:type/:name` and `POST /security/permission-sets/:name/clone`. + - `/mcp` answers its own `401`, or, ahead of it, its `404` when MCP is switched off and its `501` when no MCP service is installed. None of the three reads the body, so a signed-in caller gets the `404` or `501` too. + - `/mcp/skill` is public and reads no body: a `POST` or `DELETE` gets its own `405` + `Allow: GET`, signed in or not. + + Otherwise a signed-in caller with an unparseable body still gets the transport's `400 INVALID_REQUEST`: the body is read after the anonymous decision, not dropped. A `createHonoApp` host (`@objectstack/hono`) is unchanged; it already raised the refusal only when a domain read the body. +- 1bf5f6e: fix(runtime): `ctx.title()` in a hook body answers a title formula that reads through a lookup, as a read of the record shows it + + A junction object's composed title, `record.book.title + ' · ' + record.reader.name`, is shown by every read of the record, but a hook body's `ctx.title()` answered `null` for it, and `ctx.title('')` answered `null` for a related record whose own title reads through a lookup. + + - **`ctx.title()`** now reads the one hop the title formula reads, through the body's own `ctx.api` channel: one read per lookup the title reads through, with the caller's permissions, on the transaction the body has open. It follows the lookup's value in this write, so a `beforeInsert` names the payload's book and a `beforeUpdate` that changes `book` names the new book. A title over the record's own columns still costs no read. + - **`ctx.title('')`** returns the title its `findOne` served, the related record's own one-hop title included, instead of re-computing it without the hop. + - A related record the caller may not read leaves the title `null`, as the read of the same record shows no title to that caller. It is never a partial title and never the hidden name. A title that would need a second hop is `null`, as it is on a read. + + What to do. Nothing. A hook that composed a junction object's title by hand can call `ctx.title()` instead. +- af284b0: feat(spec): the remaining `getSession` readers read `AuthSessionApi`'s declared result, and none reads a top-level `activeOrganizationId` + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains three optional `user` members**, each declared for a named reader: `isPlatformAdmin` (`boolean`, the ADR-0068 D2 rung plugin-auth's `customSession` derives) and `role` (`string | null`, better-auth's `admin` plugin scalar), both read by `judgePlatformAdmin` in `@objectstack/plugin-auth`; and `name` (`string`), read by the runtime dispatcher's AI-route `req.user` resolver as the display name. Every member is optional, so every implementer and caller typechecks unchanged. + - **The readers read through the declared type instead of `any` or a cast** (types only, runtime behaviour unchanged): `judgePlatformAdmin` and the four platform-admin mounts in `@objectstack/plugin-auth` (better-auth's own session type now has to fit the declaration at those reads), the REST server's two session reads (`@objectstack/rest`), the dispatcher's posture read, `resolveSessionData` and both `HttpDispatcher` reads (`@objectstack/runtime`), the cloud-connection and install-local session reads (`@objectstack/cloud-connection`), the webhook redeliver door (`@objectstack/plugin-webhooks`), and the storage session bridge (`@objectstack/service-storage`). + - **Fix — the top-level `activeOrganizationId` fallback is removed** from the storage upload stamp (`@objectstack/service-storage`) and from install-local's `resolveActiveOrgId` (`@objectstack/cloud-connection`). The active organization is read from `session.activeOrganizationId` alone, where the contract declares it. No in-process producer answers a top-level `activeOrganizationId` (better-auth's read is `{ user, session }`), so for the shipped auth plugin nothing changes. An auth service that answered only the flattened shape now gets no organization: the upload is not stamped, and install-local reports no active organization. To fix it, answer the active organization on `session.activeOrganizationId`, as `AuthSessionApi` declares it. +- cbfa5e4: fix(runtime): on the standalone server, every verb on `/api/v1/actions/...` reaches the `actions` domain, so an anonymous caller is answered `401`; and an `Allow` header no longer lists verbs a route mounts only to refuse + + Clause-②: no + + On the standalone server (`os dev`, `os serve`: `HonoServerPlugin` with the dispatcher plugin), a request that matched no route under its verb was answered by the HTTP adapter: `405 METHOD_NOT_ALLOWED` with an `Allow` header built from the verbs registered on the path. Two answers were wrong. + + - **`/api/v1/actions/...` (all three path shapes, unscoped and `/environments/:environmentId`-scoped).** Only `POST` is mounted, so an anonymous `GET`, `HEAD`, `PUT`, `PATCH`, `DELETE` or `TRACE` was answered `405` + `Allow: POST` instead of the published `401 UNAUTHENTICATED`. Every such request now reaches the `actions` domain, as it does on a `createHonoApp` host: an anonymous caller gets `401 UNAUTHENTICATED` with no `Allow`, and a signed-in caller gets the domain's `405` + `Allow: POST`. An anonymous `OPTIONS` (only reachable with CORS off) gets the domain's `405` + `Allow: POST` on both hosts, because the shared anonymous decision lets a CORS preflight through. + - **`Allow` on `/api/v1/keys`, the three `/api/v1/notifications` faces and `/api/v1/mcp/skill`.** These paths mount verbs that exist only so the domain can refuse them. A verb that cannot be mounted (`OPTIONS` with CORS off, `TRACE`, an extension method), and `PUT`/`PATCH` on `/mcp/skill`, got the adapter's `405` with those refusal-only verbs listed as supported (`/keys`: `Allow: DELETE, GET, HEAD, PATCH, POST, PUT`). They now reach the domain too: `/keys` answers `401` anonymous and `405` + `Allow: POST` signed in, `/notifications` answers `401` anonymous and `404 ROUTE_NOT_FOUND` signed in, and `/mcp/skill` answers `405` + `Allow: GET`. + + Every request that was served is served as before, and no route is added: the dispatcher's existing fallback seam hands these requests to the domain. A transport that implements `setFallbackHandler` but not `resolveMountedRoute` keeps the adapter's answer, and the dispatcher says so once at boot, at `warn`. +- 9822b05: fix(hono): on a `createHonoApp` host a `HEAD` is answered as its `GET` (no body), as the standalone server answers it; `HEAD /api/v1/mcp/skill` no longer answers `405` with `Allow: GET` + + Clause-②: no + + `createHonoApp`'s `${prefix}/*` catch-all handed the dispatcher the request's raw verb. Hono answers a `HEAD` from the `GET` route, but the verb still read `HEAD`, and the dispatcher's domains decide by comparing it with `GET`, so a `HEAD` was not answered as its `GET` on this host. Measured: `HEAD /api/v1/mcp/skill` answered `405 METHOD_NOT_ALLOWED` with `Allow: GET`; `/health`, `/ready` and the scoped root `/environments/:id` answered `404 ROUTE_NOT_FOUND`; signed in, `/analytics/meta` answered `404` and `/ai/agents` `501`; anonymous, the public `/share-links/:token/resolve` answered `401` where `GET` answers `501`. With an auth service, `HEAD /auth/config` answered `404` (it was forwarded to better-auth instead of answered as the `GET`). + + - **`@objectstack/hono`.** The catch-all now dispatches a `HEAD` as `GET`, and the `/auth/config` branch answers a `HEAD` as its `GET`. Every dispatcher route answers `HEAD` with its `GET` answer's status and headers, and Hono drops the body. A path that serves no `GET` keeps its `GET` answer for `HEAD` (its `405` and `Allow`, or `401` to an anonymous caller). The standalone server (`HonoServerPlugin` with the dispatcher plugin) already answered `HEAD` this way. Only the verb changes: a domain that forwards the request (the `/mcp` transport, the `/approvals/act` page) is still handed the client's `HEAD` request. + - **`@objectstack/runtime`.** The `/approvals/act` domain decides whether a request is a `HEAD` from the forwarded request's own method, not from the dispatcher's verb. So a `HEAD` still reaches the approvals member as a `GET` and is answered with no body. +- 889bf12: fix(runtime): an anonymous caller of `/api/v1/mcp` is answered `401 UNAUTHENTICATED` whatever `OS_MCP_SERVER_ENABLED` or the `mcp` service says + + Clause-②: no + + ADR-0138 D2 takes the anonymous decision once per domain, as the domain's first statement. The `/mcp` domain checked the `OS_MCP_SERVER_ENABLED` switch and the `mcp` service before it, so an anonymous caller was answered `404 RESOURCE_NOT_FOUND` with the switch off and `501 NOT_IMPLEMENTED` with no MCP service installed, and learned whether the deployment runs MCP before it was refused. Measured on both hosts, the standalone server (`HonoServerPlugin` with the dispatcher plugin) and a `createHonoApp` host. + + - An anonymous request on `/api/v1/mcp` (any verb) is now refused `401 UNAUTHENTICATED` first, in every composition. Only the order of the refusals moves: no request that was refused before is accepted. + - A signed-in caller, or a system principal, keeps the switch's `404` and the service's `501`, in that order. + - The `401` advertises RFC 9728 resource metadata (`WWW-Authenticate: Bearer … resource_metadata="…"`) only where the resource is served: the switch on, an `mcp` service that can take the request, and an auth service that answers a metadata URL. With the switch off or no service the `401` is the plain API-key one, so it never points a client at a protected resource the deployment does not serve. +- 7a8733f: fix(runtime): a request to a declarative `apis:` endpoint path gets its `404` or `401` whatever its JSON body, not the transport's `400` + + Clause-②: no + + Only the order of refusals moves: the `404` and the `401` now answer ahead of the body's `400`. No request that was refused before is accepted, and no export changes. + + On the standalone server (`os dev`, `os serve`: `HonoServerPlugin` with the dispatcher plugin), the fallback that serves metadata-declared `apis:` endpoints (`/api/v1/apps/NAMESPACE/...` under the default prefix) read the request body before it matched an endpoint or applied `authRequired`. The transport refuses a body it cannot parse when the body is read, so that refusal answered first: + + - a `POST /api/v1/apps/ns/x` that no declaration owns, with `{"params": {"days": 30}` (the closing brace missing), got `400 INVALID_REQUEST`, while the same request with a well-formed body got `404 ENDPOINT_NOT_FOUND`, signed in or not; + - an anonymous `POST` to an `authRequired: true` endpoint with that body got `400 INVALID_REQUEST`, while a well-formed body got `401 UNAUTHENTICATED`. + + The fallback now hands the endpoint step the body unread, and the step reads it once the endpoint is matched and its policies (`rateLimit`, `authRequired`) have admitted the caller, just before `inputMapping`. A path no declaration owns answers `404`, and an anonymous caller of an `authRequired: true` endpoint answers `401`, whatever the body. A caller the endpoint admits still gets the transport's `400 INVALID_REQUEST` for an unparseable body, and a readable body reaches the target as before. +- 51ad8e4: Clause-②: yes (widening) + + fix(cli,runtime): the 17 → 18 organization-ownership upgrade of a single-posture database that holds no organization leaves no row without one, the boot the ceremony prescribes writes nothing before it is refused, and a ceremony that did not verify is never served (ADR-0131 D3, D10) + + A 17.x single-posture install whose seed ran before any organization existed holds rows of organization-owned objects with `organization_id` NULL and an empty `sys_organization`. Upgrading it took this path: the ceremony's attribution checkpointed itself done at zero rows; the boot its rewrite step asks for created the Default Organization and let the inline seed stamp some rows before it refused; the second `--apply` did not run attribution again, dropped the columns and recorded discrepancies with `verified_at` NULL; and `os start` then served that database. + + - **The ceremony creates the Default Organization (`@objectstack/cli`).** Under the `single` posture, on a database that holds no organization and rows no anchor names, `os migrate organization-ownership` plans those rows as the Default Organization's and says so in a plan warning (`defaultOrganizationCreatedByApply: true` in the plan file). `--apply` creates it before its first write, through plugin-auth's boot invariant (the row a single-posture boot creates), and gives it every such row. A run that cannot create it — a boot without the auth family that declares `sys_organization` — blocks at attribution, naming why, instead of attributing nothing and certifying. Nothing is created under a walled posture, or when the database holds organizations. + - **The ceremony names the Default Organization as the boot does (`@objectstack/cli`).** Under `single` it is the organization with `slug = 'default'`, else the sole organization whatever its slug — the boot invariant's and the tenancy service's definition (ADR-0093 D3). Before, the plan required `slug = 'default'`, so a database whose only organization had another slug kept its unanchored rows NULL through the ceremony while v18 derives every owner from that organization. Several organizations, none of them `default`, still name none, and their rows are reported. In the plan file (the command's own output, not a published schema) `defaultOrganization.slug` is now that organization's own slug, no longer always `'default'`. + - **A checkpointed attribution runs again (`@objectstack/cli`)** whenever the plan derives an owner for rows still NULL: a row the operator made attributable since, or one an earlier run found no Default Organization for. It writes only where `organization_id` is NULL, so no owner already written is derived again. A database left by an earlier release's ceremony in that state is completed by re-running `--apply`. + - **The boot guard judges before any plugin writes (`@objectstack/runtime`, `@objectstack/plugin-auth`).** `OrganizationOwnershipBootGuardPlugin` judges in its own `start()`, after the engine's schema sync (the column the rewrite step waits for is still added) and before every plugin that writes rows at start: `AppPlugin` (the inline seed), `AuthPlugin` and the identity-objects plugin (the Default Organization) name it (`com.objectstack.organization-ownership-boot-guard`) in `optionalDependencies`. A refused boot leaves every row as it found it. + - **A ceremony that did not verify is refused (`@objectstack/runtime`).** With no table left in the v17 shape, a boot whose `adr-0131-organization-ownership` marker exists and is not verified — the post-check found rows the ceremony has not carried — is refused with `ORGANIZATION_OWNERSHIP_MIGRATION_REQUIRED`, naming the re-run. A database with no marker (a fresh one) boots as before. + - **The refusal now arrives as a `cause` (`@objectstack/runtime`).** A failed `start()` is reported by the kernel as its own error ("Plugin … failed to start"), with the plugin's error as its `cause`. A host that composes the guard and inspects the boot error asks `isOrganizationOwnershipBootRefusal` of each `cause` down the chain: asked of the kernel's own error it now answers `false`. `assertOrganizationOwnershipMigrated(engine)` still throws the refusal itself. `os serve` and `os dev` read it from the chain. + - **One new export (`@objectstack/plugin-auth`, `minor`).** `ensureDefaultOrganizationExists(ql, options?)` — the Default Organization's boot invariant, find-or-create under `single` — for the ceremony, so the organization it creates is the row a single-posture boot creates, from one producer. A host composing a kernel still never calls it: `AuthPlugin` and the identity-objects plugin keep the invariant on every boot. No other package entry changes. + + Unchanged: the ceremony's steps, fates and refusals otherwise; a walled posture's unattributable rows (reported, never assigned); the boot of a fresh or verified database; and every plugin's order on a kernel that does not compose the guard. Repairing a database whose organization-scoped seed load already inserted twins of NULL-organization rows is an operator's data repair. +- c4526a7: fix(runtime): a hook's inline `handler` function is handed the same `ctx.title(field?)` a hook `body` gets, so a source boot (`@objectstack/verify`'s `bootStack(config)`, `os serve` over an `objectstack.config.ts`) answers it as `objectstack dev` does (#24289) + + Clause-②: no + + A source boot runs a hook's inline `handler` in-process, and the accessor existed only in the sandbox a `body` runs in. So a hook calling `await ctx.title()` or `await ctx.title('account_id')` (a lookup column) answered on `objectstack dev` and an artifact boot, and under `bootStack(config)` refused every write with `TypeError: ctx.title is not a function`. `bindAppArtifactHandlers` now hands each inline handler function a view of the engine's hook context that answers `title` through the same accessor the sandbox installs: the same two forms, the same served formula title and related-record read on the context's own `ctx.api`, and `null`, never the record's id, when there is no title. Every other read and every write goes to the engine's context unchanged, and that context gains no key. A `handler` that names a function (the deprecated string form) is not covered by this change. +- 6868514: Clause-②: no + + Ten user-facing strings the runtime's HTTP doors answer no longer call the system the caller is in + "this environment". The permission-set clone door's success notice (`PERMISSION_SET_CLONE_NOTICE`, the + `notice` beside the copy) now says the copy is "a new permission set authored in this system", the + wording the console's clone hint uses. The other nine drop the noun: `/mcp` and `/mcp/skill` on a + deployment that opts out answer `404` "MCP server is not enabled"; a share-link request with no + share-link service answers `501` "Sharing is not configured"; and the webhook-redelivery, approval + action-page and inbound automation-hook doors answer `501` "… no webhooks service is registered." (or + approvals, or trigger-api) when the service is missing, and "… the registered webhooks service does not + implement handleRedeliver." (or `handleActionPage`, or `handleInboundHook`) when it lacks its member. + Only the human-readable text moves: every HTTP status, error code and response shape is what it was. +- Updated dependencies [b2e7870] +- Updated dependencies [6a49efb] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [646303e] +- Updated dependencies [e9bc60b] +- Updated dependencies [52ff308] +- Updated dependencies [d1f082d] +- Updated dependencies [f5ef054] +- Updated dependencies [0d34066] +- Updated dependencies [03ac4ec] +- Updated dependencies [b0a1df0] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [f3174ef] +- Updated dependencies [e46ce7b] +- Updated dependencies [a06f0d4] +- Updated dependencies [a06f0d4] +- Updated dependencies [9975c26] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [aaaeeac] +- Updated dependencies [8a88901] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8259ea0] +- Updated dependencies [9040945] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [2bcd406] +- Updated dependencies [2bcd406] +- Updated dependencies [58a2faa] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [83dfbc0] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [ec749fb] +- Updated dependencies [ec749fb] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [41c7405] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [d65826f] +- Updated dependencies [741aa85] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [44a5011] +- Updated dependencies [1827571] +- Updated dependencies [f573976] +- Updated dependencies [e22defb] + - @objectstack/core@18.0.0-next.4 + - @objectstack/plugin-security@18.0.0-next.4 + - @objectstack/plugin-auth@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/rest@18.0.0-next.4 + - @objectstack/metadata-protocol@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/driver-memory@18.0.0-next.4 + - @objectstack/driver-sql@18.0.0-next.4 + - @objectstack/driver-sqlite-wasm@18.0.0-next.4 + - @objectstack/driver-turso@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/service-cluster@18.0.0-next.4 + - @objectstack/service-datasource@18.0.0-next.4 + - @objectstack/service-i18n@18.0.0-next.4 + - @objectstack/observability@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 76be9c7cfdc..108fa9328b1 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/runtime", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack Core Runtime & Query Engine", "type": "module", diff --git a/packages/sdui-parser/CHANGELOG.md b/packages/sdui-parser/CHANGELOG.md index d82ff72e87a..644f0eeeb86 100644 --- a/packages/sdui-parser/CHANGELOG.md +++ b/packages/sdui-parser/CHANGELOG.md @@ -1,5 +1,9 @@ # @objectstack/sdui-parser +## 18.0.0-next.4 + +No changes in this release. + ## 18.0.0-next.3 No changes in this release. diff --git a/packages/sdui-parser/package.json b/packages/sdui-parser/package.json index b42d223d8ed..c984309121c 100644 --- a/packages/sdui-parser/package.json +++ b/packages/sdui-parser/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/sdui-parser", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "ObjectStack constrained JSX-source → SDUI SchemaNode tree compiler (parse, never execute). Isomorphic, zero React. ADR-0080.", "main": "dist/index.js", diff --git a/packages/services/service-analytics/CHANGELOG.md b/packages/services/service-analytics/CHANGELOG.md index 8da0a8a73e2..0bb4d283bd0 100644 --- a/packages/services/service-analytics/CHANGELOG.md +++ b/packages/services/service-analytics/CHANGELOG.md @@ -1,5 +1,52 @@ # Changelog — @objectstack/service-analytics +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-analytics/package.json b/packages/services/service-analytics/package.json index c5ad7aa3d0a..db34b3f621a 100644 --- a/packages/services/service-analytics/package.json +++ b/packages/services/service-analytics/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-analytics", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Analytics Service for ObjectStack — implements IAnalyticsService with multi-driver strategy pattern (NativeSQL, ObjectQL, InMemory)", "type": "module", diff --git a/packages/services/service-automation/CHANGELOG.md b/packages/services/service-automation/CHANGELOG.md index 06355fd03fe..406f524d57c 100644 --- a/packages/services/service-automation/CHANGELOG.md +++ b/packages/services/service-automation/CHANGELOG.md @@ -1,5 +1,73 @@ # @objectstack/service-automation +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [f3174ef] +- Updated dependencies [e46ce7b] +- Updated dependencies [a06f0d4] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [ec749fb] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [41c7405] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [1827571] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/metadata-protocol@18.0.0-next.4 + - @objectstack/formula@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/services/service-automation/package.json b/packages/services/service-automation/package.json index b9b71cfa6d0..617e50d19e0 100644 --- a/packages/services/service-automation/package.json +++ b/packages/services/service-automation/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-automation", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Automation Service for ObjectStack — implements IAutomationService with plugin-based DAG flow execution engine", "type": "module", diff --git a/packages/services/service-cache/CHANGELOG.md b/packages/services/service-cache/CHANGELOG.md index 0f401f85360..c934694b9e3 100644 --- a/packages/services/service-cache/CHANGELOG.md +++ b/packages/services/service-cache/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/service-cache +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/observability@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-cache/package.json b/packages/services/service-cache/package.json index 899431b0624..5ddc1bda9f7 100644 --- a/packages/services/service-cache/package.json +++ b/packages/services/service-cache/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-cache", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Cache Service for ObjectStack — implements ICacheService with in-memory and Redis adapters", "type": "module", diff --git a/packages/services/service-cluster-redis/CHANGELOG.md b/packages/services/service-cluster-redis/CHANGELOG.md index e64635045bb..701a934d8fc 100644 --- a/packages/services/service-cluster-redis/CHANGELOG.md +++ b/packages/services/service-cluster-redis/CHANGELOG.md @@ -1,5 +1,47 @@ # @objectstack/service-cluster-redis +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + - @objectstack/service-cluster@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-cluster-redis/package.json b/packages/services/service-cluster-redis/package.json index a137648e35a..e2ca33e3230 100644 --- a/packages/services/service-cluster-redis/package.json +++ b/packages/services/service-cluster-redis/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-cluster-redis", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Redis cluster driver for ObjectStack — implements IPubSub/ILock/IKV/ICounter against Redis using ioredis.", "type": "module", diff --git a/packages/services/service-cluster/CHANGELOG.md b/packages/services/service-cluster/CHANGELOG.md index 301f4d5a4a2..391e1f1716e 100644 --- a/packages/services/service-cluster/CHANGELOG.md +++ b/packages/services/service-cluster/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/service-cluster +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-cluster/package.json b/packages/services/service-cluster/package.json index 6b25cd49ba1..6098f2f1c2a 100644 --- a/packages/services/service-cluster/package.json +++ b/packages/services/service-cluster/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-cluster", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Cluster Service for ObjectStack — pluggable PubSub/Lock/KV/Counter primitives. Memory driver included; postgres/redis drivers ship separately.", "type": "module", diff --git a/packages/services/service-datasource/CHANGELOG.md b/packages/services/service-datasource/CHANGELOG.md index 540232feb44..b2a589c8ab1 100644 --- a/packages/services/service-datasource/CHANGELOG.md +++ b/packages/services/service-datasource/CHANGELOG.md @@ -1,5 +1,58 @@ # @objectstack/service-external-datasource +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/driver-memory@18.0.0-next.4 + - @objectstack/driver-mongodb@18.0.0-next.4 + - @objectstack/driver-sql@18.0.0-next.4 + - @objectstack/driver-sqlite-wasm@18.0.0-next.4 + - @objectstack/driver-turso@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-datasource/package.json b/packages/services/service-datasource/package.json index 9cccc890327..cb37565aaa5 100644 --- a/packages/services/service-datasource/package.json +++ b/packages/services/service-datasource/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-datasource", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "The datasource service (ADR-0015): external-table federation (introspect/draft/import/validate) + runtime UI datasource lifecycle (list/test/create/update/remove + REST routes). Open-source mechanism; the tier line falls on which ICryptoProvider / driver factory a host injects.", "type": "module", diff --git a/packages/services/service-i18n/CHANGELOG.md b/packages/services/service-i18n/CHANGELOG.md index b03a6a3168c..e7fac7eb119 100644 --- a/packages/services/service-i18n/CHANGELOG.md +++ b/packages/services/service-i18n/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/service-i18n +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-i18n/package.json b/packages/services/service-i18n/package.json index 52fb2e48a24..4d0dad9fb4a 100644 --- a/packages/services/service-i18n/package.json +++ b/packages/services/service-i18n/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-i18n", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "I18n Service for ObjectStack — implements II18nService with file-based locale loading", "type": "module", diff --git a/packages/services/service-job/CHANGELOG.md b/packages/services/service-job/CHANGELOG.md index 0c156a9d799..48ffd02772f 100644 --- a/packages/services/service-job/CHANGELOG.md +++ b/packages/services/service-job/CHANGELOG.md @@ -1,5 +1,59 @@ # @objectstack/service-job +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-job/package.json b/packages/services/service-job/package.json index 95d08c5257e..febc8740128 100644 --- a/packages/services/service-job/package.json +++ b/packages/services/service-job/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-job", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Job Service for ObjectStack — implements IJobService with setInterval and cron scheduling", "type": "module", diff --git a/packages/services/service-knowledge/CHANGELOG.md b/packages/services/service-knowledge/CHANGELOG.md index a99e4b5947c..5c332943795 100644 --- a/packages/services/service-knowledge/CHANGELOG.md +++ b/packages/services/service-knowledge/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/service-knowledge +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-knowledge/package.json b/packages/services/service-knowledge/package.json index 1beb6adfe5c..f57ac8916bd 100644 --- a/packages/services/service-knowledge/package.json +++ b/packages/services/service-knowledge/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-knowledge", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Knowledge Service for ObjectStack — orchestrator implementing IKnowledgeService over pluggable IKnowledgeAdapter backends (RAGFlow, LlamaIndex, Dify, in-memory).", "type": "module", diff --git a/packages/services/service-mailbox/CHANGELOG.md b/packages/services/service-mailbox/CHANGELOG.md index 1ad8521e061..f0ef128b08f 100644 --- a/packages/services/service-mailbox/CHANGELOG.md +++ b/packages/services/service-mailbox/CHANGELOG.md @@ -1,5 +1,61 @@ # @objectstack/service-mailbox +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + - @objectstack/connector-imap@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-mailbox/package.json b/packages/services/service-mailbox/package.json index cc2deda50e2..7ab533132b1 100644 --- a/packages/services/service-mailbox/package.json +++ b/packages/services/service-mailbox/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-mailbox", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Mailbox Service for ObjectStack \u2014 inbound mail: the mirrored message (sys_mail_message), its record associations and their read gates.", "type": "module", diff --git a/packages/services/service-messaging/CHANGELOG.md b/packages/services/service-messaging/CHANGELOG.md index 77d50d43013..f2e025a114c 100644 --- a/packages/services/service-messaging/CHANGELOG.md +++ b/packages/services/service-messaging/CHANGELOG.md @@ -1,5 +1,60 @@ # @objectstack/service-messaging +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-messaging/package.json b/packages/services/service-messaging/package.json index 3dd70801cf0..0ec4e5838e4 100644 --- a/packages/services/service-messaging/package.json +++ b/packages/services/service-messaging/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-messaging", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Messaging Service for ObjectStack — outbound notification dispatch (ADR-0012). Ships the MessagingChannel registry, emit() fan-out, and the always-on inbox channel; other channels (email/webhook/push/IM) plug in.", "type": "module", diff --git a/packages/services/service-package/CHANGELOG.md b/packages/services/service-package/CHANGELOG.md index 64f5c427c79..eae2df00071 100644 --- a/packages/services/service-package/CHANGELOG.md +++ b/packages/services/service-package/CHANGELOG.md @@ -1,5 +1,52 @@ # @objectstack/service-package +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/services/service-package/package.json b/packages/services/service-package/package.json index 7ffc5813ad2..2270255b947 100644 --- a/packages/services/service-package/package.json +++ b/packages/services/service-package/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-package", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Package management service for ObjectStack — publish, install, and manage packages", "type": "module", diff --git a/packages/services/service-print/CHANGELOG.md b/packages/services/service-print/CHANGELOG.md index a2ed3bf4852..b37f21f227f 100644 --- a/packages/services/service-print/CHANGELOG.md +++ b/packages/services/service-print/CHANGELOG.md @@ -1,5 +1,84 @@ # @objectstack/service-print +## 18.0.0-next.4 + +### Minor Changes + +- d9a749b: Clause-②: no + + feat(service-print): print a record from the console — `PrintServicePlugin` mounts `POST /api/v1/print/records`, which prints the record as the signed-in caller and answers with the file it attached + + - **What it adds.** One HTTP route on the kernel's HTTP server, mounted at `kernel:ready` whenever the plugin is composed. Its JSON body is the in-process request, `{ app, object, recordId, page, filename? }`. The route resolves the caller with the platform's one authorization resolver, hands that caller's execution context and the request to `IPrintService.printRecordToFile`, and answers `201` with `{ fileId, attachmentId, size, filename }`. The service attaches the PDF once, as the caller; the route never attaches anything itself. + - **How an app uses it.** Declare a `type: 'api'` action whose `target` is `/api/v1/print/records`, with `recordIdParam: 'recordId'` and the rest of the request in `bodyExtra` (`app`, `object`, `page`). The console sends the clicked record's id and those fields, and shows a refusal's message as the action's error. + - **What the route refuses, before the service is asked:** an anonymous request (`UNAUTHENTICATED` / 401); a sign-in held by an authentication policy (`403` with the policy's own code); a request from an impersonation session (`PERMISSION_DENIED` / 403 — a print is archived in the session user's name, so it is never made for them by someone signed in as them); a query parameter, or a body field outside the request (`VALIDATION_ERROR` / 400). A permission store that cannot be read answers its own `SERVICE_UNAVAILABLE` / 503. + - **What it relays.** Every refusal the service declares keeps its `code` and `status` (`SERVICE_UNAVAILABLE` / 503 when no print engine is configured, `VALIDATION_ERROR` / 400 for a page without `print`, `RECORD_NOT_FOUND` / 404 for a record the caller cannot read, the files and attach refusals, `TIMEOUT` / 504, the archive's own). A 4xx keeps the service's message. A 5xx answers with the route's own sentence, and the service's text goes to the server fault log. + - **Stated limits.** The route writes no audit row of its own: a print's session sign-in and sign-out are recorded as any sign-in is, and `sys_file.owner_id` and `sys_attachment.uploaded_by` name the caller. An impersonation session is recognised by the mark the auth service writes on it (`session.impersonatedBy`, stored as `sys_session.impersonated_by`); an auth provider that marks impersonation some other way is not recognised. + - **Unchanged:** the in-process `print` service and its refusals, every export, every plugin option and environment variable, and the error-code vocabulary. A kernel with no HTTP server mounts no route and says so once at `kernel:ready`. + +### Patch Changes + +- 1fee13d: **Shipped in 18.0.0-next.3**, whose CHANGELOG missed this entry; listed here one prerelease late. + + Clause-②: no + + fix(service-print): the local placement binds its browser server's control socket to `127.0.0.1` itself, instead of relying on `playwright-core`'s default host + + - **What changed.** With `OS_PRINT_CHROMIUM_PATH` (option `chromiumPath`), each render and each boot probe starts a Chromium browser server and connects to it. That server's control socket is now opened with `host: '127.0.0.1'`. Before, no host was passed, so the bind followed `playwright-core`'s default (`localhost`, loopback only in the pinned 1.63.0). Whoever reaches that socket and knows its path drives a browser signed in as the requesting user. How far it reaches is now set by this package, and a `playwright-core` upgrade cannot widen it without a diff here. + - **What it means for a deployment.** Nothing changes on a host whose `localhost` is `127.0.0.1`, which is the usual case. On a host with no IPv4 loopback address, the local placement cannot open the socket. Its boot probe logs `Chromium could not be reached at the local executable …: listen EADDRNOTAVAIL …` and printing stays off. The socket is never bound to another address instead. On such a host, use the remote placement (`OS_PRINT_BROWSER_ENDPOINT`). + - **Unchanged:** the remote placement, the port (still random), every option and environment variable, and the exports. +- b036ac5: feat(spec): `AuthSessionApi.getSession`'s result declares the fields its in-process readers read + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains five optional members**, each declared for a named reader and typed as that reader takes it: `user.email` and `session.id` / `session.token` (read by `resolveAuthzContext`'s session arm in `@objectstack/core`), `user.authGate` as `unknown` (the ADR-0069 posture, which `normalizeAuthGate` validates against the closed posture vocabulary), and `session.impersonatedBy` as `string | null` (read by the print door's `impersonatorOf` in `@objectstack/service-print`). Before this, those reads went through `any` or a cast, so a field better-auth dropped or renamed compiled and read `undefined`. For `impersonatedBy`, that would have silently turned off the impersonation refusal. Every member is optional, so every implementer and caller typechecks unchanged. + - **`@objectstack/core`**: `resolveAuthzContext`'s session arm reads the session through the declared type instead of `any`. `ResolveAuthzInput.getSession`'s own type is unchanged. Runtime behaviour is unchanged. + - **`@objectstack/service-print`**: `impersonatorOf` reads `session.impersonatedBy` through the declared type instead of a cast. Runtime behaviour is unchanged. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [8d3dd6f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [d8e11b5] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/service-storage@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/services/service-print/package.json b/packages/services/service-print/package.json index 8a6ec8f1a2e..89fdfaf2905 100644 --- a/packages/services/service-print/package.json +++ b/packages/services/service-print/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-print", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Print Service for ObjectStack — renders a record's print page in headless Chromium under the requesting user's own principal and archives the PDF as a file attached to the record.", "type": "module", diff --git a/packages/services/service-queue/CHANGELOG.md b/packages/services/service-queue/CHANGELOG.md index 968038c0650..b0f927cda0e 100644 --- a/packages/services/service-queue/CHANGELOG.md +++ b/packages/services/service-queue/CHANGELOG.md @@ -1,5 +1,59 @@ # @objectstack/service-queue +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-queue/package.json b/packages/services/service-queue/package.json index 6677eb54473..b383760a724 100644 --- a/packages/services/service-queue/package.json +++ b/packages/services/service-queue/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-queue", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Queue Service for ObjectStack — implements IQueueService with in-memory and durable DB-backed (sys_job_queue) adapters", "type": "module", diff --git a/packages/services/service-realtime/CHANGELOG.md b/packages/services/service-realtime/CHANGELOG.md index 0b3372602e3..5a8ea2f5f1a 100644 --- a/packages/services/service-realtime/CHANGELOG.md +++ b/packages/services/service-realtime/CHANGELOG.md @@ -1,5 +1,59 @@ # @objectstack/service-realtime +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-realtime/package.json b/packages/services/service-realtime/package.json index abf54b2f2c8..dd27e8ea742 100644 --- a/packages/services/service-realtime/package.json +++ b/packages/services/service-realtime/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-realtime", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Realtime Service for ObjectStack — implements IRealtimeService with WebSocket and in-memory pub/sub", "type": "module", diff --git a/packages/services/service-settings/CHANGELOG.md b/packages/services/service-settings/CHANGELOG.md index 862c2d45576..f57aed428df 100644 --- a/packages/services/service-settings/CHANGELOG.md +++ b/packages/services/service-settings/CHANGELOG.md @@ -1,5 +1,112 @@ # @objectstack/service-settings +## 18.0.0-next.4 + +### Minor Changes + +- d65826f: fix: framework writes on tenant-column objects name the organization they can derive (ADR-0131 D9) + + Clause-②: yes (widening) + + A write on a tenant-column object with no organization is derived under `single` and refused under a wall (ADR-0131 D9). Before the engine stops letting such writes through, each of these producers now names the organization it can derive itself. Under `single` that is the Default Organization, which is the owner the engine would derive anyway, so no reader changes. Under a wall the row names an organization only where the producer has one. The rows it cannot attribute are written exactly as before. + + - **`@objectstack/plugin-auth`.** The self-registration grant (`sys_user_permission_set`) and the auth mails about a user (verification, password reset, account invitation, magic link, change-email notice → `sys_email`) read one derivable organization: the `tenancy` service's `defaultOrgId()`. Under `single` that is the Default Organization. Under a wall it is none, because the registrant belongs to no organization yet. The verification and reset mails used to persist with no organization even under `single`. + - **`@objectstack/plugin-audit`.** An activity row (`sys_activity`) about a record with no organization of its own (a `sys_user`, a platform setting), written with no session organization (a sign-up), takes the Default Organization under `single`. The record's own organization and then the session's still win. The ledger's `tenant_id` attribution is unchanged. New optional `AuditWriterOptions.getDefaultOrganizationId()`; `AuditPlugin` wires it to the `tenancy` service. + - **`@objectstack/service-settings`.** The `sys_secret` ciphertext and the `sys_setting_audit` history row of a `tenant` or `user` value carry that value's organization, which is the same owner as its `sys_setting` row. A `global` value's rows name none. New optional `organization_id` on `SettingsSecretStore.insert`'s row and `organizationId` on `SettingsAuditWriter.write`'s entry; the built-in writers persist them. A failed `sys_setting_audit` write is now reported at `error`, not `warn`: the settings write succeeds while its history row is lost. The first report names the consequence and the fix. `buildSettingAuditWriter`'s optional logger argument keeps every sink it accepted (none, `{}`, or one with an optional `warn`) and additionally takes a sink that carries `error`. It reports through `error` when the sink has it, else through `warn`. + - **`@objectstack/verify`.** The RLS probe and position personas' grants (`sys_user_permission_set`, `sys_user_position`) belong to the organization the persona is a member of (the Default Organization under `single`). They used to be written with an explicit `organization_id: null`. A persona that belongs to no organization, or to several, names none. + +### Patch Changes + +- 0c3db0f: The settings routes' identity door resolves through `@objectstack/core`'s `resolveRequestExecutionContext`, the one request-to-`ExecutionContext` composition + + Clause-②: no + + - `SettingsServicePlugin` resolves the caller of every `/api/settings` route by calling `resolveRequestExecutionContext` with this door's differences as named parameters: `anonymous: 'fail-closed'`, `authGate: 'resolved'`, `credentials: 'api-key-and-session'`, `sessionApi: 'optional'`, `session: 'request'`, its own way to reach the `tenancy` service, `localization: 'withhold'`, no request locale, `accessToken: 'withhold'`, and no OAuth provenance or admission channel. The engine and auth-service lookups, the header normalisation, the tenancy wiring, the projection to `SettingsContext` and the fail-closed `catch` stay at the door. + - The answers are the ones the door gave before: an anonymous caller, an unrecognised cookie, a failed session read and a refused API key get `401 UNAUTHENTICATED`; a session or an API key owner the authentication policy holds gets `403` with its code and message; an unreadable permission store or tenancy service gets `503 SERVICE_UNAVAILABLE`; and an API key its own lifecycle refuses gets its `403` and code. A one-off probe of 29 fault wirings through the listing route read the same answer on both sides of the change. + - Nothing to do for a consumer: no export, route, configuration key or response shape changed. +- 04fd88a: Clause-②: no + + fix(service-settings): the Setup settings pages name the organization or the environment where they said "workspace" + + - **What changed.** The built-in settings manifests and their en, zh-CN, ja-JP and es-ES bundles no longer say "workspace" (工作区, ワークスペース, espacio de trabajo). Each string now uses the noun for what the setting belongs to: + - **Organization** (组织 / 組織 / organización): the Branding name field, now **Organization name**, and the Branding description; the Company legal-name help; the Feature Flags description. These settings are stored once per organization. + - **Environment** (环境 / 環境 / entorno): the Provider section of Mail and of SMS. These settings are stored once for the running instance. + - Authentication, New user membership: the help now names the explicit act as **creating an organization**. + - **Unchanged:** every setting key, including `branding.workspace_name` and its `OS_BRANDING_WORKSPACE_NAME` override; stored values; the Settings hub `category` values; the keys and shape of the translation bundles; and the exports. Only the text a reader sees changed. +- 44a5011: Clause-②: no + + Refusals and Setup help text that a user reads inside the running system no longer call that system + "this environment". They say "this system", or they drop the noun. + + - `@objectstack/plugin-auth`: the two self-registration refusals drop the noun ("Self-registration is + closed (audience posture invite_only).", "Self-registration is limited to approved email domains, …"). + So do the OAuth self-service `503` ("OIDC provider is not enabled") and the SSO `400` + `DOMAIN_VERIFICATION_DISABLED` ("Domain verification is not enabled (set + OS_SSO_DOMAIN_VERIFICATION)."). The last-administrator guard's refusals and its two warning lines say + "this system", including "nobody able to administer the system". The OIDC authorize-gate refusal + (`403 ENV_ACCESS_DENIED`) keeps "this environment". Only the cloud control plane installs that gate, + and there the noun names the environment a cloud user is signing in to. + - `@objectstack/plugin-security`: the diagnostic `message` of the packaged-permission-set lock now reads + "… is declared by package '…' and is locked — editing it here would …". The provenance-unknown + refusal says "this system could not determine …". Both `userMessage` texts are unchanged. + - `@objectstack/service-settings`: the Mail and SMS provider groups read "Choose how this system sends + outbound email." and "… outbound SMS.". The Authentication audience group reads "Who may become a user + of this system's apps.". The zh-CN, ja-JP and es-ES bundles change only the noun, to 本系统, + このシステム and este sistema. + + Only the human-readable text moves. Every HTTP status, error code, settings key and response shape + stays as it was. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-settings/package.json b/packages/services/service-settings/package.json index 1caa2682e75..c84135dc994 100644 --- a/packages/services/service-settings/package.json +++ b/packages/services/service-settings/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-settings", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Settings service for ObjectStack — manifest registry + K/V resolver (OS_* env > Tenant > User > Default) + REST routes. See ADR-0007.", "type": "module", diff --git a/packages/services/service-sms/CHANGELOG.md b/packages/services/service-sms/CHANGELOG.md index 1a7a247a2e2..b382765761a 100644 --- a/packages/services/service-sms/CHANGELOG.md +++ b/packages/services/service-sms/CHANGELOG.md @@ -1,5 +1,62 @@ # @objectstack/service-sms +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [03ac4ec] +- Updated dependencies [b0a1df0] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [8a88901] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [9040945] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [83dfbc0] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [51ad8e4] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [d65826f] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [44a5011] +- Updated dependencies [f573976] +- Updated dependencies [e22defb] + - @objectstack/core@18.0.0-next.4 + - @objectstack/plugin-auth@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/services/service-sms/package.json b/packages/services/service-sms/package.json index 9b1c6e05fa9..6717a5553c4 100644 --- a/packages/services/service-sms/package.json +++ b/packages/services/service-sms/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-sms", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "SMS service for ObjectStack — ISmsService + transport-pluggable outbound delivery (Aliyun / Twilio / log).", "main": "dist/index.js", diff --git a/packages/services/service-storage/CHANGELOG.md b/packages/services/service-storage/CHANGELOG.md index 2c9faa2d31a..17301b16c14 100644 --- a/packages/services/service-storage/CHANGELOG.md +++ b/packages/services/service-storage/CHANGELOG.md @@ -1,5 +1,93 @@ # @objectstack/service-storage +## 18.0.0-next.4 + +### Minor Changes + +- d8e11b5: fix(service-storage)!: the storage family gate and the route judge the same credential + + Clause-②: no (narrowing) — a session the ADR-0069 policy holds is refused on every storage route whatever else the request carries; no published schema, export or option moves. + + + + **BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes. + + ADR-0069 holds a signed-in user out of every protected resource while their password has expired or while enforced MFA is not yet enrolled, until they remediate, and every route of the storage family is one. The family's gate and its routes now judge the same credential: the gate judges every credential a route of the family can serve. + + - **Refused now:** a session the policy holds gets `403` with the gate's own `code` (`PASSWORD_EXPIRED`, `PASSWORD_RESET_REQUIRED` or `MFA_REQUIRED`) and `message` on every storage route, whatever else the request carries, before the family does anything else. While a gate is active, a session that cannot be read is the family's declared `503` (`SERVICE_UNAVAILABLE`), whatever else the request carries. + - **Unchanged:** a session the policy does not hold is served exactly as before, apart from that outage answer; an API key whose owner holds an `account`-scope posture is refused with that posture, as before; and while no gate feature is active nothing changes. + + What changes for you. A user the policy holds must remediate (change the password, or enroll an authenticator) before any storage route answers them, whatever else their request carries. There is nothing to configure. + +### Patch Changes + +- 8d3dd6f: The storage download door resolves its caller through `@objectstack/core`'s `resolveRequestExecutionContext`, the one request-to-`ExecutionContext` composition + + Clause-②: no + + - The download authorizer that `StorageServicePlugin` and `mountStorageRoutes` bind for parent-governed files resolves the caller by calling `resolveRequestExecutionContext` with this door's differences as named parameters: `anonymous: 'fail-closed'`, `authGate: 'resolved'`, `credentials: 'api-key-and-session'`, `sessionApi: 'optional'`, `session: 'request'`, its own way to reach the `tenancy` service, `localization: 'withhold'`, no request locale, `accessToken: 'carry'`, and no OAuth provenance or admission channel. The auth-service lookup, the header normalisation, the tenancy wiring, the verdict reads and the fail-closed `catch` stay at the door. The family's ADR-0069 gate reader, which runs before every storage route, is unchanged. + - The answers are the ones the door gave before: an anonymous caller, an unrecognised cookie, a failed session read with no gate active and a refused API key get `401 AUTH_REQUIRED`; a session or an API key owner the authentication policy holds gets `403` with its code and message from the family's gate; an unreadable permission store or tenancy service gets `503 SERVICE_UNAVAILABLE`; and an API key its own lifecycle refuses gets its `403` and code. A one-off probe of 47 fault wirings through the download route read the same answer on both sides of the change. + - The context the door hands to the owner-record read and to a file-access delegate is now the assembled `ExecutionContext` envelope rather than the resolver's raw output: it also states `isSystem: false` and `principalKind: 'human'`, and no longer carries the internal `credential` marker, which no reader of that context consumes. + - Nothing to do for a consumer: no export, route, configuration key or response shape changed. +- af284b0: feat(spec): the remaining `getSession` readers read `AuthSessionApi`'s declared result, and none reads a top-level `activeOrganizationId` + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains three optional `user` members**, each declared for a named reader: `isPlatformAdmin` (`boolean`, the ADR-0068 D2 rung plugin-auth's `customSession` derives) and `role` (`string | null`, better-auth's `admin` plugin scalar), both read by `judgePlatformAdmin` in `@objectstack/plugin-auth`; and `name` (`string`), read by the runtime dispatcher's AI-route `req.user` resolver as the display name. Every member is optional, so every implementer and caller typechecks unchanged. + - **The readers read through the declared type instead of `any` or a cast** (types only, runtime behaviour unchanged): `judgePlatformAdmin` and the four platform-admin mounts in `@objectstack/plugin-auth` (better-auth's own session type now has to fit the declaration at those reads), the REST server's two session reads (`@objectstack/rest`), the dispatcher's posture read, `resolveSessionData` and both `HttpDispatcher` reads (`@objectstack/runtime`), the cloud-connection and install-local session reads (`@objectstack/cloud-connection`), the webhook redeliver door (`@objectstack/plugin-webhooks`), and the storage session bridge (`@objectstack/service-storage`). + - **Fix — the top-level `activeOrganizationId` fallback is removed** from the storage upload stamp (`@objectstack/service-storage`) and from install-local's `resolveActiveOrgId` (`@objectstack/cloud-connection`). The active organization is read from `session.activeOrganizationId` alone, where the contract declares it. No in-process producer answers a top-level `activeOrganizationId` (better-auth's read is `{ user, session }`), so for the shipped auth plugin nothing changes. An auth service that answered only the flattened shape now gets no organization: the upload is not stamped, and install-local reports no active organization. To fix it, answer the active organization on `session.activeOrganizationId`, as `AuthSessionApi` declares it. +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [a19b2a7] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf540a7] +- Updated dependencies [f573976] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/observability@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/services/service-storage/package.json b/packages/services/service-storage/package.json index ca22efe04e6..2d8a3e44ca6 100644 --- a/packages/services/service-storage/package.json +++ b/packages/services/service-storage/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/service-storage", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Storage Service for ObjectStack — implements IStorageService with local filesystem and S3 adapter skeleton", "type": "module", diff --git a/packages/skills/CHANGELOG.md b/packages/skills/CHANGELOG.md index 6018458aa4b..f205422b285 100644 --- a/packages/skills/CHANGELOG.md +++ b/packages/skills/CHANGELOG.md @@ -1,5 +1,20 @@ # @objectstack/skills +## 18.0.0-next.4 + +### Patch Changes + +- f1c0b57: docs(skills): `objectstack-data` and `objectstack-formula` state that `previous` is `null` on insert — `previous == null` judges an insert, a column read off it there faults, and only a write to an existing row whose prior state is not in hand leaves it unbound (#23983) + + Clause-②: no + + `skills/objectstack-data/references/data-hooks.md` and `skills/objectstack-formula/SKILL.md` said `previous` is unbound on insert events and told authors to write insert-event conditions over `record` alone. Since `@objectstack/objectql` binds `previous` to `null` on insert in validation rules and declarative hook conditions (the binding a flow's start condition already had), that text was false: `previous == null || …` judges an insert and `previous != null && …` skips one; only a column read off `previous` on insert (`previous.status`) still has no value and still fails the write, and only a write to an existing row whose prior state is not in hand leaves `previous` unbound, failing closed. The bullet, the scope-table row, the abort note and the legacy-translation note now state that binding and the `null` guard; the `record`-alone advice stays correct and is no longer presented as the rule. Text only; nothing an author writes changes, and a hook's `ctx.previous` is unchanged. +- 53ef752: docs(skills): `objectstack-api` states that a public form's submit is authorized by the form's own `publicFormGrant`, and that the `guest_portal` name on the submit context admits nothing and needs no permission set (#24239) + + Clause-②: no + + `skills/objectstack-api/SKILL.md` described the anonymous form endpoints as running under a synthetic `{ permissions: ['guest_portal'], anonymous: true }` execution context and named nothing else, so an author reading it inferred that a `guest_portal` permission set authorizes the submit and declared one, or concluded that the form falls open without one. Neither is true: the submit is authorized by the form's own declaration (ADR-0056 Option A) — the route hands the engine a `publicFormGrant` scoped to the form's target object, which admits the create and the read-back of the row it made and nothing else, decided before any permission set resolves. The `guest_portal` name on the context admits nothing; where a deployment registers a set under it, that set only feeds the grant's result masker. The passage now says so, matching `content/docs/ui/public-data-collection.mdx` and the `registerFormEndpoints` docstring in `@objectstack/rest`. Text only; nothing an author writes changes, and the form routes' behaviour is unchanged. + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/skills/package.json b/packages/skills/package.json index e47cdf25758..2af7d48090c 100644 --- a/packages/skills/package.json +++ b/packages/skills/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/skills", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "description": "The published ObjectStack skills catalog \u2014 the repository's skills/** tree, shipped at the version of the @objectstack/* packages it teaches.", "license": "Apache-2.0", "homepage": "https://github.com/objectstack-ai/objectstack/tree/main/packages/skills", diff --git a/packages/spec/CHANGELOG.md b/packages/spec/CHANGELOG.md index ad32c80d1ce..963813a0871 100644 --- a/packages/spec/CHANGELOG.md +++ b/packages/spec/CHANGELOG.md @@ -1,5 +1,402 @@ # @objectstack/spec +## 18.0.0-next.4 + +### Minor Changes + +- eebaf3b: feat(objectql,core): the driver's tenant read scope is the organization wall's verdict — one predicate for Layer 0 and every driver (ADR-0131 D8) + + Clause-②: yes (widening) + + - **What changed.** The engine no longer derives `DriverOptions.tenantId` / `tenantIds` beside the organization wall. `buildDriverOptions` projects the wall's own verdict onto them: the verdict plugin-security records on the operation (`OperationContext.tenantLayer0Verdict`) where its wall ran, and otherwise the same function over the engine's own facts (the posture in force, the context's organization and membership set). The function, `computeTenantLayer0Verdict` with its input type `TenantLayer0Input`, moves to `@objectstack/core` (new export); `@objectstack/plugin-security`'s `tenant-layer.ts` re-exports it and its Layer 0 is byte-identical. + - **FROM → TO, per posture, for a read, count, aggregate, update or delete on an object with an organization column:** + + | posture / caller | driver scope before | driver scope now | + |:--|:--|:--| + | `single`, any caller naming an organization | that organization (plus organization-less rows) | **none** | + | `isolated`, member | the active organization | unchanged | + | `group`, member | the membership set | unchanged | + | walled, exempt `PLATFORM_ADMIN` on a posture-permitting object, or the verified platform owner's read | the active organization | **none** — what Layer 0 already admitted | + | walled, system context naming an organization | that organization | unchanged | + + - **Not changed.** Inserts and upserts keep the active organization as `tenantId` in every posture: the row is stamped with it, its counter scopes by it, and an upsert that would merge into another organization's row is still refused. Both `orWhereNull` compatibility arms in `SqlDriver.applyTenantScope` stay (their retirement is a later stage of ADR-0131 D13). + - **Who notices.** A `single` deployment that holds more than one organization was kept apart by the driver only by accident; postures now differ only in enforcement (ADR-0131 D11), and `single` enforces no wall. To keep organizations apart, run the `isolated` or `group` posture. Code that relied on a context organization to narrow a read on `single` names the organization in its own `where`. + - **Migration.** ADR-0087 semantic entry `driver-read-scope-follows-tenant-wall`. A deployment's `platformGlobalObjects` carve-out already reaches the driver through the column plan (ADR-0131 D7): a declared object carries no organization column, so neither wall has anything to scope. +- 194a42d: feat(spec): `ComponentPropsMap` gains a `list-view` row, so a list view embedded on a page is judged as a list view; stored page nodes in the old `objectName` / `viewType` spelling convert to `data` / `type` on read + + Clause-②: yes (widening) + + - **The row.** `ComponentPropsMap['list-view']` is the list view's own members by reference (`ListViewSchema.shape`), with both of its object-level checks attached (the calendar visualization and the chart dataset binding), plus the two members the list overlay member declares instead: `columns` is optional, and the legacy `options` bag is judged per kind (`options.kanban` by the kanban block's schema, `options.grid` refused because a grid has no per-kind block). Until this row a page `list-view` node's `properties` were judged by nothing, so a kanban block's `groupField` or an `options.grid` bag, refused at every list view door, saved green on a page. `os validate`, `os build`, `os lint` and the page save door now report them through the existing component-props rule (`component-props-unknown-key`), naming the key. No rule, tier or door is added, and `PageSchema`'s accept set does not move. + - **The page carrier's old spelling.** A page `list-view` node names its object in `properties.data` and its list kind in `properties.type`, the spelling every other list view door uses. `properties.objectName` and `properties.viewType` are refused by name, with the prescription the react tier gives the same two aliases on `ListView` (`REACT_RETIRED_OVERLAY_PROPS.ListView`, now declared once for both tiers). Fix: replace `objectName: 'deal'` with `data: { provider: 'object', object: 'deal' }` (or name the object on the node's `dataSource.object`), and `viewType: 'kanban'` with `type: 'kanban'`; the node's own `type` stays `list-view`. `os migrate meta --from 17 --write` makes the edit where it can prove it. + - **Stored pages.** The ADR-0087 D2 conversion `page-list-view-object-name-view-type-to-data-type` moves the two aliases to `data` and `type` on every page `list-view` node it reaches (regions, slots, nested containers), retired from the load path: stored `sys_metadata` page rows, compiled artifacts and installed packages replay it on read, while an authored source meets the refusal. An alias beside a spec key that says something different is left as stored and reported as a TODO (`OS_METADATA_CONVERSION_TODO`). Its D3 record is the semantic entry `page-list-view-object-name-view-type-retired`. + - **Also classified:** a `list-view` inside a print page is refused with its reason (it pages its rows), and the view-level API data source refusal names `list-view` among the page components whose `data` prop takes a view data source. +- eda6f14: feat(spec)!: an install through `POST /api/v1/packages` may declare `manifest.scope` `project` only; `cloud` and `system` are refused, so an authored install can no longer lock itself + + `cloud` and `system` are the scopes of packages the platform ships, and the ADR-0070 D2 read-only rule (`isWritablePackage`) judges a registered package by its stored `manifest.scope`. An install through `POST /api/v1/packages` authors the package, yet the install declaration admitted all three scopes. So a manifest that copied `scope: 'cloud'` or `'system'` from a shipped package answered `201` and produced an authored package that `DELETE /packages/:id`, `PATCH /packages/:id/disable` and `POST /packages?overwrite=true` then refused with `422 WRITABLE_PACKAGE_REQUIRED`, and the stored row came back on every restart. The install declaration now narrows the install manifest's `scope` to `project` (new export `PackageInstallManifestSchema`, `ManifestSchema` with that one key narrowed), on both body forms of `PackageInstallBodySchema` and on `PackageInstallRequestSchema.manifest`. `ManifestSchema` itself is unchanged: platform packages still declare all three scopes, and they reach the registry by code boot, not over this door. The door already parses its body through that declaration, so it answers the refusal with no rule of its own; its bare-form refusal sentence now re-reads the union's own bare branch, so it carries the prescription too. + + Clause-②: no (narrowing) + + + + **BREAKING** (a published request declaration's accept set narrows), shipped as `minor` under the launch-window convention for breaking changes. + + - **FROM:** `POST /api/v1/packages` with a manifest declaring `scope: 'cloud'` or `scope: 'system'`, in the wrapped body `{ manifest }` or as a bare manifest, with or without `overwrite`, answered `201`; `PackageInstallBodySchema` and `PackageInstallRequestSchema` parsed it green. + - **TO:** the same request answers `400` with `error.code: 'VALIDATION_ERROR'`, names `scope` and the value sent, and installs nothing; both schemas refuse it with an `invalid_value` issue at `manifest.scope` (wrapped) or `scope` (bare). + - **Unchanged:** a manifest with `scope: 'project'` or no `scope` installs as before (`201`), and the door still stores the manifest as sent, so an absent `scope` stays absent. `ManifestSchema` and every other producer of it keep `cloud` / `system`. + - **The fix for an integrator:** remove `scope` from the manifest you install (it defaults to `project`), or write `scope: 'project'`. + - **A package that already locked itself this way** (an authored `sys_packages` row whose manifest declares `cloud` or `system`) stays read-only after this change, because the read-only rule reads the stored scope. No API door removes it; an operator deletes its row from the environment's database (`DELETE FROM sys_packages WHERE id = 'PACKAGE_ID'`) and restarts the server. +- 030a7b8: feat(spec)!: `record:json` is retired — refused by name at the schema door; the approval request page's raw JSON panel it was declared for was dropped (#24162, ADR-0049) + + Clause-②: no (narrowing) + + + + **BREAKING** — an accept-set narrowing on a published authoring surface, shipped as `minor` under the repo's launch-window convention for accept-set narrowings (the `user:profile`, `element:filter`, `element:form` and `ai:chat_window` retirements shipped the same way). + + `record:json` was added to `PageComponentType` in `18.0.0-next.3`, with a strict `ComponentPropsMap` row, `RecordJsonProps` (`field`, `label`, `requiredPermissions`). It was declared spec first for one face: the approval request page's raw JSON panel. The maintainer then dropped that panel — the person deciding an approval has no business need to read the request as JSON — so no renderer was built and no page placed the type: zero consumers in objectstack and objectui, and its one planned producer was closed unmerged. A declared element that nothing draws is a capability claim nothing keeps, so it is retired under ADR-0049 enforce-or-remove, refused by name, the `ai:chat_window` way. The `18.0.0-next.3` entry below records what that prerelease shipped; this entry is its retirement. + + **What is refused:** an authored `record:json` component node, at `PageComponentSchema.type`. That covers `definePage()`, `PageSchema`, and every door that parses pages: `os validate`, `os build`, `os lint` and the metadata save door. The issue is located at the node's own path, with `code: 'custom'` and `params.retiredComponentType`, and its message is the retirement prescription. `PageComponentType`'s own error map refuses the name with the same text when the enum is parsed alone. `ComponentPropsMap['record:json']` stays as a row, so every reader that dispatches on it keeps recognising the name: the component-props gate, `check-yaml-examples` and the type vocabulary's known set. The row now refuses every props bag, `{}` included, with the same prescription. One prescription string, `RETIRED_PAGE_COMPONENT_TYPES` in `@objectstack/spec/ui`, answers at all three doors. The authoring rule `component-type-unknown` reports the node by name, and stops suggesting `record:json` for a misspelling in the `record:` namespace. + + **What is removed from the exports:** `RecordJsonProps` (`@objectstack/spec/ui`), the props schema the element no longer has. Its JSON Schema (`ui/RecordJsonProps`) is no longer published. Its print classification row (`PRINT_REFUSED_PAGE_COMPONENT_TYPES`) leaves with it: a retired type is refused at the parse before any print rule reads it. + + **What stays accepted:** every other member of `PageComponentType` and `ComponentPropsMap`, byte-identically, including the four record blocks' own `requiredPermissions` gate, which `record:json` never changed. The open string arm also stays open: custom and plugin-registered types keep parsing. The only string refused is the retired name itself. + + ## FROM → TO + + | you wrote | write instead | + |:--|:--| + | a `{ type: 'record:json' }` component node in a page region, slot or container | nothing: delete the node. No replacement panel exists, by ruling | + | `properties: { field, label, requiredPermissions }` on that node | nothing: none of them was ever read | + + The one-line fix: delete the `record:json` component node. No ADR-0087 conversion is registered, because the only edit is deleting an authored page node, and a mechanical conversion does not delete page nodes: which region closes up is a layout decision. The D3 entry `ui-record-json-retired` carries that delegation, so `os migrate meta --from 17` lists it as a manual change for every stack that still names the type. + + ## Who is affected, measured + + - **objectstack** at `eda6f14b7b`: zero authored `record:json` nodes in `examples/**`, `packages/apps/**`, `apps/**`, `skills/**` and `content/docs/**` code samples. The only hits were the spec's own type list, row and print row, its tests, the lint accept pin and the generated reference docs. A tree-scoped absence pin now holds that zero. + - **objectui** `main`: zero hits (the card's census); the `.objectui-sha` pin is not moved. + - **Deployed metadata** was not measured; the type was published in one prerelease only. + + The retirement kit: + + - the retired-type map entry and the enum value removed (`packages/spec/src/ui/page.zod.ts`), its print classification row removed, and the row turned into a whole-bag refusal, with `RecordJsonProps` removed (`packages/spec/src/ui/component.zod.ts`) + - the D3 semantic entry `ui-record-json-retired`, its step-18 rationale fragment, and the `RETIRED_DEFS_BY_MAJOR` entry `ui/RecordJsonProps` + - pin tests: `component-record-json-retirement.test.ts` pins `code`, `path`, `params` and the first sentence at each door (the map, the row with empty and populated bags, `PageComponentSchema`, `PageSchema` at the element path, the enum error map), with `record:approvals` as the live control, plus a tree-scoped absence walk; `component-type-vocabulary.test.ts` pins the type known, out of the typo candidates, and `record:` still reserved; the lint accept pin `validate-record-json-23745.test.ts` is flipped into a refusal pin at both lint rules and the shared pipeline + - generated baselines and docs follow the schema: `api-surface/`, `export-origins/`, `declaration-map/`, `authorable-surface/`, `json-schema.manifest/`, the strictness-ledger counts, `spec-changes.json`, the upgrade guide and the reference docs +- b1845ed: feat(spec/contracts): `IAuthService.verifyMcpAccessToken`'s verified result gains an optional `organizationId`, the organization the user's OAuth consent bound + + Clause-②: yes (widening) + + - **What changed.** The verified result of `verifyMcpAccessToken` (`@objectstack/spec/contracts`) is now `{ userId: string; scopes: string[]; clientId?: string; organizationId?: string }`. `organizationId` is the organization the user's OAuth consent bound (the user's current organization at the moment of consent), read from the access token's `organization_id` claim. + - **Absent** when no consent bound one: a token minted before consents bound an organization. Absent means no organization, so only the user's global grants apply. + - **A claim, not an authorization.** A caller vets it on every request by the rule that vets a session's active organization (`vetOrganizationClaim`, `@objectstack/core`). Once no current membership backs it, the principal resolves with no organization (global grants only: tenant reads come back empty and tenant writes are refused), never in another organization. The token acts in that one organization only: never the union of the user's organizations, and never the organization the user's browser session has selected now. + - **Type only, and additive.** The member is optional, so an implementation that returns no `organizationId` still satisfies the contract, and nothing this package does at runtime changes. This package declares the member only: writing the claim at consent is the auth plugin's, and reading it into the OAuth session is the runtime's. +- 8215ff7: fix(spec)!: `defineStack` judges a `record:line_items` column that declares no `type` as the type it renders, as it already does on the master-detail carriers (#24190) + + Clause-②: yes (narrowing) + + + + **BREAKING** — an accept-set narrowing of what `defineStack` and `objectstack validate` publish, shipped as `minor` under the repo's launch-window convention for accept-set narrowings. The console's `record:line_items` panel fills a column that declares no `type` from the child object's field, with the same rule as the master-detail grid. `defineStack`'s cross-reference check judged that resolved type on the three master-detail carriers and not on this block, so the same column got two answers. + + **`@objectstack/spec`** + + - **`defineStack`'s cross-reference check** now reaches a `record:line_items` block in a page region's components, in a container's component children, in a slot and, since #24349, in a `page:tabs` or `page:accordion` item's `children`. It judges a column that declares no `type` as the type it renders, and reports the column schema's own refusal (`STACK_CROSS_REFERENCE_INVALID`, 422). Today that means `scale` on an identity-only column over a `currency` field of the child object. The child object is the one the panel lists: the object `dataSource` binding's `object` when the node carries one, else `childObject`. A child object the stack does not declare, a column naming no field of it, and a column the column schema refuses on its own (left to the component-props gate) are not judged. + - The step-18 migration entry `ui-record-line-items-props-closed` now says so, and no longer says that this block draws a column exactly as declared. + + ## FROM → TO + + | you wrote | write instead | + |:--|:--| + | `{ type: 'record:line_items', properties: { childObject: 'invoice_line', relationshipField: 'invoice', columns: [{ name: 'amount', scale: 2 }] } }` where `amount` is a `currency` field of `invoice_line` | the same block with `columns: [{ name: 'amount' }]` | + + The one-line fix: delete `scale` from a `record:line_items` column that renders as a currency column, whether it declares `type: 'currency'` or takes it from a `currency` child field. Nothing replaces `scale` there: the currency's ISO 4217 minor unit decides the displayed decimals. + + ## Who is affected, measured + + On `origin/main` `ec749fbb44`: two authored `record:line_items` blocks in the examples (the showcase project detail page, five typed columns, its block under a `page:tabs` item; the showcase invoice document page, four `{ name, label }` columns, two of them over `currency` fields) and one in the published skills (four `{ name }` columns). None carries `scale`, so no example, skill or fixture outside this change's own pins changes verdict. Deployed metadata was not measured. +- b036ac5: feat(spec): `AuthSessionApi.getSession`'s result declares the fields its in-process readers read + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains five optional members**, each declared for a named reader and typed as that reader takes it: `user.email` and `session.id` / `session.token` (read by `resolveAuthzContext`'s session arm in `@objectstack/core`), `user.authGate` as `unknown` (the ADR-0069 posture, which `normalizeAuthGate` validates against the closed posture vocabulary), and `session.impersonatedBy` as `string | null` (read by the print door's `impersonatorOf` in `@objectstack/service-print`). Before this, those reads went through `any` or a cast, so a field better-auth dropped or renamed compiled and read `undefined`. For `impersonatedBy`, that would have silently turned off the impersonation refusal. Every member is optional, so every implementer and caller typechecks unchanged. + - **`@objectstack/core`**: `resolveAuthzContext`'s session arm reads the session through the declared type instead of `any`. `ResolveAuthzInput.getSession`'s own type is unchanged. Runtime behaviour is unchanged. + - **`@objectstack/service-print`**: `impersonatorOf` reads `session.impersonatedBy` through the declared type instead of a cast. Runtime behaviour is unchanged. +- 6ec0e12: feat(spec/ai): a solution blueprint's select options declare their lifecycle `kind`, and a blueprint object names its `stageField` + + Clause-②: yes + + The object schema already requires a lifecycle `kind` (`open` / `won` / `lost`) on every option of the field an object's `stageField` names. The AI blueprint had no slot for either key, so a model could not say which field was an object's stage or which option ended its lifecycle, and that rule never applied to an AI-built object. Both blueprint shapes now carry both keys. + + - **Option `kind`**, on `BlueprintFieldSchema.options[]` and on the strict structured-output mirror's (`SolutionBlueprintStrictSchema`) field options. It is `SelectOption.kind`'s own enum, taken by reference, so the blueprint accepts exactly the kinds an object field's option accepts. Lenient: optional. Strict: required and nullable (null = no kind stated). + - **Object `stageField`**, on `BlueprintObjectSchema` and the strict mirror's objects: a snake_case name of one of the object's own fields, mapping onto `ObjectSchema.stageField`. Lenient: optional. Strict: required and nullable (null = no stage field). The object schema's `false` value ("no linear lifecycle") has no blueprint spelling. + - Both describes state what the object schema refuses: the field `stageField` names must be a single-value `select` or `radio` whose inline options each declare `kind`. A multiselect / checkboxes / tags field that carries options is refused as a stage field, and `kind` on such a field's options is refused by the field schema. The blueprint does not check these rules itself; the object schema does, when the applier writes the object. + - Additive only: every blueprint that parsed before still parses with the lenient schema. A producer of the STRICT shape must now send `stageField` on each object and `kind` on each option (null when it has none), as with every other key in that mirror. +- af284b0: feat(spec): the remaining `getSession` readers read `AuthSessionApi`'s declared result, and none reads a top-level `activeOrganizationId` + + Clause-②: yes (widening) + + - **`@objectstack/spec/contracts` — `AuthSessionApi.getSession`'s result gains three optional `user` members**, each declared for a named reader: `isPlatformAdmin` (`boolean`, the ADR-0068 D2 rung plugin-auth's `customSession` derives) and `role` (`string | null`, better-auth's `admin` plugin scalar), both read by `judgePlatformAdmin` in `@objectstack/plugin-auth`; and `name` (`string`), read by the runtime dispatcher's AI-route `req.user` resolver as the display name. Every member is optional, so every implementer and caller typechecks unchanged. + - **The readers read through the declared type instead of `any` or a cast** (types only, runtime behaviour unchanged): `judgePlatformAdmin` and the four platform-admin mounts in `@objectstack/plugin-auth` (better-auth's own session type now has to fit the declaration at those reads), the REST server's two session reads (`@objectstack/rest`), the dispatcher's posture read, `resolveSessionData` and both `HttpDispatcher` reads (`@objectstack/runtime`), the cloud-connection and install-local session reads (`@objectstack/cloud-connection`), the webhook redeliver door (`@objectstack/plugin-webhooks`), and the storage session bridge (`@objectstack/service-storage`). + - **Fix — the top-level `activeOrganizationId` fallback is removed** from the storage upload stamp (`@objectstack/service-storage`) and from install-local's `resolveActiveOrgId` (`@objectstack/cloud-connection`). The active organization is read from `session.activeOrganizationId` alone, where the contract declares it. No in-process producer answers a top-level `activeOrganizationId` (better-auth's read is `{ user, session }`), so for the shipped auth plugin nothing changes. An auth service that answered only the flattened shape now gets no organization: the upload is not stamped, and install-local reports no active organization. To fix it, answer the active organization on `session.activeOrganizationId`, as `AuthSessionApi` declares it. +- 14a8bd2: feat(spec)!: `marketplace:installed-list` is retired — refused by name at the schema door; the separate Installed Apps view it drew was retired, and Setup → Packages filtered to Source = marketplace replaces it (#24230, ADR-0049) + + Clause-②: no (narrowing) + + + + **BREAKING** — an accept-set narrowing on a published authoring surface, shipped as `minor` under the repo's launch-window convention for accept-set narrowings (the `user:profile`, `element:filter`, `element:form`, `ai:chat_window` and `record:json` retirements shipped the same way). + + `marketplace:installed-list` was the plugin console widget that drew the separate Installed Apps view. It was published in 17.3.0 with an empty `ComponentPropsMap` row and was never a `PageComponentType` member: it reached the page schema through the type union's open string arm. The separate view was then retired, because Setup → Packages carries the same packages and their actions behind a Source = marketplace filter. The widget's one page, `marketplace_installed`, had already left `@objectstack/cloud-connection`, whose "Installed Apps" navigation entry opens the filtered Packages view instead, and the console deleted the widget's one renderer. After that, an authored node validated clean and then drew "Unknown component type" in front of a user. A declared element nothing draws is a capability claim nothing keeps, so it is retired under ADR-0049 enforce-or-remove and refused by name, the `ai:chat_window` way. + + **What is refused:** an authored `marketplace:installed-list` component node, at `PageComponentSchema.type`. That covers `definePage()`, `PageSchema`, and every door that parses pages: `os validate`, `os build`, `os lint` and the metadata save door. The issue is located at the node's own path, with `code: 'custom'` and `params.retiredComponentType`, and its message is the retirement prescription. `ComponentPropsMap['marketplace:installed-list']` stays as a row, because for a type outside the enum the row is the only thing that keeps the name known to the component-props gate, `check-yaml-examples` and the type vocabulary. The row now refuses every props bag, `{}` included, with the same prescription. One prescription string, `RETIRED_PAGE_COMPONENT_TYPES` in `@objectstack/spec/ui`, answers at every door. The authoring rule `component-type-unknown` reports the node by name, and the typo suggester no longer offers it. + + **What else moves:** its print classification row (`PRINT_REFUSED_PAGE_COMPONENT_TYPES`) leaves with it, because a retired type is refused at the parse before any print rule reads it. The D3 entry `ui-cloud-connection-widgets-unknown-keys-refused`, which closed this type's props bag to `{}` in the same major, now speaks for `cloud-connection:panel` alone, so the upgrade guide no longer tells an upgrader to keep a node that is refused. No export is removed: the row was built by the `emptyProps` factory, which exports no def, so no JSON Schema leaves the published set. + + **What stays accepted:** every other `ComponentPropsMap` row, byte-identically, including the sibling console widgets `cloud-connection:panel` and `mcp:connect-agent`. The open string arm also stays open: custom and plugin-registered types keep parsing, the `marketplace:` namespace included. The only string refused is the retired name itself. + + ## FROM → TO + + | you wrote | write instead | + |:--|:--| + | a `{ type: 'marketplace:installed-list' }` component node in a page region, slot or container | nothing: delete the node. The Installed Apps list is Setup → Packages filtered to Source = marketplace | + | a page whose only job was to show that list | a navigation item `{ type: 'component', componentRef: 'developer:packages', params: { source: 'marketplace' } }`, which is what `@objectstack/cloud-connection`'s own "Installed Apps" entry does | + + The one-line fix: delete the `marketplace:installed-list` component node, and link to the filtered Packages view if users need to reach it. No ADR-0087 conversion is registered, because the only edit is deleting an authored page node, and a mechanical conversion does not delete page nodes: which region closes up is a layout decision. The D3 entry `ui-marketplace-installed-list-retired` carries that delegation, so `os migrate meta --from 17` lists it as a manual change for every stack that still names the type. + + ## Who is affected, measured + + - **objectstack** at `cbfa5e4335`: zero authored `marketplace:installed-list` nodes in shipped metadata. `@objectstack/cloud-connection` serves no page for it, and its pin test asserts that. The other hits were the spec's own row, print row and docblocks, its tests, two lint pins, one synthetic test fixture in `packages/cli` (now a live block) and the release-owned changelogs. A tree-scoped absence pin now holds that zero. + - **objectui** at the console pin `.objectui-sha` `5e15928b46`: the type is registered by string only, with no typed read of `ComponentPropsMap['marketplace:installed-list']`. The row's key is kept, so the console build at the pin is unaffected. objectui `main` has deleted the renderer. + - **Deployed metadata** was not measured. + + The retirement kit: + + - the retired-type map entry and the print classification row removed (`packages/spec/src/ui/page.zod.ts`), the row turned into a whole-bag refusal (`packages/spec/src/ui/component.zod.ts`), and the vocabulary docblock updated (`component-type-vocabulary.ts`) + - the D3 semantic entry `ui-marketplace-installed-list-retired` and its step-18 rationale fragment; the D3 entries `ui-cloud-connection-widgets-unknown-keys-refused` (narrowed to `cloud-connection:panel`) and `ui-mcp-connect-agent-unknown-keys-refused` (one clause) amended + - pin tests: `component-marketplace-installed-list-retirement.test.ts` pins `code`, `path`, `params` and the first sentence at each door (the map, the row with empty and keyed bags, `PageComponentSchema`, `PageSchema` at the element path), the absence from the enum and the print rows, the D3 registration and the narrowed sibling entry, with `cloud-connection:panel` as the live control, plus a tree-scoped absence walk; `component-type-vocabulary.test.ts` pins the type known only through its row, out of the typo candidates, and `marketplace:` still unreserved; the lint accept pins in `validate-component-types.test.ts` and `validate-component-props.test.ts` are flipped into refusal pins + - generated docs follow the schema: the reference page for `ui/page`, `spec-changes.json` and the upgrade guide +- 1e001c7: feat(spec): `PublishPackageDraftsResponseSchema` declares `backfillApplied`, the per-field receipt of the publish door's opt-in backfill + + Clause-②: yes (widening) + + - `backfillApplied?: { success, fields: [{ object, field, updated, error?, code? }] }`, present only when the request named a `backfill` and the batch published. Declared so the door's response parse carries it instead of stripping it. +- 0b44840: feat(spec): register four organization and invitation-placement refusal codes under `@objectstack/plugin-auth` + + Clause-②: yes (widening) + + The error-code ledger (`ERROR_CODE_LEDGER`, and so `ErrorCode`) gains four `@objectstack/plugin-auth` codes. Each names a hook refusal that today answers a bare `403 FORBIDDEN` with a message and no `code`, so a client such as the console cannot tell it apart from any other refusal on the same endpoint: + + - `ORGANIZATION_CREATION_DISABLED`: creating an organization is refused for every caller, because the deployment's effective tenancy posture enforces no organization wall (`/auth/config` reports `features.multiOrgEnabled: false`). + - `ORGANIZATION_SLUG_LOCKED_BY_ACTIVE_ENVIRONMENTS`: an organization's slug cannot change while the organization has an environment whose status is neither archived nor failed. + - `INVITE_PLACEMENT_UNSUPPORTED`: fail-closed. The invitation carries a placement (a business unit and positions), and this deployment has no delegated-administration runtime to authorize it. + - `INVITE_PLACEMENT_OUTSIDE_ISSUER_SCOPE`: the delegated-administration gate refused the invitation's placement, because the business unit or a position lies outside the issuer's delegated scope. + + Registration only: `@objectstack/plugin-auth` still throws these refusals without a code until #24243 stamps them. The same change names a standard code, which needs no ledger row, for the plugin's other four code-less refusals: `RATE_LIMIT_EXCEEDED` for both phone-OTP send walls, which stay indistinguishable; `SERVICE_UNAVAILABLE` for the slug guard's read fault; and `VALIDATION_ERROR` for the reserved `__global__` organization id or slug. No existing code changes, and no standard-catalog member is shadowed (none needs a synonym waiver). +- ef65646: Clause-②: yes (widening) + + feat(spec): an app page binds its row as `record`, so a component `visibleWhen` can follow a bound count or a plan field + + - **What it adds.** `PageSchema.record` (`PageRecordBindingSchema`): a one-row query `{ object, view?, filter?, sort? }` on a `type: 'app'` page. The page's row is the FIRST row of that query, read under the caller's identity, and every component predicate on the page reads it as `record`, the root ADR-0089 D1 already names for runtime surfaces. No new expression root is added. + - **What a predicate reads.** A count or a plan is a field on the bound row: a `summary` (roll-up) field (`record.environment_count > 1`) or the object's own field (`record.plan != 'free'`). `features.*` is not a plan: that root is the deployment's public auth flags (`/api/v1/auth/config`), the same for every organisation and every viewer. The `visibleWhen` docblock and describe now say so, and they say that a block's own element `dataSource` binds no `record` for its siblings or for its own `visibleWhen`. + - **What it refuses.** `record` on any page that is not `type: 'app'` is refused at parse (`checkPageRecordBindingPlacement`, exported from `@objectstack/spec/ui` like the other page checks): a record page's row is the record it opens, and a `home` / `list` / `utility` page has no row. Inside the binding, `limit` is refused with a prescription (a page binds one row; say which with `sort` or `filter`), and so are `id` / `recordId` (match the row with a `filter` rule on `id`). + - **Planned in the renderer.** The console does not mount the binding yet: until it does, `record` stays unbound on an app page, as before, and a predicate reading it faults, so the component renders. The liveness ledger grades the key `planned`. + - **Unchanged:** every existing page, component and predicate; the predicate root set (`record`, `current_user` with its aliases, `page.NAME`); the lint's declared roots. +- 586705d: feat(spec): the bound record blocks `record:details`, `record:highlights` and `record:quick_actions` accept an authored `emptyState`, drawn only when the block's `dataSource` binding answers no row + + Clause-②: yes (widening) + + - **The key.** `RecordDetailsProps`, `RecordHighlightsProps` and `RecordQuickActionsProps` each gain an optional `emptyState`: the list view's own `EmptyStateSchema` by reference (`{ title, message, icon, link }`), the same shape `object-grid.emptyState` takes. Until now all three rows refused the key, so a page whose binding can legitimately answer no row (for example an environment block on an organisation with no production environment) could only show the block's own placeholder: "record not found", nothing, or an empty action bar. + - **When it is drawn.** Only when the block carries a node-level `dataSource` binding and that read answers no row. A block with no binding reads the record page's record and never draws it, a loading read draws the loading state, and a failed read draws its error. On `record:quick_actions`, a bound record whose bar resolves no action keeps the existing empty placeholder. + - **Planned, not yet rendered.** No renderer reads the key yet; each describe opens with `[PLANNED — not yet rendered]`, and the renderer half is objectstack-ai/objectui#12932, which ships once a spec release carrying the key is installable. Until then an authored `emptyState` validates and is not drawn. + - **No other row moves.** Every other `ComponentPropsMap` row still refuses `emptyState` (`record:related_list`, whose `parentDataSource` has its own declared answer for no row, included), so `os validate` keeps reporting it there as `component-props-unknown-key`. Nothing narrows. +- 584b422: feat(spec): the draft read declares `draftChanges`, a pending draft's per-key provenance: which authoring surface last changed each key path of the draft, under which actor, and when + + Clause-②: yes (widening) + + - **The key.** `GetMetaItemResponseSchema` gains an optional `draftChanges` beside `item`: one entry per changed key path of the pending draft body, each `{ path, by, actor, at }`. `path` is an array of the keys from the body root to the deepest key the write changed (`["fields", "renewal_count"]`, `["fields", "renewal_count", "label"]`); an array value is one leaf, so a change to a view's columns is `["columns"]` and never carries an index (which elements differ is read by comparing the array with the published body's; the entry says who last changed the array); `[]` is the whole body of an item created as a draft. `by` is a closed set: `copilot` (an AI assistant's tool writing on a person's behalf), `studio` (a person editing in Studio) or `api` (any other caller of the save door). `actor` is the write's recorded identity or `null`, and `at` its ISO-8601 instant. + - **Where it is served.** Only on the strict draft read (`GET /meta/:type/:name?state=draft`), the read Studio's editor loads an item from, so the marks always describe the body that read returned. It is absent on the active read, the cached branch and the `?preview=draft` read. `ListDraftsResponseSchema` (`GET /meta/_drafts`) is unchanged. + - **Absent means "not recorded".** No server writes the key yet; until one does, and for a draft row saved before it did, the key is absent rather than `[]`, so a reader never takes "not recorded" for "nothing changed". + - **Nothing breaks.** Every response that parsed before still parses, and no runtime behaviour changes in this release of the spec. +- 370833a: feat(spec): `ApprovalRequestRow` declares `current_step_label`, the `sys_approval_request` column's own name for the step's label + + Clause-②: yes (widening) + + - **The member.** `ApprovalRequestRow` (`@objectstack/spec/contracts`) gains an optional `current_step_label?: string` beside `step_label`. It names the declared `sys_approval_request.current_step_label` column: the Approval node's authored label as it stood when the request reached the step. A page bound to `sys_approval_request` names the object's declared fields, and `step_label` is not one, so a served row that carries this member lets such a page list the step by its label instead of by `current_step`, the node's machine id. + - **Nothing moves.** `step_label` stays, at the same shape and with the same value, so no reader changes. The member is optional, so every existing implementer and every row built without it still conforms. It is a member of a TypeScript interface: no Zod schema and no runtime accept set changes. + - **Its producer.** `@objectstack/plugin-approvals`' request read mapping is the producer; serving the member there is that package's own change. +- 3a734e0: feat(spec)!: a page region named `main` takes only `width: 'full'` — `'small'`, `'medium'` and `'large'` there are refused at publish (#24326) + + Clause-②: yes (narrowing) + + + + **BREAKING** — an accept-set narrowing on a published authoring surface, shipped as `minor` under the repo's launch-window convention. + + `PageRegionSchema.width` is one enum shared by every region, so a `main` region accepted `'small'`, `'medium'` and `'large'`. The console never read them: every layout draws `main` at the width the layout leaves it (the flexible column beside `sidebar` and `aside`; one grid cell on `dashboard`), and the only reader of `main.width` is the page itself, for `'full'`, which lifts the page's max-width cap. A three-column page that asked for a `large` main got the space the two rails left, and every door reported success. + + - `PageSchema` now refuses `width: 'small' | 'medium' | 'large'` on a region named `main` (matched case-insensitively, as the console matches it), at `regions.N.width`, on every page kind and template. The message names the region and `'full'` as the one width it takes. The doors are `definePage`, `defineStack` (so `os validate` and `os build`, `STACK_SCHEMA_INVALID` 422) and the metadata save door (422 `INVALID_METADATA`). + - `'full'` on `main` is unchanged, with its page-wide meaning. Every other region accepts the same widths as before. + - The check is exported as `checkPageMainRegionWidth` from `@objectstack/spec/ui`, so a mirror built from `PageSchema.shape` can attach the same rule. + - Data at rest keeps loading: the retired ADR-0087 conversion `page-main-region-width-removed` deletes those widths on read: a stored page row, a compiled artifact inside the window its declared protocol range opens, and a package body registered with its declared range. The page renders exactly as before. + - `@objectstack/mcp` (the Connect an Agent page) and `@objectstack/cloud-connection` (the Cloud Connection page) drop `width: 'large'` from their `main` region. Both render the same. + + ### Migration: FROM → TO + + | Wrote | Write instead | + | :--- | :--- | + | `width: 'small' \| 'medium' \| 'large'` on a region named `main` | drop it; `'full'` stays | + + Deleting the key changes nothing on screen. Write `width: 'full'` only when you want a full-bleed page, which drops the page's max-width cap. +- 31e7d0b: feat(spec): an object can declare its default row scope, `defaultRowScope`, the row-level security policy shape without `object` + + Clause-②: yes (widening) + + `ObjectSchema` gains one optional key, `defaultRowScope`: one row-level security policy, written like a permission set's `rowLevelSecurity` entry without `object`, which the declaring object implies. The shape is exported as `DefaultRowScopeSchema` from `@objectstack/spec/security` (types `DefaultRowScope` and `DefaultRowScopeParsed`). + + - **What it parses.** Every member of `RowLevelSecurityPolicySchema` except `object`, the same member schemas: `name`, `label`, `description`, `operation`, `using`, `check`, `positions` and `enabled` (default `true`). The policy's two rules hold as written: at least one of `using` / `check`, and no `check` on a `select` or `delete` policy. + - **What it refuses.** `object` is refused by name, with a prescription to delete it: the target is implied. Any other unknown key is refused too, the policy's retired `priority` and `tags` included. + - **What it does today: nothing.** No server reads the key in this release, so declaring it filters no rows. The describe states the contract the security plugin's row-policy collection is to meet once it reads the key: the default is to apply when no permission set the caller holds carries a row policy for the object; a policy an authored set carries is to be used as authored; a superuser-bypass principal is to be unaffected; an object without the key keeps its behaviour. No object declares the key yet; the platform's identity objects are to declare it for their own tables. Authoring the key raises the liveness lint's `liveness-planned-property` warning until then. Restrict rows with a permission set's `rowLevelSecurity` policy in the meantime. + - **`composeStacks` with `objectConflict: 'merge'`** now refuses two stacks that declare a different `defaultRowScope` on the same object, the way it refuses every other fixed-shape config object (`access`, `enable`, `publicSharing`, …). The refusal message's list of those objects names `defaultRowScope` too. Nothing composed before is affected, because no stack declared the key. + - **`@objectstack/metadata-core`: the field-level-security projection classifies the new key.** When an object document is served to a caller who cannot read some of its fields, `defaultRowScope` is treated like a validation rule: the whole policy is dropped from the served document when its `using`, `check` or prose names a field that caller cannot read, and served unchanged otherwise. No behaviour moves until an object declares the key, and no object declares it yet. + - Additive: every object that parsed before still parses, and its parsed output is unchanged. +- db1f1fd: fix(spec)!: `defineStack`'s page cross-reference checks judge a component inside a `page:tabs` or `page:accordion` item's `children`, as they judge one at region level (#24349) + + Clause-②: yes (narrowing) + + + + **BREAKING** — an accept-set narrowing of what `defineStack` and `objectstack validate` publish, shipped as `minor` under the repo's launch-window convention for accept-set narrowings. `defineStack` reads a page's components through one traversal, shared by its inline-action target check and its inline grid column check. A tab or accordion item is a panel record (`{ label, value?, children }`), not a component, and the traversal stopped at it, so nothing inside a tab or an accordion panel was judged: a button whose modal target names no page built green in a tab and was refused one level up. + + **`@objectstack/spec`** + + - **`defineStack`'s page cross-reference checks** now reach a component in a `page:tabs` or `page:accordion` item's `children`, at any depth (tabs in an accordion panel, a panel in a container or under a slot). The traversal descends the panel positions the component rows declare (`pageComponentSlotPositions`), the list the other page walks already read, so it follows the declared shapes rather than naming tabs. A record under a key no row declares a panel list is not descended. + - A component in a panel gets the region-level verdict and message, located through the panel: `STACK_CROSS_REFERENCE_INVALID` (422), for example `Inline action 'open_detail' on page 'invoice_record' (regions.0.components.0.properties.items.1.children.0) references page 'no_such_modal' (via modal target) which is not defined in pages.` + - Two checks reach further: an inline action's `type: 'modal'` / `type: 'flow'` `target`, and an identity-only column (one that declares no `type`) carrying `scale` over a `currency` child field, on an `object-master-detail-form` block's `details[].columns` or a `record:line_items` block's `columns`. That includes a `record:line_items` block in a tab or accordion panel, a position the block's identity-only check did not reach before this change. + - New step-18 migration entry `ui-page-panel-children-cross-references-judged`; the entry `ui-record-line-items-props-closed` now names the panel positions in its reach. + + ## FROM → TO + + | you wrote, inside a `page:tabs` or `page:accordion` item's `children` | write instead | + |:--|:--| + | `{ type: 'element:button', properties: { action: { type: 'modal', target: 'no_such_page' } } }` | a `target` naming a page the stack declares (or a declared flow, for `type: 'flow'`) | + | `{ type: 'record:line_items', properties: { childObject: 'invoice_line', relationshipField: 'invoice', columns: [{ name: 'amount', scale: 2 }] } }` where `amount` is a `currency` field of `invoice_line` | the same block with `columns: [{ name: 'amount' }]` | + + The one-line fix: give the component inside the panel the same fix it would need at region level. Nothing about the panel or its items changes. + + ## Who is affected, measured + + On `origin/main` `31e7d0ba2`: four authored pages nest components under a panel item (the showcase project detail page, whose `record:line_items` block declares a `type` on all five columns; the platform user and organization record pages; the approval request record page), one documentation example does (three tabs of record blocks), and the published skills carry none. None holds an inline action target or an identity-only column under a panel, so no example, skill or fixture outside this change's own pins changes verdict. Deployed metadata was not measured. +- f573976: `MEMBERSHIP_REACH` (`@objectstack/spec/identity`) gains two rows, so `requiresMembershipReach` accepts two more names: `update_organization` (better-auth's `/organization/update`, statement `organization: ['update']`, reached by `owner` and `admin`) and `delete_organization` (`/organization/delete`, statement `organization: ['delete']`, reached by `owner` alone). Both are read off better-auth's own default role statements, like the existing rows, and plugin-auth's reach-table test holds each equal to the installed vendor door. An action declaring `requiresMembershipReach: 'update_organization'` is lowered to `visible: "'org_owner' in current_user.positions || 'org_admin' in current_user.positions"`; `'delete_organization'` lowers to `"'org_owner' in current_user.positions"`. Existing rows and their lowering are unchanged. + + Clause-②: yes (widening) + +### Patch Changes + +- bb7ccde: The liveness ledger's CRUD rows cite the code that reads their keys where it now lives + + Clause-②: no (only the rows' evidence paths move and every status stays `live`: `CrudEndpointsConfigSchema` accepts `operations.*` and `dataPrefix` exactly as before, and no state count moves) + + - `liveness/crud_endpoints.json`: the five `operations.*` rows and the `dataPrefix` row cited `packages/rest/src/rest-server.ts#registerCrudEndpoints` for the code that gates and prefixes the CRUD mounts. `@objectstack/rest` moved that registrar's body to `packages/rest/src/crud-routes.ts` with no behaviour change, so those six path references follow it. + - The quoted code, the producers, the verdicts and the notes are unchanged. +- df000d4: The liveness ledger's `/meta` type-list, per-type-list and maintenance switch rows cite the code that reads their keys where it now lives + + Clause-②: no (only the rows' evidence paths move and every status stays `live`: `MetadataEndpointsConfigSchema` accepts `endpoints.types`, `endpoints.items` and `endpoints.maintenance` exactly as before, and no state count moves) + + - `liveness/metadata_endpoints.json`: the `endpoints.types`, `endpoints.items` and `endpoints.maintenance` rows cited `packages/rest/src/rest-server.ts#registerMetadataEndpointsInner` for the gates on their mounts. `@objectstack/rest` moved those three route groups to `packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes` with no behaviour change, so those three path references follow them. + - The quoted code, the producers, the verdicts and the notes are unchanged. The `prefix` row keeps its citation: `metaPath` is still built in the inner method's head. +- 646303e: The liveness ledger's `/meta` cache-flag, per-item switch, doc and book rows cite the code that reads their keys where it now lives + + Clause-②: no (only the rows' evidence paths and notes move and every status stays `live`: `MetadataEndpointsConfigSchema`, `DocSchema` and `BookSchema` accept exactly what they accepted before, and no state count moves) + + - `liveness/metadata_endpoints.json`: the `enableCache` row and the `if (metadata.endpoints.item !== false)` half of the `endpoints.item` row cited `packages/rest/src/rest-server.ts#registerMetadataEndpointsInner`. `@objectstack/rest` moved the per-item reads to `packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes` with no behaviour change, so those references follow them. The `registerPerItemRoute` half of the `endpoints.item` row keeps its citation: that call still lives in the inner method's head. + - `liveness/doc.json`: the `content` row's single-item branch, and the `_note`'s book-tree and single-item branches, cite the new module. The `doc` list branch keeps its citation. + - `liveness/book.json`: the `_note` names the new module for the `book/:name/tree` branch. + - The quoted code, the producers, the verdicts and the row notes are unchanged. +- b0a1df0: `sys_organization` now declares an `invite_user` action at `record_header`, so an organization record page can offer "Invite User" as its primary header button. It calls `/api/v1/auth/organization/invite-member` with the record's id as `organizationId`, asks for `email` and `role` (the role picker offers only the roles the caller may grant), and is gated like the other three invite mirrors: the `organization` capability and the owner / admin / delegated_admin grades. Its `order: -1` puts it first in the header. + + `leave_organization` keeps `variant: 'danger'` and now declares `order: 1`, so it sorts last among the header actions and folds into the `⋯` overflow menu instead of taking the third inline slot. Its confirmation now names what is lost: the organization's environments and members (en, zh-CN, ja-JP and es-ES). + + `@objectstack/spec` books the new action in `PUBLIC_AUTH_FEATURES.organization.gatedInputs`, beside the other three invite mirrors. + + Clause-②: no +- 2ee5861: The liveness ledger's `form.sharing` and `formViews` rows quote the code that reads their keys as it now reads + + Clause-②: no (only the two rows' evidence text changes and both statuses stay `live`: `ViewSchema` accepts `form.sharing` and `formViews` exactly as before, and no state count moves) + + - `liveness/view.json`, row `form.sharing`: the evidence quoted a `slugMatchesPublicLink(sharing.publicLink, slug)` call that no longer exists. It now cites `packages/rest/src/form-routes.ts#findPublicFormView`, which loops over `anonymousFormIntakeCandidates(view)`, and the two rules in `packages/spec/src/ui/anonymous-form-intake.ts` it reads: `anonymousFormIntakeCandidates`, which hands each form's `sharing` to the slug rule, and `anonymousFormIntakeSlug`, which reads all three keys (`enabled`, `allowAnonymous` and `publicLink`). The `resolveFormBySlug` clause now says where `publicFormGrant` is set: in the submit door, for the match that `resolveFormBySlug` returns. + - `liveness/view.json`, row `formViews`: the evidence quoted `const formViews = view.formViews`, which no longer exists. It now quotes the line of `anonymousFormIntakeCandidates` that makes every `formViews` entry a candidate. + - The verdicts, the notes and every other row are unchanged. +- e46ce7b: Clause-②: no + + fix(metadata-protocol): the global search palette titles a hit by its `nameField` before the deprecated `titleFormat` + + - **What changed.** `searchAll` (`GET /api/v1/search`) now resolves a hit's `title` in this order: the primary-title pointer (`nameField`, or the deprecated alias `displayNameField`), then the `titleFormat` template, then the conventional name fields, then first/last name, then the record id. Before, `titleFormat` came first. ADR-0079 D3 and the `titleFormat` schema description both say an explicit `nameField` takes precedence. + - **What it means for a deployment.** An object that declares both keys is now titled by its `nameField` value. For example, an approval request shows `Discount Sign-off · opp_45` instead of `flow:deal_desk · opp_45`. The registry also designates a `nameField` for any object that has a title-eligible field (a text-like field, or a formula returning text). So an object that declares only `titleFormat` is now titled by that designated field too. The template still titles a row when the object has no pointer, or when the pointer's value is empty on that row. + - **Fix for an author who wants the composite title back:** add a `formula` field with `returnType: 'text'` that builds the composite, set `nameField` to it, and drop `titleFormat`. This is the migration ADR-0079 prescribes (`os explain title-format-retired`). + - **`@objectstack/spec`:** the description of `SearchAllHitSchema.title` now states the same order. No schema shape changed. +- 1489952: docs(spec): `record:line_items` `columns` states that an identity-only column hydrates from the child object's field, as on the master-detail carriers, so its heading is the child field's label and translates with it (#24140) + + Clause-②: no + + `RecordLineItemsProps.columns` said that this block does not hydrate a column from the child object's field, so authors had to declare `label`, `type` and `options` on every column, and an identity-only `{ name }` column was headed by its field name. That contradicted the shared column contract the block takes by reference: `InlineGridColumnSchema.label` says it "defaults to the child field's label via hydration". It also left a declared column heading as a plain string that no translation bundle key reaches, so a non-English print of a line-items table read English headings. The describe now states the carriers' rule in the carriers' words. Identity-only entries (`{ name }`) hydrate everything else from the child object's fields, `label`, `type` and `options` included, so the heading is the child field's label, which translates through that field's own translation. Declaring `type` opts a column out of hydration. + + Text only: every input that parsed before parses now, and every refusal is unchanged. The renderer half is objectui's `LineItemsPanel`, which will reuse the same column hydration the master-detail grid uses. Until the console carries it, an identity-only `record:line_items` column still draws as a text cell headed by its field name, so keep explicit labels on columns you need headed today. +- 70cfb12: Clause-②: no + + fix(spec): the protocol 17 → 18 upgrade rationale states the element data-binding ruling in words instead of a tracker id + + - **What was wrong.** Two sentences of step 18's rationale, the one `os migrate meta` prints for the hop and the upgrade guide renders, cited their decision as a tracker number and a ruling letter. The sentence on the element layer's flat data-binding keys and the one on `object-grid`'s `defaultFilters` each carried that parenthetical, which tells an upgrading author nothing. + - **What changes.** Each parenthetical now says what was decided, with the ruling's date: the element layer loses its second data door while the `object-*` blocks keep their native keys, and `defaultFilters` retires together with the element layer's flat binding keys, in the shape of the `defaultSort` retirement. The rest of both sentences, every other fragment, and every conversion and migration entry are unchanged. +- 8bfb17d: Clause-②: no + + The `ActionParam` `options` docblock in `src/ui/action.zod.ts` no longer quotes objectui's inline + param branch as `options: param.options`. At the console pin (`.objectui-sha` `5e15928b4631`) that + branch of `resolveActionParam` reads `options: authoredOptions`, where `authoredOptions` is + `resolveOptionLabels(param.options, ctx.locale)`: the authored array itself when every `label` is + already a string, and otherwise only the entries with an inline per-locale `label` re-spread with + that label resolved. The docblock's point is unchanged: an inline param's options, `visibleWhen` + included, reach the widget as authored. No key, schema, export, accepted value or message text + changes. +- 4f11af5: The protocol 18 migration `page-template-closed-region-unplaced-refused` now says what the console shows for a page row stored before the refusal. Its reason used to say such a row "renders as before"; on the console that ships with 18 it does not. + + Clause-②: no + + A stored page row with an unregistered template or an unplaced region is still served unchanged, and saving it again is still refused until it is renamed. Until then the console draws a refusal in its place. An unregistered template draws no layout, only a refusal that names the template and the registered templates. On a `default`, `header-sidebar-main` or `three-column` page, an unplaced region is not drawn and is not moved into another slot. Where it used to be stacked, under `main`, a refusal names it and the slots that template places. A page that names no template takes its page type's layout, which still stacks such a region under `main`. + + The entry's acceptance criteria are unchanged: rename the template or region, and each renamed region draws in its slot. +- d537a31: docs(spec): the AI blueprint schema's composed-title example reads the lookup's name one hop through it + + The `expression` and `nameField` descriptions on `BlueprintFieldSchema` / `BlueprintObjectSchema` and their strict twins gave `"record.order_no + ' · ' + record.customer"` and `"{order_no} · {customer}"` as a composed title. `customer` is a lookup, so those compose the stored id into the title. They now give `"record.order_no + ' · ' + record.customer.name"` and say that a formula reads a lookup's related record one hop through it, that a second hop is refused, and that a formula title is shown everywhere but cannot be sorted by or searched. No schema shape changes. +- 1173017: `sys_organization` now declares a `rename_organization` action ("Rename Organization") at `record_header` and `list_item`, so an organization record page can rename the organization. It collects `name` (required) and `logo`, both pre-filled from the record, and no `slug`. It calls better-auth's `/api/v1/auth/organization/update` exactly as `update_organization` does (the record's id as `organizationId`, the fields under `data`), and it is gated on `multiOrgEnabled` like the other organization actions. Because the request carries no slug, the organization keeps its slug. It is declared before `change_slug`, so on the record header it takes the third inline slot and `change_slug` moves into the `⋯` overflow menu. + + `update_organization` (name, slug and logo, list row only) and `change_slug` are unchanged. + + `@objectstack/spec` books the new action in `PUBLIC_AUTH_FEATURES.multiOrgEnabled.gatedInputs`, beside `update_organization`. + + Clause-②: yes +- 2ce2e36: Clause-②: no + + The `forceCbpMasterDetailRequired` docblock in `src/data/object.zod.ts` no longer says that today only + the security gate (`assertControlledByParentWrite`) closes the null-master-FK shape of a + `controlled_by_parent` object. That was true before the builder's force landed, and the sentence is now + in the past tense. It also names what has refused the declaration at parse since protocol 18: + `refuseControlledByParentWithoutOneMaster` refuses a `controlled_by_parent` object none of whose + `master_detail` fields declares `required: true`. No key, schema, export, accepted value or message text + changes. +- ba5bfe9: Clause-②: no + + The `source` describe on `HistoryMetaItemResponseSchema`'s events, the body of + `GET /api/v1/meta/:type/:name/history`, no longer lists `"fs"`, `"studio"`, `"rest"`, `"ai"` and + `"git-import"` as its labels: none of the write paths in `@objectstack/metadata-protocol` whose history + rows this door reads stamps any of them. It now says what the field is: an open code-path label the + server stamps, not an enum an author or client picks from, for example `protocol.saveMetaItem`, + `protocol.publishMetaItem` or `migrate-stored`, with `sys-metadata-repo` as the repository's own default + and the answer for a row that stored none. The field stays `z.string()`, so no key, type, export or + accepted value changes, and the door answers exactly what it answered before. +- 741aa85: Clause-②: no + + The `runAutomations` and `treatAsHistorical` describes on `ImportRequestSchema` (and so `CreateImportJobRequestSchema`, the same schema) now say who may send the opt-out: only the system principal, an in-process caller such as a seed or migration job. The import doors refuse `runAutomations: false` and `treatAsHistorical: true` from every other caller, every signed-in user and administrator included, with `403 PERMISSION_DENIED` before any row is read. The `runAutomations` describe no longer tells a caller that it gets a silent bulk load by sending `false`, and the schema's example body no longer sends it. The schema shape and both defaults are unchanged. + ## 18.0.0-next.3 ### Minor Changes diff --git a/packages/spec/package.json b/packages/spec/package.json index 34b89312ee5..b0b61c8032c 100644 --- a/packages/spec/package.json +++ b/packages/spec/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/spec", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "description": "ObjectStack Protocol & Specification - TypeScript Interfaces, JSON Schemas, and Convention Configurations", "license": "Apache-2.0", "main": "dist/index.js", diff --git a/packages/triggers/trigger-api/CHANGELOG.md b/packages/triggers/trigger-api/CHANGELOG.md index 708934dd72d..388d6279a78 100644 --- a/packages/triggers/trigger-api/CHANGELOG.md +++ b/packages/triggers/trigger-api/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/trigger-api +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/triggers/trigger-api/package.json b/packages/triggers/trigger-api/package.json index 242ac0d2dea..0a91f12b525 100644 --- a/packages/triggers/trigger-api/package.json +++ b/packages/triggers/trigger-api/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/trigger-api", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Inbound HTTP/webhook flow trigger for ObjectStack — per-flow HMAC-verified endpoints with queue-backed ingestion (ADR-0041)", "main": "dist/index.js", diff --git a/packages/triggers/trigger-record-change/CHANGELOG.md b/packages/triggers/trigger-record-change/CHANGELOG.md index 080140efd25..f5be67299d2 100644 --- a/packages/triggers/trigger-record-change/CHANGELOG.md +++ b/packages/triggers/trigger-record-change/CHANGELOG.md @@ -1,5 +1,51 @@ # @objectstack/plugin-trigger-record-change +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/triggers/trigger-record-change/package.json b/packages/triggers/trigger-record-change/package.json index 89c073ca495..185f94a0601 100644 --- a/packages/triggers/trigger-record-change/package.json +++ b/packages/triggers/trigger-record-change/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/trigger-record-change", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Record-change flow trigger for ObjectStack — auto-launches flows on object insert/update/delete via ObjectQL lifecycle hooks (ADR-0018)", "main": "dist/index.js", diff --git a/packages/triggers/trigger-schedule/CHANGELOG.md b/packages/triggers/trigger-schedule/CHANGELOG.md index 2fff2c1c564..097f73df831 100644 --- a/packages/triggers/trigger-schedule/CHANGELOG.md +++ b/packages/triggers/trigger-schedule/CHANGELOG.md @@ -1,5 +1,53 @@ # @objectstack/plugin-trigger-schedule +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [b2e7870] +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [f5ef054] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [2ac0616] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/core@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/metadata-core@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/triggers/trigger-schedule/package.json b/packages/triggers/trigger-schedule/package.json index 2b56f10ab07..624e2e496a0 100644 --- a/packages/triggers/trigger-schedule/package.json +++ b/packages/triggers/trigger-schedule/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/trigger-schedule", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Schedule flow trigger for ObjectStack \u2014 auto-launches flows on a cron/interval/once schedule via the IJobService (ADR-0018)", "main": "dist/index.js", diff --git a/packages/types/CHANGELOG.md b/packages/types/CHANGELOG.md index def6d003425..9f295854b0e 100644 --- a/packages/types/CHANGELOG.md +++ b/packages/types/CHANGELOG.md @@ -1,5 +1,46 @@ # @objectstack/types +## 18.0.0-next.4 + +### Patch Changes + +- Updated dependencies [eebaf3b] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [b0a1df0] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [1489952] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [70cfb12] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [af284b0] +- Updated dependencies [14a8bd2] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [1173017] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [370833a] +- Updated dependencies [3a734e0] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [f573976] + - @objectstack/spec@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/types/package.json b/packages/types/package.json index 33b38783634..fb03860b05f 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/types", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Shared interfaces describing the ObjectStack Runtime environment", "main": "dist/index.js", diff --git a/packages/verify/CHANGELOG.md b/packages/verify/CHANGELOG.md index b5fec749c22..5c459f029af 100644 --- a/packages/verify/CHANGELOG.md +++ b/packages/verify/CHANGELOG.md @@ -1,5 +1,179 @@ # @objectstack/verify +## 18.0.0-next.4 + +### Patch Changes + +- 1eae21f: fix(verify): the dashboard date-window family also proves sums and derived measures, and says why an average stays out + + Clause-②: no (no export, option or type moves: the family reads the measures it already read, and a widget it used to exclude can now be proven or contradicted) + + `runDashboardWindowProofs` (and `os verify --dashboard-windows`) now judges every verdict in fixture rows, so it compares every measure whose difference can be read back as rows: + + - **A `sum`** is probed. Each fixture row holds the summed field to the value the CRUD derivation synthesizes for it, or to the value the widget's own selection already holds there. The sum's difference over that value is the rows the window took in. FROM: a sum-only widget was `excluded` (`no-count-measure`), and a sum beside a count was an `excluded` measure (`not-a-count`). TO: `proven` or `contradicted` like a count. A sum of a relationship path, a summed field with no synthesizable value, and a held value that is not a non-zero number are `excluded` as `count-field-unsynthesizable`, and the reason names which. + - **A `derived` measure** (`ratio`, `sum`, `difference`, `product`) is probed through its operands, which the widget's own answer carries beside it. Each operand must be a count or a sum of the same dataset, and the fixture rows meet every operand's filter. Under each preset every operand must take in exactly the rows the window selects. The value the widget shows must equal `evaluateDerivedMeasures` (the server's own evaluator, from `@objectstack/service-analytics`) over that row's operands, or the preset is `contradicted` and the detail names both values. A derived measure over a derived one, or over a measure that is not a count or a sum, stays `excluded` (`not-a-count`), and the reason names the operand. + - **An `avg`, `min`, `max` or `count_distinct`** stays `excluded` (`not-a-count`, or `no-count-measure` for a widget with nothing else), with a reason that says why. What a fixture row does to it depends on rows the app already holds in the window: their number for an average, their extreme for a minimum or maximum, their values for a distinct count. Nothing the widget answers reveals that, so deriving it would mean guessing the data. The reason text changes: FROM `"avg_effort" aggregates avg of effort, not a row count` TO `"avg_effort" averages effort: an average is not additive — …`. + - The widget-level `no-count-measure` reason reads `no measure the widget selects can be compared (…)`. The `excludedKind` values are unchanged. + - **A new `not-run` cause.** The family gives up on a preset when a sum's own total is so large that its floating-point difference cannot resolve one fixture row. That needs 2^34 times the held value, and the detail says so. + - **Upgrading:** on an app whose dashboards bind sums or derived measures, `os verify --dashboard-windows` now probes those widgets. A window that does not reach them now fails the run (`hardFailures`), where before they were only counted as `excluded`. +- d65826f: fix: framework writes on tenant-column objects name the organization they can derive (ADR-0131 D9) + + Clause-②: yes (widening) + + A write on a tenant-column object with no organization is derived under `single` and refused under a wall (ADR-0131 D9). Before the engine stops letting such writes through, each of these producers now names the organization it can derive itself. Under `single` that is the Default Organization, which is the owner the engine would derive anyway, so no reader changes. Under a wall the row names an organization only where the producer has one. The rows it cannot attribute are written exactly as before. + + - **`@objectstack/plugin-auth`.** The self-registration grant (`sys_user_permission_set`) and the auth mails about a user (verification, password reset, account invitation, magic link, change-email notice → `sys_email`) read one derivable organization: the `tenancy` service's `defaultOrgId()`. Under `single` that is the Default Organization. Under a wall it is none, because the registrant belongs to no organization yet. The verification and reset mails used to persist with no organization even under `single`. + - **`@objectstack/plugin-audit`.** An activity row (`sys_activity`) about a record with no organization of its own (a `sys_user`, a platform setting), written with no session organization (a sign-up), takes the Default Organization under `single`. The record's own organization and then the session's still win. The ledger's `tenant_id` attribution is unchanged. New optional `AuditWriterOptions.getDefaultOrganizationId()`; `AuditPlugin` wires it to the `tenancy` service. + - **`@objectstack/service-settings`.** The `sys_secret` ciphertext and the `sys_setting_audit` history row of a `tenant` or `user` value carry that value's organization, which is the same owner as its `sys_setting` row. A `global` value's rows name none. New optional `organization_id` on `SettingsSecretStore.insert`'s row and `organizationId` on `SettingsAuditWriter.write`'s entry; the built-in writers persist them. A failed `sys_setting_audit` write is now reported at `error`, not `warn`: the settings write succeeds while its history row is lost. The first report names the consequence and the fix. `buildSettingAuditWriter`'s optional logger argument keeps every sink it accepted (none, `{}`, or one with an optional `warn`) and additionally takes a sink that carries `error`. It reports through `error` when the sink has it, else through `warn`. + - **`@objectstack/verify`.** The RLS probe and position personas' grants (`sys_user_permission_set`, `sys_user_position`) belong to the organization the persona is a member of (the Default Organization under `single`). They used to be written with an explicit `organization_id: null`. A persona that belongs to no organization, or to several, names none. +- Updated dependencies [b2e7870] +- Updated dependencies [6a49efb] +- Updated dependencies [eebaf3b] +- Updated dependencies [78652c0] +- Updated dependencies [194a42d] +- Updated dependencies [bb7ccde] +- Updated dependencies [bb7ccde] +- Updated dependencies [df000d4] +- Updated dependencies [df000d4] +- Updated dependencies [646303e] +- Updated dependencies [646303e] +- Updated dependencies [e9bc60b] +- Updated dependencies [52ff308] +- Updated dependencies [d1f082d] +- Updated dependencies [f5ef054] +- Updated dependencies [0d34066] +- Updated dependencies [0c3db0f] +- Updated dependencies [8d3dd6f] +- Updated dependencies [f7af839] +- Updated dependencies [a237d7f] +- Updated dependencies [03ac4ec] +- Updated dependencies [5e97907] +- Updated dependencies [b0a1df0] +- Updated dependencies [6e43b7b] +- Updated dependencies [727091c] +- Updated dependencies [62c4441] +- Updated dependencies [f792290] +- Updated dependencies [eda6f14] +- Updated dependencies [2ee5861] +- Updated dependencies [e46ce7b] +- Updated dependencies [d702b18] +- Updated dependencies [a06f0d4] +- Updated dependencies [a06f0d4] +- Updated dependencies [a19b2a7] +- Updated dependencies [9975c26] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [2ac0616] +- Updated dependencies [5a9a80b] +- Updated dependencies [1489952] +- Updated dependencies [04fd88a] +- Updated dependencies [aaaeeac] +- Updated dependencies [8eccf51] +- Updated dependencies [8a88901] +- Updated dependencies [030a7b8] +- Updated dependencies [b1845ed] +- Updated dependencies [99e9d29] +- Updated dependencies [70cfb12] +- Updated dependencies [aa31569] +- Updated dependencies [8259ea0] +- Updated dependencies [9040945] +- Updated dependencies [8bfb17d] +- Updated dependencies [8215ff7] +- Updated dependencies [4f11af5] +- Updated dependencies [b036ac5] +- Updated dependencies [d537a31] +- Updated dependencies [1bf5f6e] +- Updated dependencies [1bf5f6e] +- Updated dependencies [d537a31] +- Updated dependencies [6ec0e12] +- Updated dependencies [2bcd406] +- Updated dependencies [2bcd406] +- Updated dependencies [58a2faa] +- Updated dependencies [e74ec56] +- Updated dependencies [58a2faa] +- Updated dependencies [2bcd406] +- Updated dependencies [e74ec56] +- Updated dependencies [af284b0] +- Updated dependencies [cbfa5e4] +- Updated dependencies [14a8bd2] +- Updated dependencies [77e9f79] +- Updated dependencies [83dfbc0] +- Updated dependencies [9822b05] +- Updated dependencies [889bf12] +- Updated dependencies [7a8733f] +- Updated dependencies [1e001c7] +- Updated dependencies [1e001c7] +- Updated dependencies [0b44840] +- Updated dependencies [e15897c] +- Updated dependencies [ef65646] +- Updated dependencies [586705d] +- Updated dependencies [d79dc48] +- Updated dependencies [1173017] +- Updated dependencies [8baeda5] +- Updated dependencies [5da2e88] +- Updated dependencies [51ad8e4] +- Updated dependencies [ec749fb] +- Updated dependencies [c4526a7] +- Updated dependencies [2ce2e36] +- Updated dependencies [584b422] +- Updated dependencies [62e2b6d] +- Updated dependencies [370833a] +- Updated dependencies [2f569a9] +- Updated dependencies [69147ab] +- Updated dependencies [3a734e0] +- Updated dependencies [6868514] +- Updated dependencies [ba5bfe9] +- Updated dependencies [31e7d0b] +- Updated dependencies [538f923] +- Updated dependencies [d65826f] +- Updated dependencies [741aa85] +- Updated dependencies [741aa85] +- Updated dependencies [db1f1fd] +- Updated dependencies [cf16a0d] +- Updated dependencies [cf540a7] +- Updated dependencies [cf540a7] +- Updated dependencies [d8e11b5] +- Updated dependencies [44a5011] +- Updated dependencies [f573976] +- Updated dependencies [f573976] +- Updated dependencies [2a1e48a] +- Updated dependencies [71fa166] +- Updated dependencies [e22defb] + - @objectstack/core@18.0.0-next.4 + - @objectstack/plugin-security@18.0.0-next.4 + - @objectstack/plugin-auth@18.0.0-next.4 + - @objectstack/objectql@18.0.0-next.4 + - @objectstack/spec@18.0.0-next.4 + - @objectstack/plugin-sharing@18.0.0-next.4 + - @objectstack/rest@18.0.0-next.4 + - @objectstack/service-settings@18.0.0-next.4 + - @objectstack/service-storage@18.0.0-next.4 + - @objectstack/plugin-audit@18.0.0-next.4 + - @objectstack/platform-objects@18.0.0-next.4 + - @objectstack/runtime@18.0.0-next.4 + - @objectstack/plugin-hono-server@18.0.0-next.4 + - @objectstack/plugin-email@18.0.0-next.4 + - @objectstack/plugin-webhooks@18.0.0-next.4 + - @objectstack/plugin-approvals@18.0.0-next.4 + - @objectstack/mcp@18.0.0-next.4 + - @objectstack/metadata@18.0.0-next.4 + - @objectstack/plugin-pinyin-search@18.0.0-next.4 + - @objectstack/service-analytics@18.0.0-next.4 + - @objectstack/service-automation@18.0.0-next.4 + - @objectstack/service-cache@18.0.0-next.4 + - @objectstack/service-datasource@18.0.0-next.4 + - @objectstack/service-job@18.0.0-next.4 + - @objectstack/service-mailbox@18.0.0-next.4 + - @objectstack/service-messaging@18.0.0-next.4 + - @objectstack/service-package@18.0.0-next.4 + - @objectstack/service-queue@18.0.0-next.4 + - @objectstack/service-realtime@18.0.0-next.4 + - @objectstack/service-sms@18.0.0-next.4 + - @objectstack/trigger-api@18.0.0-next.4 + - @objectstack/trigger-record-change@18.0.0-next.4 + - @objectstack/trigger-schedule@18.0.0-next.4 + - @objectstack/types@18.0.0-next.4 + ## 18.0.0-next.3 ### Patch Changes diff --git a/packages/verify/package.json b/packages/verify/package.json index 7d862175334..41051717dc6 100644 --- a/packages/verify/package.json +++ b/packages/verify/package.json @@ -1,6 +1,6 @@ { "name": "@objectstack/verify", - "version": "18.0.0-next.3", + "version": "18.0.0-next.4", "license": "Apache-2.0", "description": "Boot any ObjectStack app in-process and verify it through the real HTTP stack — auto-derived CRUD round-trip fidelity plus the cross-owner RLS invariant. Catches runtime regressions that static checks miss.", "type": "module", diff --git a/skills/objectstack-ai/SKILL.md b/skills/objectstack-ai/SKILL.md index 9c9f6def51d..e3a5dc0c535 100644 --- a/skills/objectstack-ai/SKILL.md +++ b/skills/objectstack-ai/SKILL.md @@ -12,7 +12,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: ai tags: agent, tool, skill, knowledge, llm, embedding, mcp --- diff --git a/skills/objectstack-api/SKILL.md b/skills/objectstack-api/SKILL.md index 74567684b73..98e6bef3c78 100644 --- a/skills/objectstack-api/SKILL.md +++ b/skills/objectstack-api/SKILL.md @@ -14,7 +14,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: api tags: rest, endpoint, auth, realtime, server --- diff --git a/skills/objectstack-automation/SKILL.md b/skills/objectstack-automation/SKILL.md index 4bf131d95d7..9b2b51fb429 100644 --- a/skills/objectstack-automation/SKILL.md +++ b/skills/objectstack-automation/SKILL.md @@ -13,7 +13,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: automation tags: flow, workflow, trigger, approval, state-machine, scheduled, webhook --- diff --git a/skills/objectstack-data/SKILL.md b/skills/objectstack-data/SKILL.md index 09f3b49c2a4..b286418484e 100644 --- a/skills/objectstack-data/SKILL.md +++ b/skills/objectstack-data/SKILL.md @@ -15,7 +15,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: data tags: object, field, validation, index, relationship, hook, schema, permission, rls, security, seed, fixture --- diff --git a/skills/objectstack-formula/SKILL.md b/skills/objectstack-formula/SKILL.md index 8eca1839f04..401c1c85876 100644 --- a/skills/objectstack-formula/SKILL.md +++ b/skills/objectstack-formula/SKILL.md @@ -13,7 +13,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x and @objectstack/formula 18.x (CEL) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: expression tags: cel, formula, predicate, condition, validation, visibility, seed-dynamic --- diff --git a/skills/objectstack-i18n/SKILL.md b/skills/objectstack-i18n/SKILL.md index f3335b6bf48..91b2b82b8e9 100644 --- a/skills/objectstack-i18n/SKILL.md +++ b/skills/objectstack-i18n/SKILL.md @@ -13,7 +13,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: i18n tags: i18n, translation, locale, l10n, bundle, coverage --- diff --git a/skills/objectstack-platform/SKILL.md b/skills/objectstack-platform/SKILL.md index bbee24c8068..328d6b36b23 100644 --- a/skills/objectstack-platform/SKILL.md +++ b/skills/objectstack-platform/SKILL.md @@ -17,7 +17,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x and @objectstack/core 18.x (Zod v4 schemas), Node 22+ metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: platform tags: project, defineStack, driver, hono, plugin, kernel, service, requires, capability, DI, lifecycle, cli, deploy, ops --- diff --git a/skills/objectstack-query/SKILL.md b/skills/objectstack-query/SKILL.md index d5a6400a665..c81c2ee11df 100644 --- a/skills/objectstack-query/SKILL.md +++ b/skills/objectstack-query/SKILL.md @@ -11,7 +11,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: query tags: query, filter, sort, paginate, aggregate, ObjectQL, full-text --- diff --git a/skills/objectstack-ui/SKILL.md b/skills/objectstack-ui/SKILL.md index 0a6dcba4ed7..3451cd5d6bb 100644 --- a/skills/objectstack-ui/SKILL.md +++ b/skills/objectstack-ui/SKILL.md @@ -18,7 +18,7 @@ license: Apache-2.0 compatibility: Requires @objectstack/spec 18.x (Zod v4 schemas) metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: ui tags: view, app, page, dashboard, report, chart, action, widget, doc --- diff --git a/skills/objectstack-upgrade/SKILL.md b/skills/objectstack-upgrade/SKILL.md index 00600a8eaca..7a2333322db 100644 --- a/skills/objectstack-upgrade/SKILL.md +++ b/skills/objectstack-upgrade/SKILL.md @@ -16,7 +16,7 @@ license: Apache-2.0 compatibility: Requires `@objectstack/spec` and `@objectstack/cli` at the TARGET major; the chain replays from the spec's `MIGRATION_SUPPORT_FLOOR`. No network access required. metadata: author: objectstack-ai - version: "18.0.0-next.3" + version: "18.0.0-next.4" domain: process tags: upgrade, migration, protocol, major, retired-keys, tombstone, conversions, validate, report ---