Repository navigation
fix(spec): manifest.namespace refusal and TSDoc name the leading-letter rule - #19869
Conversation
…er rule
The regex `^[a-z][a-z0-9_]{1,19}$` requires a lowercase letter first, but
the refusal sentence and the TSDoc `Rules:` line only stated length and
charset, so `1leave` / `_leave` were refused by a sentence they satisfy.
The publish-payload copy in marketplace/package.zod.ts carried the same
sentence and is updated in step; a new pin holds all four namespace
fields to one sentence.
Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 136 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 75eb534af0878f97da34b6d195b9db7a0946788a && git checkout 75eb534af0878f97da34b6d195b9db7a0946788a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2bbb462335ad617f51ba7fc1a0c872f932f47513 27704f4cc2d7b6b06b17fcd053d330270345bf7d && git checkout -B drift-repro 2bbb462335ad617f51ba7fc1a0c872f932f47513 && git merge --no-ff 27704f4cc2d7b6b06b17fcd053d330270345bf7d
node scripts/docs-audit/affected-docs.mjs --json 2bbb462335ad617f51ba7fc1a0c872f932f47513
|
Contract reviewServed-tier: 45/45 Isolated at-tier reviewer subagent, run by the ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #19831
Clause-②: no
manifest.namespaceis enforced by^[a-z][a-z0-9_]{1,19}$, so its first character must be a lowercase letter. The refusal sentence and the TSDocRules:line only stated the length and the charset.1leaveand_leavesatisfied every clause an author was shown, and the regex still refused them. This PR makes both texts state what the regex enforces. The regex is byte-identical, so the accept set does not move.What changes
ManifestSchema.namespacerefusal (packages/spec/src/kernel/manifest.zod.ts)Namespace must be 2-20 chars, lowercase alphanumeric + underscoreNamespace must be 2-20 chars, start with a lowercase letter, and contain only lowercase letters, digits and underscoresRules:line2-20 characters, lowercase letters, digits, and underscores only.2-20 characters, starting with a lowercase letter; lowercase letters, digits, and underscores only (1leave and _leave are refused).PackageSchema.namespacerefusal (packages/spec/src/marketplace/package.zod.ts). Through it,CreatePackageRequestSchema.namespaceand the scaffold-onlyTemplateManifestSchema.namespacetooThe
package.zod.tscopy is the second author-facing restatement. The step-1 sweep below found it inpackages/spec, triage's grading named it, and the dispatch authorised fixing it here. If only one copy changed, the manifest and the publish payload would prescribe two different rules for one vocabulary. Its in-code comment now says that the sentence is kept identical as well as the pattern.The new sentence has no trailing period on purpose.
duplicatePackageinmetadata-protocolappends.after the declaration's message (protocol.ts, the explicit-targetNamespaceand derived-namespace refusals).Measured, before and after
Each value was parsed through
ManifestSchema.shape.namespacefrom source:origin/main0e90a8d1c5before, this branch27704f4cc2after.1leave_leavea(1 char)Leaveleave,ab, 20-char nameAll refusals are one issue with code
invalid_format. The verdicts are identical on both sides. Only the text changed.Restatement census (step 1)
This is
git grepover the whole tree at0e90a8d1c5, for the regex literal, the sentence,2-20 chars,2-20 charactersandalphanumeric + underscore. CHANGELOGs and release pages are excluded.packages/spec/src/kernel/manifest.zod.tsrefusal + TSDocpackages/spec/src/marketplace/package.zod.tsrefusalpackages/spec/src/api/endpoint-publish-gate.tsrefusal (apis:without a legal namespace)packages/spec/src/kernel/namespace-prefix.tscode comment(2-20 chars)packages/cli/src/commands/package/publish.tsrefusal + docblockpackages/spec, nonepackages/cli/src/commands/init.tsdocblockpackages/spec, nonepackages/create-objectstack/src/index.tsdocblockpackages/spec, nonecontent/docs/api/declarative-endpoints.mdxpackages/spec, nonedocs/adr/0048-…,docs/adr/0121-…packages/spec, none.changeset/19577-duplicate-package-explicit-namespace.md(unreleased)duplicatePackage's refusal textpackages/spec, not edited. See Acceptance notesskills/**content/docs/references/**(generated).describe()text only, which states no rulePins of the old sentence (step 4)
This is
git grepover all ofpackages/(andexamples/,apps/,scripts/) for the sentence and its fragments.packages/spec/src/marketplace/package-namespace.test.tspinned it verbatim twice (PackageSchemaandTemplateManifestSchemarefusals). Both now pin the new text.packages/metadata-protocol/src/protocol.duplicate-package-target-namespace.test.tsreads the sentence fromManifestSchema.shape.namespaceat run time and never retypes it. No edit was needed, and it passes against the rebuilt spec.objectui(origin/main544ecba) has zero hits too, with the control wordnamespacehitting.Generated artifacts (step 5)
pnpm --filter @objectstack/spec buildthencheck:generated: all 15 generated artifacts are up to date, and nothing was regenerated or committed. The.describe()text is unchanged, socontent/docs/references/kernel/manifest.mdxdoes not move. The builtdist/carries the new sentence in 18 files and the old one in 0.Changeset (step 6)
.changeset/manifest-namespace-leading-letter-refusal.mdis"@objectstack/spec": patch. The reading:patch.@objectstack/specchanges: the builtdist/carries the new sentence, soskip-changesetdoes not apply.Clause-②: no: no authorable key is added, removed, widened or narrowed, and the regex and accept set are byte-identical.@objectstack/metadata-protocolships no changed bytes, because it reads the message at run time. So it has no changeset entry.Tests
All of these ran at
27704f4cc2unless another commit is noted.packages/spec/src/kernel/manifest.test.ts:1leaveand_leaveare refused as exactly oneinvalid_formatissue, and the message matchesstart with a lowercase letter. The same sentence still names2-20 charsand the charset. As a control,leave,crm,my_app_2,a1and a 20-char name still parse, both on the field and in a whole manifest.package-namespace.test.ts("two gates, one vocabulary"): all four namespace fields refuse1leavewith the manifest's exact sentence. The existing verdict table could not see the prose, and this pin closes that class of drift.pnpm --filter @objectstack/spec test: 525 files passed, 15503 tests passed, 1 todo.pnpm --filter @objectstack/spec typecheck(tsc + scripts +check:test-typecheck): exit 0, and the debt ledger is unchanged.@objectstack/metadata-protocol: built its dependency closure, thenpnpm --filter @objectstack/metadata-protocol testran 188 files passed and 3 skipped, and 2673 tests passed and 19 skipped. The verbose run ofprotocol.duplicate-package-target-namespace.test.tspassed 13/13.48ea3d9960(samepackages/spectree):scripts/ablation-replace.mjsput today's sentence back through an anchor that must hit, with a shell trap restoring fromHEAD.manifest.zod.tsonly. It went red, 4 failed / 85 passed: both leading-letter pins, the charset pin, and the four-field sentence pin.package.zod.tsonly (drift between the two declarations). It went red, 3 failed / 86 passed: the four-field sentence pin and both verbatim pins.HEADand an emptygit diff HEADfor both files. Nothing was left in the tree..tsfiles:eslint --no-inline-config --format json, 4 files reported, 0 errors, 0 warnings. The population comes from eslint's ownisPathIgnored(false for all 4). The config never enables type-aware linting (noparserOptions.project), so this diff cannot change a verdict on an untouched file.Gates
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat27704f4cc2derived 83 commands. All 83 ran, and--ranreconciles them as 83 run, 0 NOT-MEASURED, 0 UNRUN. Three gates first exited 3 (PREREQUISITE NOT MET, workspacedist/absent):check:dual-build-cjs-loads,check:lean-entry-closureandcheck:type-check-debt. Afterturbo run build --filter='./packages/*' --filter='./packages/*/*'all three exit 0. Six roster gates in directories this diff touches also exit 0:check-changeset-fixed,check:meta-url-spelling,check:spec-changes,check:authz-resolver,check:error-code-casingandcheck:filter-alias-parity. The specrepovitest project and the CI-only jobs are left to CI.This branch is based on
0e90a8d1c5.origin/mainis 4 commits ahead (2bbb462335). None of them touches a file in this diff, and their diff has zero hits for the sentence.Acceptance notes
ManifestSchemakey.manifest.id/PackageSchema.manifestId(manifestIdRefusal, same file) say "lowercase dot-separated segments; hyphens allowed inside a segment, underscores are not.", but the pattern also requires each segment to open with a letter. Measured at0e90a8d1c5:com.example.2appandcom.2example.appare refused with exactly that sentence and no suggestion. They satisfy every clause it states. This is reported for the seat to file..changeset/19577-duplicate-package-explicit-namespace.md(line 15) quotes the old sentence as the textduplicatePackagereturns. After this PR that quote describes text the runtime no longer produces. It is outsidepackages/spec, so it is not edited here. This PR's own changeset states the new sentence and namesduplicatePackage, so the same release carries the correction. The replacement for the quoted fragment isNamespace must be 2-20 chars, start with a lowercase letter, and contain only lowercase letters, digits and underscores.versionkey and the imports ofmanifest.zod.ts, and PR feat(spec): doc navigation item targeting a book and/or a doc #19789 regeneratesmanifest.mdx. This PR touches neither region and moves no generated file.Generated by Claude Code