Skip to content

Commit ff48407

Browse files
fix(spec): in-system copy says "this system" or drops the noun, not "this environment" (cloud#3002 ruling A) (#24454)
Fixes #24406 Clause-②: no ## What this does Published copy in `packages/spec` called the running system "this environment". objectstack-ai/cloud#3002 ruling A, as the card quotes it, reads each string by its sense: - a set "made here, not from a package" reads "authored in this system"; - "the system the caller is in" drops the noun, or says "this system"; - a string that means a deployment or configuration scope keeps its word. This PR reads every non-test `this environment` hit in `packages/spec/src`: `git grep -c` answers 15 on `origin/main` `538f923cb9`, the card's sites plus the addendum's TSDoc. 11 change and 4 keep the word, each with its reason in the table. The spelling matches what the `services` PR (#24407) and the `cloud-connection` / `mcp` PR (#24413) landed. Wording only. No refusal `code`, status, kind, issue path, accept set or export moves. ## Per-string reading All sites are under `packages/spec/src/`. Line numbers are on `538f923cb9`; no line moves. | site | before | after, or kept: reason | |:--|:--|:--| | `security/permission-set-owner-binding.ts:127`, the `packageId` refusal on the save, restore and publish doors | "a set written into no package is authored in this environment, which an absent `packageId` states" | "… is authored in this system, …" (made here) | | `security/permission.form.ts:52`, Setup help text for `packageId` | "Leave empty for a set created in this environment." | "Leave empty for a set authored in this system." (made here) | | `security/permission.zod.ts:680`, the `permission.managedBy` tombstone prescription | "and a set created in this environment carries neither." | "and a set authored in this system carries neither." (made here) | | `migrations/entries/semantic/18.permission-set-managed-by-retired.ts:18`, upgrade-guide `replacement` | "a set created in this environment carries neither." | "a set authored in this system carries neither." (made here; the same sentence as the tombstone) | | `system/auth-config.zod.ts:345`, TSDoc of `AudienceConfigSchema` | "who may become a user of an app built on this environment" | "… an app built on this system" (the system the reader is in) | | `system/auth-config.zod.ts:401`, TSDoc of `posture` | "Who may become a user of this environment's apps:" | "Who may become a user of this system's apps:" (the sentence #24407 gave Setup's Authentication group) | | `system/auth-config.zod.ts:417`, describe of `posture` | "Who may self-register into this environment: invite_only …" | "Who may self-register: invite_only …" (noun dropped, as plugin-auth's self-registration refusals now read) | | `system/auth-config.zod.ts:581`, TSDoc of `audience` | "who may become a user of this environment's apps" | "who may become a user of this system's apps" | | `system/auth-config.zod.ts:601`, describe of `audience` | "Audience posture: who may self-register into this environment (…)" | "Audience posture: who may self-register (…)" (noun dropped) | | `api/protocol.zod.ts:3373`, describe of `AiModelsResponse.models` | "Models this environment offers" | "Models this system offers" (the system the caller asks; it also holds for the adapter `listModels()` fallback shape, which has no plan behind it) | | `contracts/metadata-service.ts:393`, TSDoc of `IMetadataService.listDiagnosed` | a consumer that renders "this environment contains N items" | "this system contains N items" (a rendering inside the running system) | | `data/seed-loader.zod.ts:265`, TSDoc of `env` | "Only datasets matching this environment are loaded." | kept: names the deployment tier. `env` is the `prod`, `dev` or `test` enum, resolved from `config.env` or `NODE_ENV` (`metadata-protocol` `SeedLoader.resolveEnvConfig`) | | `data/seed-loader.zod.ts:269`, describe of `env` | "Only load datasets matching this environment" | kept: the same key and the same deployment tier | | `migrations/entries/semantic/18.package-uninstall-environment-wide.ts:21`, upgrade-guide `replacement` | "the uninstall removes every row bound to the package in this environment" | kept: names the ADR-0131 D6 environment-wide scope, against the organization scope the retired `organizationId` / `allTenants` keys named. The entry id and its `acceptanceCriteria` say "environment-wide", and so does the runtime's own remedy for a write sent with an organization | | `system/environment-artifact.zod.ts:155`, TSDoc of the consent-record key | "no consent record exists for this environment" | kept: the environment artifact is the per-deployment envelope the control plane assembles, and the consent record it carries is per deployment | ## Generated outputs, by their generators only - `content/docs/references/{api/protocol,security/permission,system/auth-config}.mdx` come from `pnpm --filter @objectstack/spec gen:docs`, after `check:generated` named `check:docs` as the one stale artifact (1 of 14). - `packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts` comes from `node scripts/check-i18n-bundles.mjs --write`, after `pnpm check:i18n` reported `platform-objects DRIFTED (1)`. One line moves, the `packageId` help text. The `zh-CN`, `ja-JP` and `es-ES` bundles are untouched, and no key is added or dropped. No `packages/platform-objects` source file is edited. ## Tests and pins No test in the repository pins any of the old sentences: `git grep -F` over the tree finds each only in its source, the generated pages and released changelogs. The tombstone test `permission-set-managed-by-retirement.test.ts` pins the prescription with a regex whose `.*` spans the changed clause, so no pin needed re-truing. ## Changeset `.changeset/24406-spec-this-system-copy.md` is a `patch` for `@objectstack/spec` and `@objectstack/platform-objects`. Both publish: neither sets `private`, and `src/**/*.zod.ts`, `dist` and the generated bundle ship. ## Verification Every reading below is at HEAD `9fcf38b94b`, the final commit, on base `538f923cb9`. Each exit code was captured before any pipe. - `pnpm --filter @objectstack/spec build`, through `scripts/pm/os-verify-lock.sh`: `VERDICT command-exit 0`. - `pnpm --filter @objectstack/spec check:generated`: "✓ All 14 generated artifacts are up to date". Before `gen:docs` it read "✗ 1 of 14 artifact(s) stale: content/docs/references/**". - `pnpm --filter @objectstack/spec test`: `Test Files 697 passed (697)`, `Tests 21267 passed | 1 todo (21268)`, exit 0. - `pnpm --filter @objectstack/spec typecheck`: exit 0. - `pnpm --filter @objectstack/platform-objects test`: `Test Files 77 passed (77)`, `Tests 1184 passed (1184)`, exit 0. - `pnpm --filter @objectstack/platform-objects typecheck`: exit 0, with "check:test-typecheck: OK". This was taken after `formula`, `lint` and `core` were rebuilt with declarations. - `pnpm check:i18n`: before the regeneration, exit 1 with "platform-objects DRIFTED (1)". After it, exit 0 with "OK (10 package(s) — all bundles in sync, no undeclared authoring keys)". - The derived gate families come from `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`: 109 for this diff. The `--ran` reconciliation answers "109 derived famil(ies) accounted for — 107 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)". 106 exited 0. - NOT MEASURED: `check:skill-examples` and `check:dual-build-cjs-loads` exited 3 (PREREQUISITE NOT MET). They read the declarations or built output of packages outside this diff's build closure, such as `client-react` and `studio`. CI's required jobs run them on a full build. - `check:dts-closure` exited 1, naming 53 packages with no `.d.ts`. Every one of them is a package this worktree built JS-only (`OS_SKIP_DTS=1`) so the i18n generator's CLI could run, so the reading describes the local tree, not this diff. CI measures it on its own full build. - No ablation and no reverse verification: the diff changes copy only, and no test pins the old sentences. ## Acceptance notes These were read and are not this card's to move, being outside the census or outside this lane. Carrier for each: none. - `packages/client/src/index.ts:6702`, the TSDoc of `ai.models()`, and `content/docs/api/plugin-endpoints.mdx:112`, a hand-written page listed in `scripts/docs-audit/handwritten-docs.json`, still say "Models this environment offers". They mirror the describe this PR moves. - `packages/metadata/src/metadata-manager.ts:1024` carries the same "this environment contains N items" rationale as the `IMetadataService` TSDoc, in a comment. - `security/permission-set-owner-binding.ts:103`, on the build and register doors, says "environment-authored". It is outside the literal census, and "environment-authored" is the documented name for that class of metadata (`content/docs/permissions/*.mdx`, `content/docs/deployment/install-modes.mdx`). So it keeps its word, as "the environment catalog" did in the sibling PRs. - `api/protocol.zod.ts:3359` ("the environment's plan-filtered picker list") and `:3371` ("Whether this is the environment default") are outside the literal census. They name the deployment's plan-scoped allowlist and its configured default. `:3359` cites `ADR-0028`, which in this registry is the metadata-naming ADR, not a model allowlist. The record it means is probably a cloud one, which AGENTS.md cites as `cloud ADR-NNNN`. - The released `CHANGELOG.md` entries and consumed `.changeset/pre/*` files keep the old sentences. They are release-owned. --- _Generated by [Claude Code](https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent ec951e2 commit ff48407

12 files changed

Lines changed: 34 additions & 17 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
---
2+
'@objectstack/spec': patch
3+
'@objectstack/platform-objects': patch
4+
---
5+
6+
Clause-②: no
7+
8+
fix(spec): published copy that names the running system says "this system", or drops the noun, not "this environment"
9+
10+
- **What changed.** Inside the running system, user-facing copy names the system the reader is in as "this system", or drops the noun. A permission set made here, not shipped by a package, is "authored in this system".
11+
- The permission-set `packageId` refusal on the save, restore and publish doors now reads "a set written into no package is authored in this system, which an absent `packageId` states".
12+
- The permission set's `packageId` help text in Setup ends "Leave empty for a set authored in this system." The English Setup forms bundle of `@objectstack/platform-objects` is regenerated from it.
13+
- The `permission.managedBy` tombstone prescription, and the `permission-set-managed-by-retired` protocol-18 upgrade-guide entry, say "a set authored in this system carries neither".
14+
- The `AudienceConfig.posture` and `AuthConfig.audience` describes say "who may self-register", with no noun. Their TSDoc says "who may become a user of this system's apps", the sentence the Setup Authentication group already reads.
15+
- `AiModelsResponse.models` describes "Models this system offers". The `IMetadataService.listDiagnosed` TSDoc example reads "this system contains N items".
16+
- **Kept "environment", where the word names a deployment or configuration scope.** `SeedLoaderConfig.env` ("Only load datasets matching this environment", the `prod | dev | test` tier), the `package-uninstall-environment-wide` upgrade-guide entry (the ADR-0131 environment-wide scope, as against an organization), and the environment artifact's consent-record TSDoc.
17+
- **Unchanged.** Every error code, status, issue path, accepted value and export. Only the human-readable text moves.

‎content/docs/references/api/protocol.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ const result = AiAgentCapabilitiesSchema.parse(data);
207207

208208
| Property | Type | Required | Description |
209209
| :--- | :--- | :--- | :--- |
210-
| **models** | `(string \| { id: string; label: string; default: boolean })[]` | ✅ | Models this environment offers |
210+
| **models** | `(string \| { id: string; label: string; default: boolean })[]` | ✅ | Models this system offers |
211211
| **defaultModel** | `string` | optional | Default model id, when the service reports one |
212212

213213
### Nested Shape: `AiModelsResponse.models[number]`

‎content/docs/references/security/permission.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ const result = AdminScopeSchema.parse(data);
118118
| **label** | `string` | optional | Display label |
119119
| **description** | `string` | optional | Human-readable description shown in Setup |
120120
| **packageId** | `string` | optional | [ADR-0086 D3] Owning package id for a package-shipped set (absent = env-authored); a value that disagrees with the package that ships the set, or that it is saved into, is refused by the stack schema (defineStack, os validate, os build), the package registration, and the metadata save, restore and publish doors |
121-
| **managedBy** | `never` | optional | [REMOVED] `permission.managedBy` was removed in @objectstack/spec 18 (ADR-0049 enforce-or-remove, ADR-0086 D3) — nothing ever read the authored key: no seeder, package upgrade or lock consulted it, so `package` never made a set upgrade-owned and `platform` or `user` never shielded one from an upgrade. Delete the key; a set's provenance is the package that ships it, decided from that package's artifact and recorded as `packageId`, and a set created in this environment carries neither. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
121+
| **managedBy** | `never` | optional | [REMOVED] `permission.managedBy` was removed in @objectstack/spec 18 (ADR-0049 enforce-or-remove, ADR-0086 D3) — nothing ever read the authored key: no seeder, package upgrade or lock consulted it, so `package` never made a set upgrade-owned and `platform` or `user` never shielded one from an upgrade. Delete the key; a set's provenance is the package that ships it, decided from that package's artifact and recorded as `packageId`, and a set authored in this system carries neither. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
122122
| **isDefault** | `boolean` | optional (default: `false`) | [ADR-0090 D5] App baseline for the everyone position: app-level sets are auto-bound at boot (guarded, idempotent); package-level sets become install-time suggestions an admin confirms |
123123
| **objects** | `Record<string, { allowCreate?: boolean; allowRead?: boolean; allowEdit?: boolean; allowDelete?: boolean; … }>` | ✅ | Entity permissions |
124124
| **fields** | `Record<string, { readable?: boolean; editable?: boolean }>` | optional | Field level security |

‎content/docs/references/system/auth-config.mdx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ Advanced / low-level Better-Auth options
5252

5353
| Property | Type | Required | Description |
5454
| :--- | :--- | :--- | :--- |
55-
| **posture** | `Enum<'invite_only' \| 'email_domain' \| 'open'>` | optional (default: `"invite_only"`) | Who may self-register into this environment: invite_only (default — operator acts only), email_domain (allowlisted email domains), or open (anyone). email_domain forces email verification on; open forces it on unless the deployment explicitly sets emailAndPassword.requireEmailVerification to false. |
55+
| **posture** | `Enum<'invite_only' \| 'email_domain' \| 'open'>` | optional (default: `"invite_only"`) | Who may self-register: invite_only (default — operator acts only), email_domain (allowlisted email domains), or open (anyone). email_domain forces email verification on; open forces it on unless the deployment explicitly sets emailAndPassword.requireEmailVerification to false. |
5656
| **allowedEmailDomains** | `string[]` | optional | Email domains admitted to self-register under posture email_domain (exact, case-insensitive match; subdomains need their own entries). Required non-empty for email_domain; refused under other postures. |
5757
| **selfRegistrationPermissionSet** | `string` | optional | Name of the permission set granted to each self-registrant. Required when posture is email_domain or open; refused for invite_only. admin_full_access is refused. |
5858

@@ -77,7 +77,7 @@ Advanced / low-level Better-Auth options
7777
| **oidcProviders** | `{ providerId: string; name?: string; discoveryUrl?: string; issuer?: string; … }[]` | optional | List of OIDC/OAuth2 providers for enterprise SSO. Product or enterprise packages can pass this directly or contribute it through auth:configure. |
7878
| **emailAndPassword** | `{ enabled?: boolean; disableSignUp?: boolean; requireEmailVerification?: boolean; minPasswordLength?: number; … }` | optional | Email and password authentication options forwarded to better-auth |
7979
| **emailVerification** | `{ sendOnSignUp?: boolean; sendOnSignIn?: boolean; autoSignInAfterVerification?: boolean; expiresIn?: number }` | optional | Email verification options forwarded to better-auth |
80-
| **audience** | `{ posture?: Enum<'invite_only' \| 'email_domain' \| 'open'>; allowedEmailDomains?: string[]; selfRegistrationPermissionSet?: string }` | optional | Audience posture: who may self-register into this environment (invite_only — the default — \| email_domain \| open). See AudienceConfigSchema. |
80+
| **audience** | `{ posture?: Enum<'invite_only' \| 'email_domain' \| 'open'>; allowedEmailDomains?: string[]; selfRegistrationPermissionSet?: string }` | optional | Audience posture: who may self-register (invite_only — the default — \| email_domain \| open). See AudienceConfigSchema. |
8181
| **advanced** | `{ crossSubDomainCookies?: object; useSecureCookies?: boolean; disableCSRFCheck?: boolean; cookiePrefix?: string }` | optional | Advanced / low-level Better-Auth options |
8282
| **ssoOnlyMode** | `boolean` | optional | SSO-only login: hide the local password form + self-registration (the break-glass password endpoint stays enabled) |
8383
| **mutualTls** | `{ enabled?: boolean; clientCertRequired?: boolean; trustedCAs: string[]; crlUrl?: string; … }` | optional | Mutual TLS (mTLS) configuration |
@@ -169,7 +169,7 @@ OIDC / Generic OAuth2 provider configuration for enterprise SSO
169169

170170
| Property | Type | Required | Description |
171171
| :--- | :--- | :--- | :--- |
172-
| **posture** | `Enum<'invite_only' \| 'email_domain' \| 'open'>` | optional (default: `"invite_only"`) | Who may self-register into this environment: invite_only (default — operator acts only), email_domain (allowlisted email domains), or open (anyone). email_domain forces email verification on; open forces it on unless the deployment explicitly sets emailAndPassword.requireEmailVerification to false. |
172+
| **posture** | `Enum<'invite_only' \| 'email_domain' \| 'open'>` | optional (default: `"invite_only"`) | Who may self-register: invite_only (default — operator acts only), email_domain (allowlisted email domains), or open (anyone). email_domain forces email verification on; open forces it on unless the deployment explicitly sets emailAndPassword.requireEmailVerification to false. |
173173
| **allowedEmailDomains** | `string[]` | optional | Email domains admitted to self-register under posture email_domain (exact, case-insensitive match; subdomains need their own entries). Required non-empty for email_domain; refused under other postures. |
174174
| **selfRegistrationPermissionSet** | `string` | optional | Name of the permission set granted to each self-registrant. Required when posture is email_domain or open; refused for invite_only. admin_full_access is refused. |
175175

‎packages/platform-objects/src/apps/translations/en.metadata-forms.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2334,7 +2334,7 @@ export const enMetadataForms: NonNullable<TranslationData['metadataForms']> = {
23342334
},
23352335
packageId: {
23362336
label: "Package Id",
2337-
helpText: "The package that ships this set; a value naming any other package is refused. Leave empty for a set created in this environment."
2337+
helpText: "The package that ships this set; a value naming any other package is refused. Leave empty for a set authored in this system."
23382338
},
23392339
systemPermissions: {
23402340
label: "System Permissions",

‎packages/spec/src/api/protocol.zod.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3370,7 +3370,7 @@ export const AiModelsResponseSchema = lazySchema(() => z.object({
33703370
label: z.string().describe('Display label for the picker'),
33713371
default: z.boolean().describe('Whether this is the environment default'),
33723372
}),
3373-
])).describe('Models this environment offers'),
3373+
])).describe('Models this system offers'),
33743374
defaultModel: z.string().optional().describe('Default model id, when the service reports one'),
33753375
}));
33763376

