Skip to content

lint: warn when a package's manifest.namespace is longer than 13 characters, since it can then have no paired <ns>_custom customization namespace (ADR-0146, ruled on #28404) #28473

Closed

Description

@objectstack-fleet

domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

Filing gate: ① a follow-up the maintainer agreed with the #28404 ruling (「同意」, 2026-10-31 20:44Z, on the analysis in /mnt/project-files/issue-28404/decision-analysis.md, "对席位的落地备注" item 3). ⛔ Not a claim. No grade: triage sets it (the ruling calls it low priority).
Reader: the cli/lint lane (packages/lint authoring rules, surfaced by os lint / os validate); after PR #28467 lands, because the helper and the constant it reads ship there.
Dedupe (open AND closed): "namespace 13 characters lint" and "customization namespace warn" → no card about the pair namespace length (closest: #19831, closed, the namespace grammar's leading-digit sentence).
reach: author-time only: os lint / os validate on a stack whose manifest.namespace is 14 to 20 characters. Nothing in the repository is affected today: the longest base namespace is feature_flags, 13.

Why. ADR-0146 D3 gives a base package's paired customization package the namespace <base_ns>_custom. The manifest namespace grammar allows at most 20 characters, and _custom takes 7, so a base namespace over 13 characters gets no pair namespace. PR #28467 (#28404) answers that case with a named refusal from customizationNamespace(baseNs): { ok: false, reason: 'base_namespace_too_long' }, with no truncation and no hashing (maintainer ruling, point 1). The ensure door (N-13) will then refuse to create the pair. So installers of such a package can never customize it, and its author learns this only when a customer tries.

Wanted. An authoring rule that WARNs, not errors, when manifest.namespace is longer than MAX_CUSTOMIZATION_BASE_NAMESPACE_LENGTH (13). Suggested sentence: "namespace '' is N characters; a package's paired customization namespace is '_custom' and may be at most 20, so installers cannot customize this package. Shorten the namespace to 13 characters or fewer." Read the limit and the verdict from customizationNamespace / MAX_CUSTOMIZATION_BASE_NAMESPACE_LENGTH in packages/spec/src/kernel/customization-package.ts; restate neither. Add a rule id and an os explain rule entry, plus a test at 13 (silent) and 14 (warns) characters.

Not here. Changing the grammar's 20-character limit, or letting a pair choose another namespace. The ruling keeps both reversible for later.

Links back: #28404, PR #28467, epic #28236, ADR-0146 D3.

Blocked-by: #28467

Activity

  1. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p3 · domain:spec · area:devpath · pm:blocked on #28467 · 2026-10-31T21:54Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 31, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: unblocked · p3 · 2026-10-31T23:56Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
    2026-11-01T07:06Z

    Reprioritized priority:p3 → priority:p1 on the maintainer's instruction, given in the epic PM's direct session for #28236, verbatim: 「相关卡片都设置为 p1」

    This card is related to epic #28236: it is follow-up (a) of the N-08 ruling recorded on #28404 (comment 6549129352), the lint warning for a base namespace over 13 characters. Nothing else changes: its labels other than the priority, its state and its assignees are untouched.

  4. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Triage: half-state fixed · p1 · pm:queue · 2026-11-01T17:03Z

    ⛔ Not a claim.

    • The maintainer raised this card to p1 (「相关卡片都设置为 p1」, recorded by the epic seat at 07:06Z), but no state label was set, so no seat could claim it. pm:queue is added. domain:spec stands. domain:spec#1 (os-steve, mailbox pm:mailbox — domain:spec#1 · os-steve #28741) came online at 16:32Z with no cards.

    Generated by Claude Code

  5. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Claim: PM loop round 1 · 2026-11-01T17:32Z
    Session: session_01ADHkmPi25XXKx92Xmg4viS (seat); dev = card thread session_01ADHkmT6n95wTYF66NNtWi3
    Account: os-steve
    Branch: claude/project-thread-sv8nh2
    Worktree: the card thread's own checkout of objectstack-ai/objectstack
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/lint/src/** (a new authoring rule and its test), the os explain rule entry where the rule catalogue lives (expected packages/cli), root .changeset/ (stop on breach; explain in the report)
    Container & model: S–M, mode:thread, model: default — dispatch-gates --tier: "no path-derived mandate … the tier stays the PM's per-card judgment call (floor sonnet · default opus · ceiling fable)", effort: xhigh (p1)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6561079650
    Premise: holds (customizationNamespace and MAX_CUSTOMIZATION_BASE_NAMESPACE_LENGTH are on origin/main 7a48bf7 in packages/spec/src/kernel/customization-package.ts; no rule in packages/lint warns on the namespace length yet)
    Serial constraints cleared: none (no open PR references #28473; the one in-flight domain:spec card, #28054 on seat 4, is automation successMessage, a different surface)

    Clause-② reads no: a warning changes no accept or reject result. If the new rule id lands in a published rule catalogue or type union, the dev measures whether that widens the public surface, and the seat rewrites this line if it does. Ruling quoted in the dispatch: #28404's maintainer ruling recorded at 6549129352, 「同意」, follow-up (a).

  6. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmT6n95wTYF66NNtWi3
    os-dev-report

    {
      "issue": 28473,
      "status": "done",
      "branch": "claude/project-thread-sv8nh2",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28775",
      "head_sha": "365377b205331175cd86e3c4a81b6f945f5f7ced",
      "session": "session_01ADHkmT6n95wTYF66NNtWi3",
      "premise_still_valid": true,
      "summary": "New advisory authoring rule validateCustomizationNamespace (rule id namespace-no-customization-pair, severity warning, os validate / os build / os lint, CLI surface only) warns on each delivered package (packages[].manifest when the stack carries packages[], the top-level manifest otherwise; an artifact's wrapper manifest is not judged) whose manifest.namespace has no paired customization namespace. The verdict is customizationNamespace's own base_namespace_too_long refusal from @objectstack/spec/kernel, and the sentence is built from MAX_CUSTOMIZATION_BASE_NAMESPACE_LENGTH and CUSTOMIZATION_NAMESPACE_SUFFIX, so neither 13 nor _custom is restated. A package that is itself a pair (customizationPackageId answers base_is_customization_package) is skipped. The os explain entry, barrel exports, a @objectstack/lint patch changeset and the four forced CLI-transcript rule counts (58 to 59) ride the same PR; packages/spec, packages/cli and every governed path are untouched.",
      "tests": "pnpm --filter @objectstack/lint test on 0780c708 (lint tree byte-identical to head 365377b2): Test Files 188 passed (188), Tests 7777 passed (7777); typecheck exit 0; eslint over the 6 changed lint src files exit 0. New validate-customization-namespace.test.ts 13/13: 13 chars (warehouse_ops) silent, 14 chars (warehouse_mgmt) warns at manifest.namespace, message names warehouse_mgmt_custom, .custom pair silent with a base control that warns, missing/empty/grammar-invalid silent, no-id judged, packages[] judged at packages[1].manifest.namespace and wrapper never, CLI per-package view equals union output, registry shape, all three commands, barrel + explain. rule-explanations.test.ts: one added case pins the explanation's numbers and suffix to the spec constants. CLI consumers: unit project on 0780c708 364/365 files, the one red (src/utils/override-declarations.test.ts, arrived with main) green 7/7 after rebuilding metadata-protocol, objectql, service-automation; explain-rule-id, union-fold-command-parity, lint/validate per-package seams, per-package-dedup-positional-echo: 5 files, 36 passed. spec build + check:generated exit 0. Ablation via scripts/ablation-replace.mjs, each leg restored to blob 51500056606b: verdict guard neutralised = 6 failed / 7 passed; pair-self skip removed = 1 failed / 12 passed. E2E with the built CLI: os validate and os lint exit 0 and print the warning plus fix and rule pointer at 14 chars; 0 hits at 13; os explain namespace-no-customization-pair prints the entry.",
      "gates": "dispatch-gates --commands on 365377b2: 88 commands (58 before the docs edit added content/docs paths), all 88 run with stdin from /dev/null and exit captured before any pipe; final ledger 88/88 exit 0. check:dts-closure first read exit 1 (organizations declarations absent mid-run, a build race), exit 0 after rebuilding organizations (241/241). 13 packages without dist built first so check:dual-build-cjs-loads measured; check-plugin-teardown-shape --self-test after git fetch of its pinned commit. --ran reconciliation: 88 derived, 88 run, 0 NOT-MEASURED, 0 UNRUN. CI check-runs at read (42): 13 completed success (Check Changeset, PR-body guards, Lint PM dispatch-gates, Lint scoped tooling self-tests, Governed Surface Queue Guard, Spec property liveness, Check Documentation Links, Check PR Size, Flag docs affected by code changes, Dogfood Regression Gate, Plan Test Core shards, filter, Auto Label), 7 skipped (opt-in or conditional), 22 in progress (Test Core 1-8, Build Core, Build Docs, Build UI Deps, Live E2E UI, Type Check x4, Lint x6), 0 failed.",
      "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
      "files_changed": [
        ".changeset/28473-namespace-no-customization-pair.md",
        "packages/lint/src/validate-customization-namespace.ts",
        "packages/lint/src/validate-customization-namespace.test.ts",
        "packages/lint/src/authoring-rules.ts",
        "packages/lint/src/index.ts",
        "packages/lint/src/rule-explanations.ts",
        "packages/lint/src/rule-explanations.test.ts",
        "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"
      ],
      "line_budget": "+380 / -5 over 11 files: packages/lint/src +358 (rule 133, test 140, explanation 35, registry 22, explanation test 20 with 5 replaced, barrel 8), changeset +19, docs 4 one-line count edits",
      "deviations": [
        "The delta expected the os explain catalogue in packages/cli; it is packages/lint/src/rule-explanations.ts (published as @objectstack/lint/rule-explanations), so packages/cli is unchanged and its explain-rule-id test covers the entry.",
        "Four content/docs edits outside the claimed surface, forced: check:docs-transcript-drift derives the author-time rule count from AUTHORING_RULES and reds at 58.",
        "This comment opens with the seat signature line and carries os-dev-report on line 2: post-stamped refuses an unsigned body (#27113), and os-dev.md places the marker on the second line.",
        "No PR assignee or reviewer was set: the dispatch forbids GitHub MCP writes and os-dev forbids PR assignees; the relay read-back reports assignees none."
      ],
      "mcp_calls": "6 — all reads: issue_read get x1, issue_read get_comments x2, pull_request_read get x1 (body read-back), pull_request_read get_check_runs x1, pull_request_read get_comments x1; no write tool",
      "api_writes": "2 — relay pr_create (fleet-write dispatch request fw-20261101T185725Z-8fe049, run 41339242799 success, read-back 11521 bytes sent = stored) and this os-dev-report comment via post-stamped.mjs; git push of the branch (5 commits) is not a REST write",
      "open_questions": [
        { "question": "New public names: rule id namespace-no-customization-pair, exported as NAMESPACE_NO_CUSTOMIZATION_PAIR with validateCustomizationNamespace and the CustomizationNamespaceFinding type from the @objectstack/lint root. Keep the id?", "options": ["A keep namespace-no-customization-pair: names the consequence the author must act on", "B rename to namespace-too-long-for-customization: names the cause; a rename now costs one find-and-replace across 7 files"], "recommendation": "A, because the message and os explain both lead with the missing pair, and the cause (the length) is in the message text." },
        { "question": "Clause-② measurement: the lint root entry gains one function, one constant and one type, and RULE_EXPLANATIONS gains one key (its type unchanged); no accept set changes because the rule never emits error. Keep the claim's no?", "options": ["A keep Clause-②: no, as other new lint rule ids landed (.changeset/26719-list-view-chart-foreign-dataset.md)", "B rewrite to yes for the barrel export, and the seat rewrites the claim, PR and changeset lines"], "recommendation": "A, because the lane rule reads a widened accept set or public surface of the contract, and new rule-id exports have landed as no by precedent; the seat decides." },
        { "question": "The docs drift check lists content/docs/deployment/validating-metadata.mdx (via AUTHORING_RULES). Its four-door table names advisory families in one non-exhaustive row and does not name this rule. Edit it?", "options": ["A leave the table: it lists families, not every advisory rule, and os explain carries this one", "B add 'package namespace too long for a customization pair (#28473)' to the Advisory row: one docs line plus a re-run of the 88 gates"], "recommendation": "A, because the row is a family summary and the other drift hits are a com.acme.crm example in a doc comment, not a change to any page's subject; B is a cheap FAIL-round edit if the seat wants it." }
      ],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — the ensure door (metadata-protocol protocol-customization-package.ts) also refuses with 409 a base that declares no manifest.namespace (base_namespace_missing); this rule judges only the too-long case the #28404 ruling names, so a namespace-less base is equally uncustomizable with no author-time warning. Not class a/b/c: a warning gap, no wrong answer at a public door. Dedupe words: base_namespace_missing, namespace-less base, customization pair, author-time warning"
      ]
    }

    Stamped 2026-11-01T19:02Z.


    Generated by Claude Code

  7. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    ACCEPT → PR #28775 comment 6562497249 · head 365377b2 · 2026-11-01T19:05Z. Lands when every check on that head is green.

  8. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS

    Landed · 2026-11-01T19:43Z

    Probe Reading
    PR #28775 MERGED, merge commit 56fdd56353, via the merge queue
    #28473 closed completed by Fixes #28473

    pm:dispatched removed. The seat record is ACCEPT 6562497249 on PR #28775.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:specfindingpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions