You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 4bb4877
Browse filesBrowse the repository at this point in the historyBrowse files
fix(spec): the liveness ledger grades an app page's `record` binding `live`, citing the console reader for each of its keys
6
+
7
+
Clause-②: no
8
+
Metadata-protocol: none
9
+
10
+
-`liveness/page.json` graded `record` and every key under it `planned`, with the note that nothing read a page-level `record` yet. The console now reads it: an app page's binding is read once per page open as the first row under `sort`, and every component's `visibleWhen` sees that row as `record`. `record` and every key under it (`object`, `view`, `filter` with `field` / `operator` / `value`, `sort` with `field` / `order`) are now `live`, each citing the code that reads it.
11
+
- Nothing to do on upgrade: only the shipped `liveness/page.json` grades and evidence text change. No key, schema shape or export changes.
fix(plugin-audit,platform-objects): the audit log's default list shows what people did, not the rows the platform writes beside it; the audit objects read in Chinese
7
+
8
+
Clause-②: no
9
+
Metadata-protocol: none
10
+
11
+
Setup's 审计日志 opens the first `sys_audit_log` list view, `recent`. One sign-in used to put two rows there (the `login` row and an `update` of the person's `sys_user` stamping `last_login_at` / `last_login_ip`), the first sign-in through the console a third (the token-signing key in `sys_jwks`), and one settings change three or four (the `config_change` row, the stored setting, a secret's ciphertext, and the settings trail's own entry).
12
+
13
+
- The ledger writer now marks the sign-in stamp row with `metadata: {"event":"auth.last_login_stamp"}`, the way `config_change` rows carry `settings.set`. Only an update of `sys_user` whose changes are the stamp's fields alone is marked; an administrator's edit of a user is not. The row is still written.
14
+
-`recent` leaves out the marked row, the rows on `sys_secret`, `sys_setting_audit`, `sys_platform_setting_audit`, `sys_jwks`, `sys_migration` and `sys_migration_journal`, and the rows on `sys_setting` / `sys_platform_setting` other than `config_change`. Nothing is removed from storage, and `all_events` (全部) still lists every row.
15
+
-`sys_platform_setting` and `sys_platform_setting_audit` were in no translation bundle, so they read English in every locale; they now ship in `@objectstack/platform-objects`' bundles with zh-CN translations (ja-JP and es-ES carry the English fill). `sys_comment_reaction`'s zh-CN bundle is translated.
fix: the Business Unit page and the `security-private-no-readscope` hint say what needs the enterprise edition
7
+
8
+
Clause-②: no
9
+
Metadata-protocol: none
10
+
11
+
-**Business Unit page (`sys_business_unit` description, en and zh-CN).** It used to say units are "used to scope data and reports". It now says what a unit does in the open edition: sharing rules can share records with a unit, or a unit and the units under it, and members get that access and lose it when they leave. It also says that letting a permission set read every record of a unit (read scope `unit`) needs the enterprise edition. That matches the compile refusal, which is unchanged.
12
+
-**Business unit Timezone help (en and zh-CN).** It used to say date boundaries are computed in this zone. Nothing reads the column, so the help now says it changes no date calculation and that Settings → Localization → Default timezone is the one in effect, as the organization's Timezone help already does.
13
+
-**`security-private-no-readscope` hint (`os validate`, `os lint`).** The hint now offers only the scopes the stack can compile. When `requires` lists `hierarchy-security`, nothing changes: it offers `own_and_reports`, `unit`, `unit_and_below` and `org`. Without it, the hint offers `org` (or widening the object's `sharingModel`), and says that the three hierarchy scopes need `requires: ['hierarchy-security']`, which `@objectstack/security-enterprise` provides. The rule still fires on the same grants at the same severity.
feat(cloud-connection): `GET /api/v1/marketplace/install-local/:manifestId/sample-data` answers how many of an installed app's sample records a purge would delete now
6
+
7
+
Clause-②: yes (widening)
8
+
Metadata-protocol: none
9
+
10
+
-**New read-only route.**`GET /api/v1/marketplace/install-local/:manifestId/sample-data` answers `{ success: true, data: { manifestId, count, unidentified } }`. `count` is the number of rows `POST …/purge-sample-data` would delete if it ran now, found by the purge's own matcher with nothing deleted. A record a user added is not counted, and neither is a sample record whose key field (its dataset's `externalId`) was edited, because the purge deletes neither. A sample record edited only in other fields is still a sample row: the purge deletes it and the count includes it. `unidentified` counts the sample records the purge would report in `errors` and leave in place (no usable key value, or a key two rows carry). Before this, the count was known only from the install answer's `data.seeded`, so after a reload no API could say how much sample data was left; the installed-apps listing's `withSampleData` says only whether any is left.
11
+
-**Same rules as the purge.** It requires `manage_metadata`, answers `404` for a package that is not installed and `422 OS_PROTOCOL_INCOMPATIBLE` for an entry this runtime refused to load, and counts in the purge's scope (the caller's active organization under an organization wall, every row without one). A walled session with no active organization gets `count: 0`, the same answer as the listing's `withSampleData: false` for that session. A package with no seed datasets gets `count: 0`. With no data engine or metadata service it answers `500 DRIVER_UNAVAILABLE`, never `0`.
12
+
-**It cannot delete.** It is a `GET` and accepts no query parameter: any parameter (`dryRun`, `confirm`, …) is refused with `400 VALIDATION_ERROR` before any row is read.
13
+
-**Unchanged.** What the purge deletes, its permission and its answer; the listing's `withSampleData`.
14
+
-`@objectstack/cloud-connection` now depends on `@objectstack/rest`, for the shared closed-query-set refusal message.
fix(spec)!: a SQLite datasource's `config.filename` must name a database: `''` and a name that starts with `:` without being exactly `:memory:` are refused by the stack parse and the metadata save door, not only by the datasource admin doors
7
+
8
+
Clause-②: no (narrowing) — `SqliteConfigSchema.filename` and `SqliteWasmConfigSchema.filename` refuse `''` and any `:`-prefixed value other than exactly `:memory:`, so `defineStack` / `os validate` / `os build` and `PUT /api/v1/meta/datasource/NAME` refuse a `sqlite` / `sqlite-wasm` datasource they accepted before. Nothing is widened.
9
+
Metadata-protocol: narrowed
10
+
Skills-docs: none — `skills/**` teaches no SQLite `filename` value, and `content/docs/**` teaches only `:memory:` and file paths, which stay accepted. The prescription ships in the refusal text, the FROM → TO below and the upgrade-guide entry.
**BREAKING** accept-set narrowing on `SqliteConfigSchema.filename` and `SqliteWasmConfigSchema.filename` (`@objectstack/spec`), and so at every door that judges a datasource against the spec: `defineStack`, `os validate`, `os build` and `PUT /api/v1/meta/datasource/NAME`.
15
+
16
+
-**Before.** Both schemas typed `filename` as any placeholder-free string. `:memory:/srv/data/parts.db` (the New datasource dialog's prefilled `:memory:` with a path typed after it) and `''` parsed. Every layer reads a name that starts with `:` as fileless, so no file was ever opened: a `sqlite` datasource opened it as a relative path whose directory does not exist and every statement failed, and a `sqlite-wasm` one opened a throwaway in-memory database whose writes were gone at the next connect. The datasource admin doors (Test connection, create, update) already refused both; a datasource declared in a stack or saved through the metadata door was stored anyway, and the admin door's update would not take it back.
17
+
-**Now.** The parse answers one issue at `filename` (`config.filename` on a datasource, `datasources.N.config.filename` in a stack) that names the value, says why and prescribes the fix: `'…' is neither ':memory:' nor a file path. A name that starts with ':' is not read as a file, so this datasource would store nothing: use exactly ':memory:' …`, or `'' is empty and names no database file. …`. `defineStack` raises it as `STACK_SCHEMA_INVALID` (422); the metadata door answers `422 INVALID_METADATA`.
18
+
-**Unchanged.** Exactly `:memory:`, an absent `filename` (still defaulting to `:memory:`), and relative, absolute, Windows-drive and `file:` paths parse as before. Whether a path can be opened or created is still judged only at runtime, by the datasource admin doors; the schema cannot touch the file system.
19
+
-**`@objectstack/service-datasource`.** The admin doors give the same answers as before, word for word. The two arms the string alone decides now come from the driver-contract parse they already run, so `sqlite-database-filename.ts` keeps only its filesystem arm: one definition, in the spec.
20
+
-**`@objectstack/spec`.** The upgrade-guide entry `sqlite-filename-names-no-database-refused` (a semantic migration: no automatic rewrite, because whether the author meant an ephemeral database or the path typed after the prefix is a judgement about the datasource).
21
+
22
+
**FROM → TO.**`config: { filename: ':memory:/srv/data/parts.db' }` → `filename: '/srv/data/parts.db'` to keep the data in that file, or `filename: ':memory:'` for an ephemeral database; `filename: ''` → `':memory:'` or a file path.
fix(spec): `PackageInstallResponseSchema` declares the installed row that `POST /api/v1/packages` really answers
6
+
7
+
Clause-②: yes (narrowing)
8
+
Metadata-protocol: none
9
+
10
+
**BREAKING** accept-set narrowing at the install answer contract (`@objectstack/spec`): `PackageInstallResponseSchema` refuses the `{ package }` wrapper it accepted, and `PackageInstallResponse['data']` no longer declares `package`, `dependencyResolution`, `namespaceConflicts` or `message`.
11
+
12
+
`POST /api/v1/packages` answers `{ success: true, data: <row> }` with status `201`, where the row is the `InstalledPackage` the registry recorded. `PackageInstallResponseSchema` (the output `PackageApiContracts.installPackage` binds) declared `data: { package, dependencyResolution?, namespaceConflicts?, message? }` instead. No surface sent that wrapper, so code written against the contract read `data.package` and got `undefined`. The first-party client already followed the wire: `client.packages.install` resolves to `InstalledPackage`.
13
+
14
+
-**FROM**`data: { package: InstalledPackage, dependencyResolution?, namespaceConflicts?, message? }`**TO**`data: InstalledPackage`. The name, the type names (`PackageInstallResponse`, `PackageInstallResponseParsed`) and the binding are unchanged. The server's answer is unchanged.
15
+
-**Both directions.** The schema now accepts the bare row the door serves, which it refused, and refuses the `{ package }` wrapper no server sends. The type `PackageInstallResponse['data']` loses `package`, `dependencyResolution`, `namespaceConflicts` and `message`, so TypeScript code that reads them stops compiling. That code read `undefined` at runtime already.
16
+
-**Fix for a consumer:** read the row from `data` itself. Replace `response.data.package` with `response.data`. Drop any read of `data.dependencyResolution`, `data.namespaceConflicts` or `data.message` on this route: the door never sent them.
17
+
-**Level.**`patch`: this is the shape of an API answer, not anything a metadata author writes.
18
+
19
+
<!-- adr-0087: not-required (no-migration-prescription) an API answer shape, not metadata: no stored row or authored source holds it, so objectstack migrate meta has nothing to rewrite. The consumer line above is for integrator source code, which the compiler now flags (ADR-0087 D8); type-surface-only is closed to it only because the schema lives in packages/spec -->
fix(objectql, metadata-protocol, cloud-connection)!: a declared `.custom` action override is keyed and judged the same way at every door, and only its declared base's handler slots beneath it
8
+
9
+
Clause-②: yes (narrowing) — the protocol door (save and publish) now refuses a declared action override whose `objectName` is not the base action's, with `403 NOT_OVERRIDABLE` and rule `declared_package_ships_no_such_item`, the refusal `os validate` and the managed install already gave it; the save door used to admit it. The managed install now admits a re-install of the pair whose override relaxes an earlier tightening back to the base's own gates, which it used to refuse with `declared_action_widens_access` while the save door admitted it. Every other input answers as before.
10
+
Metadata-protocol: none
11
+
Skills-docs: follow-up #30505 — the declared action override's docs card; it owes one sentence that an override keeps the base action's `objectName`.
12
+
13
+
<!-- adr-0087: not-required (no-migration-prescription) No key of any schema is added, removed, renamed or re-shaped: the action schema and the `overrides` declaration keep their shapes. What moves is which door admits one declared override shape, so all three doors give one answer, and the refusal carries its own remedy; nothing at rest is rewritten. -->
14
+
15
+
**BREAKING** accept-set narrowing at the protocol door (`@objectstack/metadata-protocol`, ADR-0146 D2/D9): a declared action override names its base's action by `<objectName>:<name>`, the identity `os validate` and the managed install key it on.
16
+
17
+
-**Before.** The save and publish doors matched a declared action override's base by name alone, so an override in the base's `.custom` pair could move the base's action to another object. Its handler was not rebound there, and `os validate` and the install door refused the same body.
18
+
-**Now.** The save door refuses it as the other two doors do: the base ships no action of that name on that object. Keep the base action's `objectName` in the override, or remove `overrides` and add the action under the pair's own name.
19
+
20
+
`@objectstack/cloud-connection`: the managed install judges a declared action override against the installed base's code artifact (`SchemaRegistry#getArtifactItem`), as the save door does. The protocol's per-package listing it read before serves an armed override under the base's envelope, so a re-install of the pair was judged against its own previous version.
21
+
22
+
`@objectstack/objectql`: `ObjectQL#registerActionOverride` takes the package the override declares (`overrides.package`) as an optional fifth argument, and the authored-action resync passes it. `registerAction` under a key such an override holds slots the handler beneath it only when the registry's code artifact of the action belongs to that package, on that object; any other registration, one with no package included, replaces the override and is warned like every other handler collision. Before, any other package's registration slotted beneath it, so a row of another base's pair bound before the real base registered kept running after the real base's handler arrived, while every read served the real base's action; and a registration with no package slotted beneath it at `debug`, where the method's docblock promises a warning.
|**message**|`string`| optional | Installation status message |
493
+
|**manifest**|`{ id: string; namespace?: string; defaultDatasource?: string; version: string; … }`| ✅ | Package manifest at the AUTHORING stage; a row installed by a `defineStack()` host carries the assembled body instead — see `AssembledInstalledPackageSchema` / `InstalledPackageAtEitherStageSchema`|
494
+
|**source**|`Enum<'code' \| 'marketplace' \| 'authored'>`| ✅ | Where the installed package came from: code (registered by the deployment's own code), marketplace (installed from the marketplace), or authored (a database-authored package) |
0 commit comments