Repository navigation
feat(lint): warn when a package namespace is too long to have a paired customization namespace - #28775
Conversation
…d customization namespace A new advisory authoring rule, `namespace-no-customization-pair`, reports a delivered package whose `manifest.namespace` gets no `<ns>_custom` pair namespace (ADR-0146 D3): `customizationNamespace` answers `base_namespace_too_long`, so the ensure door will refuse to create the `.custom` pair and installers can never customize the package. The verdict and the sentence read the spec helper and its constants; nothing restates the limit or the suffix. Runs on `os validate`, `os build` and `os lint`, CLI surface only, with an `os explain` entry. Claude-Session: https://claude.ai/code/session_01ADHkmT6n95wTYF66NNtWi3 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmT6n95wTYF66NNtWi3 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmT6n95wTYF66NNtWi3 Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADHkmT6n95wTYF66NNtWi3 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 9 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin cb2bc485097f887c2653d58bb689cae559e9a9f7 && git checkout cb2bc485097f887c2653d58bb689cae559e9a9f7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 850eab2a948528d63d687dca8e77f26ebe7ed787 365377b205331175cd86e3c4a81b6f945f5f7ced && git checkout -B drift-repro 850eab2a948528d63d687dca8e77f26ebe7ed787 && git merge --no-ff 365377b205331175cd86e3c4a81b6f945f5f7ced
node scripts/docs-audit/affected-docs.mjs --json 850eab2a948528d63d687dca8e77f26ebe7ed787
|
|
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS Head-sha: 维护者速读
席位意见:可落地。实现只读 spec 自己的 helper 和常量,没有另抄一份限值;边界(13 静默、14 告警、 席位核对 — ACCEPT ·
|
Requested by Steve · project thread
Fixes #28473
Clause-②: no
Metadata-protocol: none
维护者速读
namespace-no-customization-pair:包的manifest.namespace超过 13 个字符时,os validate/os build/os lint提示"这个包没有配对的_custom定制命名空间",并给出os explain说明。.custompairing constants, the<base_ns>_customnamespace rule, the__creservation, the overridable-type list and the base-field display whitelist #28404 的裁决(「同意」),命名空间超长的基础包拿不到配对定制包,确保定制包的入口会直接拒绝,安装方永远无法定制它;以前作者要等客户第一次定制被拒才知道,那时改命名空间等于改所有对象名和表名。packages/lint/src/validate-customization-namespace.ts(判定只读 spec 的customizationNamespace,不另写数字)、packages/lint/src/authoring-rules.ts末尾的注册项、packages/lint/src/rule-explanations.ts的新条目。pnpm --filter @objectstack/lint exec vitest run src/validate-customization-namespace.test.ts,预期 13 个用例全过(13 字符静默,14 字符告警,.custom配对包本身静默)。What changes
validateCustomizationNamespace(packages/lint/src/validate-customization-namespace.ts), rule idnamespace-no-customization-pair, severitywarning. For each package one install delivers (packages[].manifestwhen the stack carriespackages[], the top-levelmanifestotherwise; an artifact's own wrapper manifest is not judged), it askscustomizationNamespace(manifest.namespace)from@objectstack/spec/kerneland reports exactly itsbase_namespace_too_longrefusal, atmanifest.namespace(orpackages[N].manifest.namespace).MAX_CUSTOMIZATION_BASE_NAMESPACE_LENGTHandCUSTOMIZATION_NAMESPACE_SUFFIX; the rule spells neither 13 nor_customitself. The grammar maximum it prints isMAX_CUSTOMIZATION_BASE_NAMESPACE_LENGTH + CUSTOMIZATION_NAMESPACE_SUFFIX.length, the identitycustomization-package.tsdefines the limit by.customizationPackageIdanswersbase_is_customization_package: a pair is never paired again); the helper'sbase_namespace_missingandbase_namespace_invalidrefusals (the second isos validate's grammar refusal).AUTHORING_RULES:tier: 'advisory',input: 'normalized', all three commands,surfaces: CLI_ONLYwith asurfaceReason(a package manifest is not a metadata item, so the runtime publish gate has nothing to judge; the ensure door already refuses at 409).os explain namespace-no-customization-pair: a new entry inpackages/lint/src/rule-explanations.ts(the tableos explainreads;packages/cliis unchanged).validateCustomizationNamespace,NAMESPACE_NO_CUSTOMIZATION_PAIRand theCustomizationNamespaceFindingtype from@objectstack/lint's root entry (the rule-id barrel test requires the id there)..changeset/28473-namespace-no-customization-pair.md,@objectstack/lintpatch.authoringRulesFor), 58 → 59:content/docs/deployment/cli.mdx,content/docs/deployment/validating-metadata.mdx,content/docs/getting-started/build-with-claude-code.mdx,content/docs/ui/react-pages.mdx(check:docs-transcript-driftreds without it).Why
ADR-0146 D3 names a base package's paired customization package's namespace
BASE_NS_custom. The manifest namespace grammar admits 20 characters and the suffix takes 7, so a base namespace over 13 characters has no pair namespace. The ruling on #28404 (comment 6549129352, 「同意」) answers that with a named refusal and no truncation or hashing, and the ensure door refuses such a base with409. Its installers can never customize it, and its author would learn that only when a customer first tried. This is follow-up (a) of that ruling.Tests
packages/lint/src/validate-customization-namespace.test.ts(new, 13 cases): the fixtures sit on the helper's own boundary; 13 characters silent; 14 characters warns atmanifest.namespacewith severitywarning; the message nameswarehouse_mgmt_custom; a.custompair with a 20-character namespace is silent while the same namespace on a base warns; missing, empty and grammar-invalid namespaces are silent; nomanifest.idstill judged; artifactpackages[]judged per body and the wrapper never; the CLI per-package view of each body yields the union run's findings byte for byte (so the CLI's per-package de-duplication adds no second copy); registry entry shape; all three commands report it; barrel and explain entry present.packages/lint/src/rule-explanations.test.ts: one added case holds the explanation's written numbers and suffix (the module imports nothing) to the spec constants.Acceptance notes
os explaincatalogue inpackages/cli. It lives inpackages/lint/src/rule-explanations.ts(published as@objectstack/lint/rule-explanations); the CLI'sos explainandrule:pointer read it, sopackages/clineeds no edit.packages/cli/test/explain-rule-id.test.tsiterates every entry through the command and covers the new one.@objectstack/lint's root entry gains one function, one rule-id constant and one type, andRULE_EXPLANATIONSgains one key (its type is unchanged). No accept set changes: the rule never emitserror. Other new lint rule ids landed underClause-②: no(for example.changeset/26719-list-view-chart-foreign-dataset.md).base_namespace_missing, 409). This rule does not judge that case; the ruling names the long one.Verification
Final head
365377b2(main merged at5493b884). The lint tree is byte-identical between the merge commit0780c708and the final head (the last commit touchescontent/docsonly), so the lint and CLI readings taken on0780c708hold for it.Tests
pnpm --filter @objectstack/lint teston0780c708: Test Files 188 passed (188), Tests 7777 passed (7777).pnpm --filter @objectstack/lint typecheck: exit 0.pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generatedon0780c708: exit 0 (spec moved on main's side; nothing to regenerate here).pnpm --filter @objectstack/cli exec vitest run --project uniton0780c708: 364/365 files, 5174/5175 tests. The one red,src/utils/override-declarations.test.ts, arrived with the merge (main's flow-override change) and read a stalemetadata-protocoldist; after rebuilding the packages the merge moved (metadata-protocol,objectql,service-automation) it passed 7/7. Before the merge, on15f94cb9: 363/365, the two reds beingpublished-subpath-*.pin.test.tsneeding the CLI's own dist; afterpnpm --filter @objectstack/cli build, 29/29.test/explain-rule-id.test.ts,test/union-fold-command-parity.test.ts,test/lint-per-package-authoring-seam.test.ts,test/validate-per-package-authoring-seam.test.ts,test/per-package-dedup-positional-echo.test.ts: 5 files, 36 tests passed.packages/lint/srcfiles: exit 0.End to end, through the public door
A throwaway
defineStackconfig withmanifest.namespace: 'warehouse_mgmt'(14 characters), run with the built CLI:os validate: exit 0, and prints the warningpackage "com.acme.warehouse": namespace "warehouse_mgmt" is 14 characters, so this package has no paired customization namespace: "warehouse_mgmt_custom" would be 21, past the 20 a namespace may have, and installers cannot customize the package, thenfix: shorten manifest.namespace to 13 characters or fewer, and rename the object names it prefixes to match, thenrule: namespace-no-customization-pair at manifest.namespace — os explain namespace-no-customization-pair for why a long namespace leaves a package uncustomizable.os lint: exit 0, the same warning.warehouse_ops(13 characters):os validateprints the rule id 0 times.os explain namespace-no-customization-pairprints the five paragraphs.Ablation (rule source, read from src by the suite)
Through
scripts/ablation-replace.mjs, each leg restored to the HEAD blob (51500056606b,git diff HEADempty):validate-customization-namespace.test.ts6 failed / 7 passed (the 14-character, message, pair-control, no-id, artifact and all-commands cases)..custompair case).Dispatch gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon365377b2listed 88 commands (58 before the docs edit addedcontent/docspaths). All 88 run, each with stdin from /dev/null and its exit captured before any pipe: 87 exit 0 on the first pass.pnpm check:dts-closurefirst read exit 1 (organizations' declaration files absent mid-run while another gate's build touched that package); afterpnpm --filter @objectstack/organizations buildit read exit 0 (241/241 declaration files present). Before running, 13 packages without adist/were built, socheck:dual-build-cjs-loadsmeasured instead of answering exit 3;check-plugin-teardown-shape --self-testread its pinned commit aftergit fetch --depth=1.Reconciliation:
dispatch-gates --ran: 88 derived, 88 run, 0 NOT-MEASURED, 0 UNRUN (a derived zero).CI check-runs on this PR: not read yet (in progress at the time of writing).
Human Minimum
packages/specholds no runtime logic. (No schema touched.)pnpm lintpasses. (eslint on the six changed files: exit 0; the repo-wide run is CI's.)main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed. (Not touched.)content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.Generated by Claude Code