Repository navigation
Commit 0e73de8
docs(skills): teach one naming rule, every package-authored metadata name starts with its namespace (#30831)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DWyW8k3qL1ZSS5TK1cXcDAs)_
Fixes #30805
Clause-②: no
Metadata-protocol: none
## 维护者速读
- 改了什么:发布的 authoring skills 现在只教一条命名规则——包里写的每个元数据名都以包的 namespace
开头(`crm_send_email`、`crm_sales_rep`),例外只有字段、对象扩展、容器视图和翻译;全部示例和 evals
里的裸名已改成带前缀。
- 为什么改:落实 #30798 裁决 C 的第二阶段,让 AI 在第三阶段把裸名变成拒收之前就已经按一种写法写名字。
- 风险与回滚:只改 `skills/**` 文档和 evals,不动任何代码、schema 或行为;回滚就是还原这一个 PR。
- 先看:`skills/objectstack-platform/SKILL.md` 第 123 行新加的规则段,以及下方的改名表和保留名单。
- 两分钟验证:`node scripts/check-skills-token-ratchet.mjs`
全绿(每个文件都在原天花板内,靠同文件删减付费);`pnpm --dir ui run check:skill-eval-tokens` 通过。
- 需要你做的:按 Tier H 走受治理审批;下方 Open questions 里有三处需要席位或你拍板的保留项(裸对象名、连接器
id、能力名)。
## What changes
`objectstack-platform/SKILL.md` states the rule once, as a new paragraph
right after the object rule:
every metadata name a package authors starts with its namespace (flows,
actions, hooks, apps, pages,
dashboards, reports, datasets, queues, positions, permission sets,
skills). The exceptions are fields,
object extensions and container views (derived from a prefixed object),
and translations (the namespace,
an underscore, then the locale). The paragraph is taught as the
authoring contract. It also says what
origin/main enforces today, read off the code:
- `defineStack` refuses a bare object name
(`validateObjectNamespacePrefix`,
`packages/spec/src/kernel/namespace-prefix.ts`).
- `os build` refuses a bare doc name (`docs/namespace-prefix`, an
error).
- `os lint`'s `naming/namespace-prefix` warns only when one package
declares an app, page, dashboard,
flow, action, report or dataset name twice
(`packages/cli/src/commands/lint.ts` around L280-330).
The old sentence in that paragraph said `os lint` warns on every bare UI
or automation name whenever a
namespace is set. That is false on main, so it is removed. #30804 (open)
widens that lint. If it lands
first, this paragraph's last sentence must be re-matched to its type
list before merge. If this PR lands
first, #30804's PR owes that one-sentence update.
Every example and eval in `skills/**` that showed or generated a bare
non-object name now carries the
prefix. Snippets with no manifest use `crm`. Snippets with their own
package context keep it: `todo` for
the i18n app-todo mirror, which already uses `todo_app`.
Cross-references move together: action targets,
`dashboardName` / `reportName`, approver and queue values,
`permissionSets`, `skills`, `action_` tool
names, translation keys and eval `must_contain` tokens.
`skills/README.md` "Conventions enforced across skills" now teaches the
same rule. It keeps the existing
short-object-name convention for snippets, with no object-level
`namespace` and no `tableName`.
### Rename table (file → old → new)
| File | Old | New |
|:--|:--|:--|
| objectstack-platform/SKILL.md | position `legal_counsel`, permission
set `contracts_editor` | `crm_legal_counsel`, `crm_contracts_editor` |
| objectstack-platform/evals/config-plugins-ops.json | eval 1 expects
"prefixed object names"; eval 9 queue `support_intake` (×3), position
`service_agent` (×2) | "prefixed object and flow names (`crm_account`,
`crm_convert_lead`)"; `crm_support_intake`, `crm_service_agent` |
| objectstack-ui/rules/actions.md | actions `reassign_lead`,
`convert_lead`, `create_campaign`, `print_a3`, `open_invoice_pdf` (and
its handler target); flow target `lead_conversion`; app segment
`/apps/crm/…` | `crm_reassign_lead`, `crm_convert_lead`,
`crm_create_campaign`, `crm_print_a3`, `crm_open_invoice_pdf`;
`crm_lead_conversion`; `/apps/crm_enterprise/…` (the app navigation.md
declares) |
| objectstack-ui/rules/navigation.md | `dashboardName:
'sales_dashboard'`, `reportName: 'opportunities_by_stage'`, page
`task_workbench` | `crm_sales_dashboard`, `crm_opportunities_by_stage`,
`crm_task_workbench` |
| objectstack-ui/rules/pages.md | pages `invoice_print`, `release_notes`
| `crm_invoice_print`, `crm_release_notes` |
| objectstack-ui/rules/dashboards.md | dashboard `sales_dashboard`;
datasets `sales`, `opportunity_metrics` (×3), `order_metrics` (×2),
`project_metrics`, `task_metrics` (×2), `employee_metrics`,
`contract_metrics` (×2); cube `opportunity`; actionUrl
`create_opportunity`, `export_dashboard_pdf`; report
`pipeline_coverage_by_quarter` | each with `crm_` (the cube's `sql:
'opportunity'` table stays) |
| objectstack-ui/evals/views-apps-actions-pages.json | flow
`lead_conversion` (evals 3, 8); key `_actions.log_call` (eval 11) |
`crm_lead_conversion`; `_actions.crm_log_call` |
| objectstack-ui/evals/report-period-export.json | dataset
`opportunity_metrics` (×2) | `crm_opportunity_metrics` |
| objectstack-automation/SKILL.md | assignment rule `case_intake`;
queues `escalations`, `support_intake` (×2) | `crm_case_intake`;
`crm_escalations`, `crm_support_intake` |
| objectstack-automation/references/examples-flows.md | flows
`escalate_overdue_cases`, `renewal_alert` |
`crm_escalate_overdue_cases`, `crm_renewal_alert` |
| objectstack-automation/references/integration-recipes.md | flow
`stripe_balance_sweep` | `crm_stripe_balance_sweep` |
| objectstack-automation/references/state-machines-and-approvals.md |
flows `opportunity_discount_approval`, `dynamic_approval`; positions
`sales_manager` (×2, one in the `org_membership_level` counter-example),
`sales_director`, `manager`, `auditor` | each with `crm_` |
| objectstack-automation/evals/flows-triggers-approvals.json | flows
`budget_approval` (eval 3), `convert_lead` (eval 9); positions
`manager`, `finance_manager` (×2); permission set `contractor_portal`;
assignment rule `case_intake`; queues `support_team`, `support_leads` |
each with `crm_` |
| objectstack-automation/evals/approvals/test-revise-loop.md | flow
`budget_approval` | `crm_budget_approval` |
| objectstack-ai/SKILL.md | skill `case_management`; action
`escalate_case`, tool `action_escalate_case` | `crm_case_management`;
`crm_escalate_case`, `action_crm_escalate_case` |
| objectstack-ai/evals/skills-tools-knowledge.json | `escalate_case` /
`action_escalate_case` (eval 1, `must_contain` too); `close_case` /
`action_close_case` (evals 4, 5); agent `support_bot` (eval 5); eval 1
expects "a snake_case `name`" | each with `crm_`; "a namespace-prefixed
snake_case `name`" |
| objectstack-upgrade/references/examples-upgrade.md | agent
`support_bot`; `skills: ['case_management']` | `crm_support_bot`;
`['crm_case_management']` |
| objectstack-data/SKILL.md | queue `support_intake`; position
`service_agent` | `crm_support_intake`; `crm_service_agent` |
| objectstack-data/rules/security.md | permission set `sales_user` (×2);
position `sales_rep` | `crm_sales_user`; `crm_sales_rep` |
| objectstack-data/references/data-hooks.md | hooks
`my_validation_hook`, `fill_position_on_hire`,
`protect_strategic_accounts`, `audit_trail`, `cascade_account_updates`,
`mask_pii`, `h` | `crm_validation_hook`, `crm_fill_position_on_hire`,
`crm_protect_strategic_accounts`, `crm_audit_trail`,
`crm_cascade_account_updates`, `crm_mask_pii`, `crm_h` |
| objectstack-data/references/examples-objects.md | hook
`account_defaults` | `crm_account_defaults` |
| objectstack-data/rules/validation.md | hook `tax_id_external_check` |
`crm_tax_id_external_check` |
| objectstack-data/evals/hooks-security-seeds-datasources.json | queue
`inside_sales` (×2), position `inside_sales_rep` (eval 8) |
`crm_inside_sales`, `crm_inside_sales_rep` |
| objectstack-i18n/SKILL.md | `_actions.complete` (the app-todo mirror,
namespace `todo`) | `_actions.todo_complete` |
| objectstack-i18n/evals/bundles-locales-coverage.json | action `close`
(eval 1); report `pipeline_by_stage`, permission set `sales_manager`,
position `regional_lead`, picklist `deal_stage` (eval 6); page `home`,
flow `expense_claim` (eval 7) | each with `crm_` |
| objectui/rules/protocol.md | action `"approve"` | `"crm_approve"` |
| skills/README.md | "Short object names; no `namespace`, no
`tableName`." | the one rule plus its exemptions, then "Short object
names in snippets; no object-level `namespace`, no `tableName`." |
Not renamed, because they are exempt as nested names: validation rule
names, flow node ids, edge targets,
widget ids, dataset dimensions and measures, assignment-rule entries,
nav item ids, params, and container
view keys.
### Held, not renamed (report per the dispatch's external-caller stop)
- **Connector ids** `stripe`, `github_mcp`, `warehouse_api`,
`legacy_erp` (integration-recipes.md),
`erp_api` (datasources.md), `sap_erp` (upgrade eval 3) and the platform
`slack` connector. The id is
what credentials and inbound calls name.
- **The api flow `carrier_callback`.** Its name is a segment of the
inbound URL the carrier calls
(`…/hooks/carrier_callback/carrier`).
- **The datasource `warehouse`.** Its name keys the
`OS_DATASOURCE_WAREHOUSE_PASSWORD` variable.
- **Capability names** `view_pii` and `expense.approve_request`. Their
grammar is dotted, and how a prefix
applies to it is a phase-3 question.
- **Function handler names** (`normalize`, `helpdesk.aiTriageStub`,
`billing.sync`). They are not a
metadata type. **Knowledge source id** `support_kb`. It is not stack
metadata.
- **Bare object names in snippets** (`account`, `lead`, `task`, …).
These are outside this card, which
covers non-object names. See Open questions.
### Token ratchet: paid by same-file deletion, no ceiling moved
| File | Deleted | Reading (tokens / ceiling) |
|:--|:--|:--|
| objectstack-platform/SKILL.md | the false `os lint` sentence; two TOC
parentheticals that restate the H2 headings of `plugin-development.md`
and `operations.md` | 5990 / 6011 |
| objectstack-ui/rules/actions.md | "— an action can live in multiple
surfaces" (restates "is an array") | 2259 / 2259 |
| objectstack-ui/rules/dashboards.md | "the escape hatch escalates when
the user wants the full surface" (restates the bullet's lead) | 6252 /
6262 |
| objectstack-ui/rules/navigation.md | "Canonical rules:" (the heading
already cites objectui ADR-0055) | 2195 / 2196 |
| objectstack-ui/rules/pages.md | "hygiene, not uniqueness: two packages
coexist on one bare name (ADR-0048 §3.4)". It contradicts the rule this
PR teaches, and #30798's audit measured doc reads as first-match without
a package. | 5648 / 5669 |
| objectstack-automation/SKILL.md | "The third automation family:" (the
heading names it) | 6443 / 6449 |
| objectstack-automation/references/state-machines-and-approvals.md |
the `maxRevisions` row's pointer, which the bullet list below repeats |
5721 / 5730 |
| objectstack-data/SKILL.md | the seeds pointer's TOC parenthetical (it
restates `seeds.md`'s headings and was already missing one) | 6087 /
6123 |
| objectstack-data/references/data-hooks.md | the `afterFind` code
comment that repeats the sentence above it | 10056 / 10066 |
| objectstack-data/references/examples-objects.md | the intro line that
restates its "Lifecycle Hooks" heading | 1741 / 1756 |
| objectstack-data/rules/security.md | the comment that repeats the
`defineStack({ permissions })` line under it | 3688 / 3698 |
| objectstack-data/rules/validation.md | "— the typed, supported
extension point" | 2746 / 2756 |
Package-wide (all authored and eval files under `skills/**` this PR
touches, ceil of bytes over 4):
7633 → 7635 lines, 97336 → 97353 tokens (+17). The +17 is the eval
fixtures, which are measured but not
ratcheted, plus `skills/README.md`, which is outside the ratchet. Every
ratcheted file is at or under its
ceiling.
## Why
Decision #30798 ruled C (maintainer, verbatim: 「同意 c」). Every
package-authored metadata name carries its
namespace, in three phases. This is phase 2, the AI-authoring half: the
published skills teach the one rule
before phase 3 makes a bare name a refusal.
## Tests
No code changes, so there are no new unit tests. The gates that read
`skills/**` are what judge this diff.
Families derived by `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`
(48), run locally at head `7fd40067`, each exit code recorded as
`COMMAND :: exit CODE`:
- 47 of 48 exit 0. They include `node
scripts/check-skills-token-ratchet.mjs` (and `--self-test`),
`pnpm --filter @objectstack/spec run check:skill-examples` (after the
`@objectstack/client-react...` closure build), `check:skill-docs`,
`check:skill-refs`,
`pnpm --filter @objectstack/lint run check:doc-formula-expressions`
(after the `@objectstack/lint...`
closure build), `pnpm check:skill-top-level-keys`,
`check:skill-identifier-liveness`,
`check:skill-frame-sync`, `check:skill-compatibility`,
`check:nul-bytes`, `check:doc-authoring`,
`check:pm-governed-merges` and `pnpm --dir ui exec vitest run scripts/`.
- `pnpm check:dual-build-cjs-loads` exited 3 with "PREREQUISITE NOT MET
— this gate reads built output,
and some package has no dist/". Nothing was measured. The gate reads
`packages/**/dist`, which this diff
does not touch. The PR's CI check-run is the reading for it.
- `--ran` reconciliation: "48 derived famil(ies) accounted for — 47 run,
1 NOT-MEASURED (1 DERIVED from
a recorded exit 3)", with 0 unrun.
- Run outside the derived list: `pnpm --dir ui run
check:skill-eval-tokens --self-test` and
`pnpm --dir ui run check:skill-eval-tokens` exited 0 ("Every
must_contain token is taught by its own
skill bundle"). `pnpm lint` exited 0.
## Acceptance notes
- **Out of scope, reported to the seat and not fixed here:**
`skills/objectui/guides/project-setup.md`
L199-221 imports `defineConfig` from `@objectstack/runtime`, which
exports no `defineConfig` on main.
The snippet also nests `objects` under `apps[]` with the name `my-app`.
It carries no `os:check`
marker, so no gate compiles it.
- Under the rule, an app named exactly `crm` is bare, because
`validateObjectNamespacePrefix` checks
for the namespace followed by an underscore. That is why the action URL
now points at
`crm_enterprise`.
## Open questions
1. **Bare object names in skill snippets.** Most snippets use short
object names (`account`, `lead`) by
the README convention, while ADR-0129 requires the prefix once a
namespace is set. Recommendation: a
separate card after phase 3, so this PR stays at the card's non-object
scope.
2. **Connector, inbound api flow and datasource names** (held above).
Recommendation: keep them, and have
phase 3 exempt names an external caller or a variable spells, or rename
them with a migration note.
3. **Dotted capability names.** Phase 3 should state how the prefix
applies (`crm.view_pii` or
`crm_view_pii`).
## Verification
1. Check out the branch and run `node
scripts/check-skills-token-ratchet.mjs`. Every ratcheted file
reads at or under its ceiling, and no ceiling moved.
2. Run `pnpm --dir ui run check:skill-eval-tokens`. It passes, including
the renamed
`action_crm_escalate_case` token.
3. Read `skills/objectstack-platform/SKILL.md` L123 against
`packages/spec/src/kernel/namespace-prefix.ts`
and `packages/cli/src/commands/lint.ts`. The enforcement sentence
matches main.
4. Run `git grep -nE "name:
'(lead_conversion|support_intake|sales_rep|escalate_case|case_management)'"
-- skills`.
It returns nothing.
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(`packages/spec` untouched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes.
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset.
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English, apart from the fleet's
required `## 维护者速读` summary.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_016WV4cw4B24PbS5MZbX1r5j
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 18870c9 commit 0e73de8
28 files changed
Lines changed: 129 additions & 115 deletions
File tree
- .changeset
- skills
- objectstack-ai
- evals
- objectstack-automation
- evals
- approvals
- references
- objectstack-data
- evals
- references
- rules
- objectstack-i18n
- evals
- objectstack-platform
- evals
- objectstack-ui
- evals
- rules
- objectstack-upgrade/references
- objectui/rules
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | | - | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
99 | 104 | | |
100 | 105 | | |
101 | 106 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
97 | | - | |
| 97 | + | |
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
| |||
104 | 104 | | |
105 | 105 | | |
106 | 106 | | |
107 | | - | |
108 | | - | |
| 107 | + | |
| 108 | + | |
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
7 | | - | |
| 6 | + | |
| 7 | + | |
8 | 8 | | |
9 | 9 | | |
10 | | - | |
| 10 | + | |
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
37 | | - | |
| 36 | + | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
292 | 292 | | |
293 | 293 | | |
294 | 294 | | |
295 | | - | |
| 295 | + | |
296 | 296 | | |
297 | 297 | | |
298 | 298 | | |
| |||
303 | 303 | | |
304 | 304 | | |
305 | 305 | | |
306 | | - | |
| 306 | + | |
307 | 307 | | |
308 | 308 | | |
309 | 309 | | |
310 | | - | |
311 | | - | |
| 310 | + | |
| 311 | + | |
312 | 312 | | |
313 | 313 | | |
314 | 314 | | |
315 | 315 | | |
316 | 316 | | |
317 | 317 | | |
318 | 318 | | |
319 | | - | |
| 319 | + | |
320 | 320 | | |
321 | 321 | | |
322 | 322 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
11 | 11 | | |
12 | 12 | | |
13 | 13 | | |
14 | | - | |
| 14 | + | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
47 | | - | |
| 46 | + | |
| 47 | + | |
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
| |||
120 | 120 | | |
121 | 121 | | |
122 | 122 | | |
123 | | - | |
124 | | - | |
| 123 | + | |
| 124 | + | |
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
| |||
155 | 155 | | |
156 | 156 | | |
157 | 157 | | |
158 | | - | |
| 158 | + | |
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
| |||
190 | 190 | | |
191 | 191 | | |
192 | 192 | | |
193 | | - | |
| 193 | + | |
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
| |||
210 | 210 | | |
211 | 211 | | |
212 | 212 | | |
213 | | - | |
| 213 | + | |
214 | 214 | | |
215 | 215 | | |
216 | 216 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
85 | | - | |
| 85 | + | |
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
| |||
0 commit comments