Skip to content

Commit c41b42e

Browse files
claude[bot]claude
andauthored
docs(spec): make the SystemIdentifierSchema docblock name the surfaces it actually validates (#14194)
* docs(spec): make the SystemIdentifierSchema docblock name its real bound surfaces The docblock claimed eleven consuming surfaces. The per-surface census (its os-dev-report comment, measured on origin/main @ e2debee) measured exactly ONE of the eleven as validated by this schema: select option values (SelectOptionSchema.value). Eight go to SnakeCaseIdentifierSchema or an inline flat regex that forbids dots outright; event keys went to the sibling EventNameSchema, since retired unbound. Rewrite the prose to name the whole bound list as it stands today (SelectOptionSchema.value plus the three object-storage keys the old prose never claimed, recorded as bound-but-unauthored), to point each unbound surface at the schema that really validates it, and to state that these are different accept sets rather than looser spellings of one grammar. Drop the Event keys row of the naming table and the 'for events' example, both of which asserted a binding that no longer exists. Comment lines only: no regex change, no accept-set change, no .describe() change. The storage-owned length-ceiling note is carried through unchanged. * docs(changeset): correct the reach claim to what was measured The first draft of this changeset said the docblock ships into @objectstack/spec's .d.ts and so reached consumers' editor tooltips. Measured after the build: it does not -- no emitted .d.ts or .d.cts carries any docblock from identifiers.zod.ts. The corrected note states the real audience (readers of this source, human and agent), which is the audience the card was filed for. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 07cced5 commit c41b42e

2 files changed

Lines changed: 111 additions & 23 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
docs(spec): make the `SystemIdentifierSchema` docblock name the surfaces it actually validates (#13621)
6+
7+
Prose truth restoration. No regex change, no accept-set change, no `.describe()`
8+
change — the diff is comment lines only.
9+
10+
The docblock claimed eleven consuming surfaces ("Applies to all metadata that
11+
acts as a machine identifier": object names, field names, role names, permission
12+
set names, action/trigger names, event keys, app IDs, menu/page IDs, select
13+
option values, workflow names, webhook names). The per-surface census on #12245
14+
its `os-dev-report` comment is the measurement of record, taken on `origin/main`
15+
@ `e2debee6` — measured **exactly one** of those eleven as validated by this
16+
schema: select option values (`SelectOptionSchema.value`). Eight are validated by
17+
`SnakeCaseIdentifierSchema` or by an inline flat regex that forbids dots outright,
18+
and event keys went to the sibling `EventNameSchema`, since retired unbound
19+
(#13613). The reach is in-repo, measured: tsup's declaration emit does not carry
20+
this file's docblocks into the published `.d.ts`, so no consumer tooltip changes
21+
— the readers being corrected are the ones working in this source, which is the
22+
reason the card was filed: an AI generator reads this docblock as authority on
23+
where the grammar applies, and a docblock governing one surface while claiming
24+
eleven is a false map of the contract.
25+
26+
The rewritten docblock states, for today's tree:
27+
28+
- The **whole** bound list, as a table: `SelectOptionSchema.value`
29+
(`data/field.zod.ts`, the one surface with a real authored population, reused by
30+
the form-view option list via `SelectOptionSchema.shape`), plus three
31+
object-storage keys the old prose never claimed — `LifecyclePolicyRuleSchema.id`,
32+
`BucketConfigSchema.name`, `ObjectStorageConfigSchema.name` — recorded as bound
33+
in declaration with nothing authoring them ("nothing to census", not "censused
34+
clean").
35+
- Where the ten unbound surfaces are **actually** validated, so a reader who came
36+
here for the object-name rule leaves with the right file: the inline
37+
`/^[a-z_][a-z0-9_]*$/` sites, `SnakeCaseIdentifierSchema`,
38+
`MetadataItemNameSchema`, and — for event keys — the closed `DataEventType` /
39+
`BulkDataEventType` enums, which are not a grammar at all.
40+
- That these are **different accept sets**, not looser spellings of one another,
41+
with the measured `SystemIdentifierSchema` vs `MetadataItemNameSchema` delta
42+
named (`a.`, `a..b`, `a.1b`, `a._b`).
43+
- That the dot this grammar accepts is unexercised on its one live surface: 0 of
44+
1218 authored select option values contain one.
45+
46+
The `Event keys | dot.notation` row of the naming-convention table and the
47+
`'order.created' (for events)` example both asserted a binding that no longer
48+
exists; both now state what is true. The storage-owned length-ceiling note
49+
(#12144) is carried through unchanged.

packages/spec/src/shared/identifiers.zod.ts

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

0 commit comments

Comments
 (0)