‎packages/spec/src/contracts/metadata-service.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ export interface IMetadataService {
390390
* read a MISS and an OUTAGE arrive as the same `undefined`; on the plural
391391
* read they arrive as the same *array*, and an array carries a **count**.
392392
* A count is the strongest positive claim a read can make — a consumer that
393-
* renders `list(t).length` as "this environment contains N items" states,
393+
* renders `list(t).length` as "this system contains N items" states,
394394
* positively and numerically, something no read established. `list` cannot
395395
* be fixed in place (every caller would have to change at once, and most
396396
* are right to keep ignoring the verdict), so the verdict is offered

‎packages/spec/src/migrations/entries/semantic/18.permission-set-managed-by-retired.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export const entry: SemanticMigration = {
1515
surface: 'permissions[].managedBy — the authored per-record provenance (package / platform / user) on a '
1616
+ 'permission set',
1717
replacement: 'Nothing: delete the key. A set\'s provenance is the package that ships it, decided from that '
18-
+ 'package\'s artifact and recorded as packageId; a set created in this environment carries neither. The '
18+
+ 'package\'s artifact and recorded as packageId; a set authored in this system carries neither. The '
1919
+ 'sys_permission_set.managed_by column the platform keeps is a different surface and is unchanged.',
2020
reason: 'The D2 conversion `permission-set-managed-by-removed` deletes `managedBy` from every permission set '
2121
+ 'in author sources, stored permission rows and built artifacts, and the delete is lossless: no seeder, '

‎packages/spec/src/security/permission-set-owner-binding.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ export function judgePermissionSetOwnerBinding(
124124
: `, or ${door === 'save' ? 'save the set' : 'save it'} into package '${declared}' `
125125
+ `(?package=${declared} on the REST door, packageId in process)`;
126126
const owner = bound === null
127-
? 'a set written into no package is authored in this environment, which an absent `packageId` states'
127+
? 'a set written into no package is authored in this system, which an absent `packageId` states'
128128
: 'the package a set is written into is its owner';
129129
const tail = `and ${owner} (ADR-0086 D3): a \`packageId\` that disagrees is refused, never silently overridden.`;
130130
const message = door === 'save'

‎packages/spec/src/security/permission.form.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export const permissionForm = defineForm({
4949
// (`judgePermissionSetOwnerBinding`).
5050
{ field: 'description', type: 'textarea', colSpan: 2, helpText: 'Human-readable description shown in Setup.' },
5151
{ field: 'isDefault', type: 'boolean', colSpan: 1, helpText: 'Gives this set to everyone who signs in, through the built-in everyone position. A set the app itself declares is granted at startup; a set from an installed package becomes a suggestion an admin confirms in Setup. A set with high-privilege permissions is never granted this way. Default false.' },
52-
{ field: 'packageId', type: 'text', colSpan: 2, helpText: 'The package that ships this set; a value naming any other package is refused. Leave empty for a set created in this environment.' },
52+
{ field: 'packageId', type: 'text', colSpan: 2, helpText: 'The package that ships this set; a value naming any other package is refused. Leave empty for a set authored in this system.' },
5353
],
5454
},
5555
{

0 commit comments

Comments
 (0)