Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .changeset/system-identifier-docblock-truth.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
---
"@objectstack/spec": patch
---

docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)

Prose truth restoration. No regex change, no accept-set change, no `.describe()`
change — the diff is comment lines only.

The docblock claimed eleven consuming surfaces ("Applies to all metadata that
acts as a machine identifier": object names, field names, role names, permission
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
option values, workflow names, webhook names). The per-surface census on #12245 —
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
and event keys went to the sibling `EventNameSchema`, since retired unbound
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
— the readers being corrected are the ones working in this source, which is the
reason the card was filed: an AI generator reads this docblock as authority on
where the grammar applies, and a docblock governing one surface while claiming
eleven is a false map of the contract.

The rewritten docblock states, for today's tree:

- The **whole** bound list, as a table: `SelectOptionSchema.value`
(`data/field.zod.ts`, the one surface with a real authored population, reused by
the form-view option list via `SelectOptionSchema.shape`), plus three
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
in declaration with nothing authoring them ("nothing to census", not "censused
clean").
- Where the ten unbound surfaces are **actually** validated, so a reader who came
here for the object-name rule leaves with the right file: the inline
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
`BulkDataEventType` enums, which are not a grammar at all.
- That these are **different accept sets**, not looser spellings of one another,
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
named (`a.`, `a..b`, `a.1b`, `a._b`).
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
1218 authored select option values contain one.

The `Event keys | dot.notation` row of the naming-convention table and the
`'order.created' (for events)` example both asserted a binding that no longer
exists; both now state what is true. The storage-owned length-ceiling note
(#12144) is carried through unchanged.
85 changes: 62 additions & 23 deletions packages/spec/src/shared/identifiers.zod.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,33 +5,70 @@ import { z } from 'zod';
/**
* System Identifier Schema
*
* Universal naming convention for all machine identifiers (API Names) in ObjectStack.
* Enforces lowercase with underscores or dots to ensure:
* - Cross-platform compatibility (case-insensitive filesystems)
* - URL-friendliness (no encoding needed)
* - Database consistency (no collation issues)
* - Security (no case-sensitivity bugs in permission checks)
*
* **Applies to all metadata that acts as a machine identifier:**
* - Object names (tables/collections)
* - Field names
* - Role names
* - Permission set names
* - Action/trigger names
* - Event keys
* - App IDs
* - Menu/page IDs
* - Select option values
* - Workflow names
* - Webhook names
*
* A lowercase machine-identifier grammar: starts with a letter, then letters,
* digits, underscores or dots. It is one of several identifier grammars in
* this repo, not the universal one — see the binding list below before
* reaching for it. Where it *is* bound, the shape buys the usual four
* properties: cross-platform safety (case-insensitive filesystems),
* URL-friendliness (no encoding needed), database consistency (no collation
* issues), and no case-sensitivity bugs in permission checks.
*
* **Bound surfaces — this is the whole list.**
* An earlier revision of this docblock claimed eleven consuming surfaces. The
* per-surface census on #12245 — its `os-dev-report` comment is the
* measurement of record, taken on `origin/main` @ `e2debee6` — measured
* **exactly one** of those eleven as validated by this schema; the other ten
* are validated by something else (next block). What composes this schema is:
*
* | Bound key | Declared at | Authored population |
* |------|---------|---------|
* | Select option `value` | `SelectOptionSchema.value` (`data/field.zod.ts`) | 1218 authored values censused |
* | Lifecycle rule `id` | `LifecyclePolicyRuleSchema.id` (`system/object-storage.zod.ts`) | none — nothing authors it |
* | Bucket `name` | `BucketConfigSchema.name` (`system/object-storage.zod.ts`) | none |
* | Storage config `name` | `ObjectStorageConfigSchema.name` (`system/object-storage.zod.ts`) | none |
*
* Select option values are the only surface with a real authored population.
* The option shape is reused by the form-view option list
* (`FormSelectOptionSchema`, `ui/view.zod.ts`, derived from
* `SelectOptionSchema.shape`), so both option lists carry this grammar. The
* three object-storage keys are bound in declaration only: the census found no
* corpus to measure for them and reports them as "nothing to census", never as
* measured clean.
*
* **NOT bound here — where those names are actually validated.** A generator
* that consults this docblock to learn what validates a name needs the real
* answer. None of these is a looser or stricter spelling of this grammar;
* they are different accept sets, so substituting one for another changes
* what is refused:
*
* - Object names, field names, workflow names — inline
* `/^[a-z_][a-z0-9_]*$/` at `data/object.zod.ts`, `data/field.zod.ts`,
* `automation/flow.zod.ts`. Dots forbidden; a leading `_` allowed.
* - Role (position) names, permission set names, action/trigger names, app
* IDs, menu/page IDs, webhook names — {@link SnakeCaseIdentifierSchema}.
* Dots forbidden.
* - Metadata item names (the `sys_metadata` / `/api/v1/meta` addressing
* identity) — {@link MetadataItemNameSchema}. Dots *allowed*, but as
* qualifiers between anchored segments, so it refuses the empty-,
* digit-initial and underscore-initial segments this schema accepts
* (`a.`, `a..b`, `a.1b`, `a._b`). Enforced at the metadata publish door.
* - Event keys — no author-facing grammar at all. The event vocabulary is the
* closed literal enums `DataEventType` / `BulkDataEventType`
* (`api/events.zod.ts`); the sibling `EventNameSchema` that the census found
* holding this claim was retired unbound under ADR-0049 (#13613 — tombstone
* at the foot of this file). The four branded aliases that wrapped *this*
* schema went the same way, also unbound (#13612).
*
* **Naming Convention Summary:**
* | Type | Pattern | Example |
* |------|---------|---------|
* | Machine ID | snake_case | `crm_account`, `btn_submit`, `role_admin` |
* | Event keys | dot.notation | `user.login`, `order.created` |
* | Labels | Any case | `Client Account`, `Submit Form` |
*
* The dot this grammar accepts is unexercised on the one live surface: 0 of
* the 1218 authored select option values contain one (#12245). Recorded as the
* measurement it is — dots are accepted, not a convention to write in.
*
* **Length ceiling — storage-owned, deliberately not declared here (#12144).**
* The identifier schemas in this file declare a floor and a grammar but no
* `.max()`: the enforced ceiling on an identifier is the `maxLength` of the
Expand All@@ -50,9 +87,11 @@ import { z } from 'zod';
* - 'account'
* - 'crm_account'
* - 'user_profile'
* - 'order.created' (for events)
* - 'api_v2_endpoint'
*
* - 'order.created' (the grammar accepts a dot; no bound surface authors one
* today — see the note above, and never read this row as the event-name
* contract, which is a closed enum elsewhere)
*
* @example Invalid identifiers (will be rejected)
* - 'Account' (uppercase)
* - 'CrmAccount' (camelCase)
Expand Down
Loading