Repository navigation
Description
epic:#28130#1 · hotlong · session_040dfd80c4c9411b895c698e785a6848
Filing gate: ④ child of epic #28236 (P1, cut after ADR-0146's rewrite merged in #28295) · the shared definitions N-09, N-10, N-13, N-14 and N-23a/b read. ⛔ Not a claim.
Part of #28236 (P1, child N-08) · governs: ADR-0146 D2 (declaration, type list), D3 (pairing, namespace, range), D6 (excluded types), D10 (__c, display whitelist) · lane: domain:spec · size: M.
Follows #28390 ruling A (6542177429): only the paired <base>.custom declares an override. The pair predicate below is what the gates read.
Business reason. A developer writing hotcrm.custom in a repository and an administrator customizing in Studio must produce the same declaration, one the write gate, the read, export and os validate all understand. Without one Zod shape and one set of constants, every door would spell "this item overrides HotCRM's" its own way.
Measured (75ecaf4ad8).
packages/spec/srchas nooverrideskey,.customconstant or__crule; no__cname appears in non-test sources underexamples/orpackages/.- Every type D2 lists spreads
MetadataProtectionFields(packages/spec/src/kernel/metadata-protection.zod.ts:81). DEFAULT_METADATA_TYPE_REGISTRY(packages/spec/src/kernel/metadata-plugin.zod.ts) has 30 entries. Four runtime-creatable types appear on neither side of D2:seed,external_catalog,tool,skill.packages/spec/src/kernel/namespace-prefix.ts:54#deriveNamespaceFromPackageIdreturnscustomforhotcrm.custom. Namespaces must match/^[a-z][a-z0-9_]{1,19}$/(namespace-prefix.ts:18#NAMESPACE_RE; the same regex inline atmanifest.zod.ts:564), so<base_ns>_customfits only a base namespace of 13 characters or fewer. The longest in the repository isfeature_flagsat 13 (packages/services/service-settings/src/manifests/feature-flags.manifest.ts:7).- Field names match
/^[a-z_][a-z0-9_]*$/(packages/spec/src/data/field.zod.ts:1363), which admitsregion__c.packages/spec/src/data/object.zod.ts:4709#ObjectExtensionSchemais strict and has no slot for patching a single field.
Wanted.
- An optional
overrides: { package, basedOn }on every overridable type, one shared field set in the style ofMetadataProtectionFields; its.describe()saysbasedOnis recorded and never judged (D2). The key survivesdefineStack(), the stored body and export. - Pairing constants and pure helpers: the
.customsuffix,customizationPackageId(baseId),isCustomizationPackage(id), the base of a pair, an "is the pair of" predicate, and the dependency range^<major>from the base'smanifest.version(D3; merge item 1). Dotted ids such ascom.example.showcase.customare covered. N-08 owns these; N-13 (the ensure door) and N-14 (uninstall protection) consume them and areBlocked-bythis card. ⛔ Spelled nowhere else. - Namespace rule, decided here:
customizationNamespace(baseNs)returns<base_ns>_custom, never derived. A base namespace longer than 13 characters gets no pair namespace: the helper answers a named reason and N-13 refuses that base with a sentence. ⛔ No truncation, no hashing: either is a second spelling the declaration cannot be checked against. ADR-0146 is silent here, so the PR lists it for the maintainer. Nothing in the repository is affected today. - The
__creservation: a suffix constant andisExtensionFieldName(name). Enforcement is N-23b. - A closed list of overridable types, checked against the registry:
permission,position,capabilityexcluded;objectextend-only;flow's andaction's D9 conditions carried as data. The PR classifiesseed,external_catalog,toolandskill, or reports to the maintainer the ones D2 cannot decide. - The base-field display whitelist as one closed constant, each member admitted by D10's test against
FieldSchema. The per-field patch slot goes onObjectExtensionSchemahere, or the PR says it rides N-23a. - Generated baselines (authorable surface, JSON Schema, reference docs) regenerated; the
Metadata-protocollevel declared.
Tests. The schema accepts and refuses overrides shapes. Table tests cover the helpers (dotted ids, 13- and 14-character namespaces, the range). A type-list test pinned to the registry fails when an unclassified runtime-creatable type is added. A whitelist test checks every member exists on FieldSchema and none is structural.
Not here. Any door: the write gate (N-09), the read (N-10), the ensure door (N-13), uninstall (N-14), enforcing __c and the whitelist (N-23a/b).
Dedupe. Searched override declaration, __c suffix, custom namespace derived, display property whitelist: only #28236 and #28237 match.
Links back: #28236, #28390 (ruling 6542177429), ADR-0146 D2, D3, D6, D10, ADR-0048 §3.2.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 31, 2026
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
Claim: PM loop round 3 · 2026-10-31T14:36Z
Session:
session_017ihL2fi9zfJhUTubyJYb6VAccount:
marchtianBranch:
claude/issue-28404-override-declarationWorktree:
objectstack-issue-28404Domain:
domain:specSeat:
domain:spec#4Ruling-ref: the maintainer put this card ahead of the queue (「28404 插队」, 2026-10-31T14:35Z); ADR-0146 (Accepted on
main) D2, D3, D6, D10 and its "Decided here" items 1, 2 and 4; #28390 ruling A (6542177429): only the paired<base>.customdeclares an override. The card's item 3 namespace rule (no pair namespace past 13 characters, no truncation, no hashing) and item 5's classification ofseed,external_catalog,tool,skillare listed in the PR for the maintainer; the PR does not land before those are confirmed.File surface:
packages/spec/src/kernel/metadata-protection.zod.tsor a sibling new module for the sharedoverridesfield set and the pairing constants and helpers (the.customsuffix,customizationPackageId,isCustomizationPackage, the base of a pair, the pair predicate, the^<major>range,customizationNamespace), the overridable-type list besidepackages/spec/src/kernel/metadata-plugin.zod.ts's registry, the__cconstant andisExtensionFieldName, the display-property whitelist besidepackages/spec/src/data/field.zod.ts, the schemas of each D2 type that spreadsMetadataProtectionFieldsonly where the spread does not already carry the new set,packages/spec/src/data/object.zod.ts#ObjectExtensionSchemaonly if the per-field patch slot is taken here (else the PR says it rides N-23a),defineStack()only if the key would be dropped, the barrel exports, tests, regenerated spec artefacts and reference docs by repo tooling only, theui/packages/typesmirrors only where they are exhaustive copies, changeset (@objectstack/specminor). No door: N-09, N-10, N-13, N-14, N-23a/b are not taken (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: TIER_DEFAULT(opus) under the quota-exhaustion exemption (maintainer 「你没有fable了,使用 opus开发完之后等契约复审」),effort: highClause-②: yes (widening)
Metadata-protocol: added
Responsibility: a developer's
hotcrm.customin a repository and an administrator's Studio customization produce the same override declaration | one Zod field setoverrides: { package, basedOn }, one set of pairing constants and helpers, one overridable-type list, one__crule and one display whitelist, spelled once in spec | every door of epic #28236 (N-09, N-10, N-13, N-14, N-23a/b) andos validateThread-read: none
Premise: holds on
a8d2fd7f30:packages/spec/srchas nooverrideskey,.customconstant or__crule;deriveNamespaceFromPackageId('hotcrm.custom')returnscustom(namespace-prefix.ts:54);ObjectExtensionSchemais strict with no per-field patch slotSerial constraints cleared: no open PR touches
metadata-protection.zod.ts,namespace-prefix.ts,metadata-plugin.zod.ts,data/object.zod.ts,kernel/manifest.zod.tsor ADR-0146. #28390 (the ADR-0146 amendment recording ruling A,domain:skills, no PR yet) edits the ADR text only; this card reads the ruling, not that textobjectstack-fleet commentedon Oct 31, 2026
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
os-dev-report
{ "issue": 28404, "status": "done", "branch": "claude/issue-28404-override-declaration", "pr": "https://github.com/objectstack-ai/objectstack/pull/28467", "head": "e0253ce45c42f6688e112e25a7eed70b16de77c7", "session": "session_017ihL2fi9zfJhUTubyJYb6V", "premise_still_valid": true, "gates": { "derivation": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths, from the merge base) on head e0253ce45c: 112 commands", "run": "112 run, 112 exit 0, 0 exit 3, 0 non-zero; exit codes recorded before any pipe", "ran_reconciliation": "dispatch-gates --ran: 112 derived accounted for, 112 run, 0 NOT-MEASURED (derived zero), 0 UNRUN; exit 0", "also_run": [ "pnpm --filter @objectstack/spec check:generated :: exit 0 (14/14 current)", "packages/spec vitest run (full) :: exit 0, Test Files 865 passed, Tests 24491 passed, 1 todo", "pnpm --filter @objectstack/spec typecheck :: exit 0", "pnpm --filter @objectstack/runtime exec vitest run src/domains/packages-export-override-declaration.test.ts :: exit 0, 3/3 (on 0f9936f445)", "pnpm --filter @objectstack/runtime typecheck :: exit 0 (on 0f9936f445)" ], "earlier_findings_fixed": [ "check:llms-txt exit 1: kernel 30->31, total 212->213 (fixed in 395b5e16df)", "full spec suite: 3 files red on the new key (form reconciliation, flows translation face, file-description level-1 count), fixed in e8d402b7e9" ], "ci": "not cited: the PR is a draft just opened; the seat reads its check-runs" }, "line_budget": { "size": "M", "total": "+1912 -21 over 88 files", "hand_written_src": "+641 (18 non-test .ts under packages/spec/src)", "tests": "+723 (6 test files, 4 new)", "generated": "the remainder (references, api-surface, export-origins, declaration-map, authorable-surface, skill indexes, state-count shards)" }, "deviations": [ "file surface widened, each by a gate or suite pin: 12 liveness ledgers (props.overrides planned rows), packages/spec/llms.txt counts, ledger rows in src/system/metadata-form-zod-reconciliation.test.ts (8 root omit rows) and src/system/flows-translation-face.test.ts (1 noReader row)", "runtime test added: packages/runtime/src/domains/packages-export-override-declaration.test.ts (the export survival proof)", "the module header of override-declaration.zod.ts opens at level 2 so the description renderer's level-1 count stands", "first merge of origin/main: its regeneration was amended into the merge commit to carry the trailer pair (os-regen hook accepted it); the two later merges kept merge then regeneration as separate commits; the third merge resolved a text conflict in src/ui/app.zod.ts (override spread placed inside main's new superRefine(checkAppLandingItems))", "Fable review not run, per the order", "pnpm install re-run in the worktree after a merge (main added @objectstack/types to connector-mcp; the stale link made check:type-check-debt refuse with exit 3)" ], "files_changed": { "count": 88, "hand_written": [ ".changeset/28404-override-declaration.md", "packages/runtime/src/domains/packages-export-override-declaration.test.ts", "packages/spec/liveness/action.json", "packages/spec/liveness/app.json", "packages/spec/liveness/book.json", "packages/spec/liveness/dashboard.json", "packages/spec/liveness/dataset.json", "packages/spec/liveness/doc.json", "packages/spec/liveness/email_template.json", "packages/spec/liveness/flow.json", "packages/spec/liveness/mapping.json", "packages/spec/liveness/page.json", "packages/spec/liveness/report.json", "packages/spec/liveness/translation.json", "packages/spec/llms.txt", "packages/spec/src/automation/flow.zod.ts", "packages/spec/src/data/field-customization.test.ts", "packages/spec/src/data/field-customization.ts", "packages/spec/src/data/index.ts", "packages/spec/src/data/mapping.zod.ts", "packages/spec/src/kernel/customization-package.test.ts", "packages/spec/src/kernel/customization-package.ts", "packages/spec/src/kernel/index.ts", "packages/spec/src/kernel/override-declaration.test.ts", "packages/spec/src/kernel/override-declaration.zod.ts", "packages/spec/src/system/book.zod.ts", "packages/spec/src/system/doc.zod.ts", "packages/spec/src/system/email-template.zod.ts", "packages/spec/src/system/flows-translation-face.test.ts", "packages/spec/src/system/metadata-form-zod-reconciliation.test.ts", "packages/spec/src/system/translation.zod.ts", "packages/spec/src/ui/action.zod.ts", "packages/spec/src/ui/app.zod.ts", "packages/spec/src/ui/dashboard.zod.ts", "packages/spec/src/ui/dataset.zod.ts", "packages/spec/src/ui/page.zod.ts", "packages/spec/src/ui/report.zod.ts", "packages/spec/src/ui/view.zod.ts" ] }, "mcp_calls": "0", "api_writes": "2 - relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, run 41238481969 -> PR #28467); this os-dev-report comment via post-stamped (POST /repos/objectstack-ai/objectstack/issues/28404/comments)", "open_questions": [], "for_the_maintainer": [ "namespace: customizationNamespace answers <base_ns>_custom; a base namespace over 13 characters gets none (reason base_namespace_too_long), no truncation or hashing", "seed: excluded, basis undecided (D2 admits it, ADR-0126 section 3 calls it one-shot)", "datasource: excluded, basis undecided (ADR-0126 section 3 origin-gated; the card's census missed it)", "tool and skill: excluded, basis undecided (behavioural, no ledger switch or D9-style conditions)", "external_catalog: excluded, basis not_package_shipped (D2's first sentence), for confirmation", "prerelease base version: customizationDependencyRange('2.0.0-beta.1') answers ^2", "basedOn required (non-empty) whenever overrides is present" ], "out_of_scope_findings": [ "class: b · reach: GET /packages/:id/export omits emailTemplates (assemblePackageManifest excludes it), so an overriding email template in <base>.custom does not travel · evidence: packages/runtime export assembly · dedupe words: export emailTemplates skipped, package export email template · carrier: N-10 · noted, not filed", "class: none (loader decision) · reach: defineStack views: (defineView containers) and translations: (locale bundles) cannot carry overrides; the key lives on the item shapes · evidence: override-declaration.test.ts strict defineStack covers 11 item collections, not these two · carrier: epic #28236 read and Studio children · noted, not filed", "class: none · reach: none today · a flow clone of an overriding .custom flow would copy overrides onto a new name · carrier: N-21 · noted, not filed", "class: none · the card's census of undecided types missed datasource (the registry's only multi-line entry) · carrier: maintainer (for_the_maintainer item) · noted, not filed" ] }Reported 2026-10-31T20:22Z.
Generated by Claude Code
feat(spec): record the maintainer's rulings in the override classific…
objectstack-fleet commentedon Oct 31, 2026
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
Maintainer ruling recorded · 2026-10-31T21:05Z
田騏, 2026-10-31 20:44Z, verbatim: 「同意」 (project message
cmsg_01VmuUhXU9SSUzqJYyXSmYSiMH79UZBdPt7vGoKukHYjwz). That is agreement to the full analysis of the seven "For the maintainer" points of PR #28467 (project fileissue-28404/decision-analysis.md, sections 结论先行 and 对席位的落地备注).Binding, all seven confirmed as built:
base_namespace_too_long), with no truncation and no hashing.seedis excluded.datasourceis excluded.toolandskillare excluded.external_catalogis excluded.^<numeric major>(2.0.0-beta.1→^2).basedOnis required inside the block.Follow-ups agreed with it:
manifest.namespaceis longer than 13 characters, since it can then have no paired<ns>_customcustomization namespace (ADR-0146, ruled on #28404) #28473 (finding, blocked by feat(spec): one override declaration for code and Studio, with the .custom pairing helpers and the closed overridable-type list (ADR-0146) #28467).OVERRIDE_TYPE_CLASSIFICATIONnow uses decided bases.seed→adr_0126_one_shot,datasource→setup_owned_connection(ADR-0146 D1 plus ADR-0126 §3 origin-gated),tool/skill→behavioural_switch_first(action's path). Theundecidedvalue is removed and pinned absent. This is on PR feat(spec): one override declaration for code and Studio, with the .custom pairing helpers and the closed overridable-type list (ADR-0146) #28467, commit5d79f48d2f.basedOnrecords the base package'smanifest.versionthe override was taken from, not a history version of the item. Its.describe()is changed in the same commit. The ADR-0146 D2 wording is to be aligned in the docs(adr): ADR-0146 amendment — only the paired<base>.customdeclares an override; a dependent package adds under its own names (ruled A, withdraws rider 3) #28390 text amendment.PR #28467 lands through pm-dispatch after a contract-review PASS on its head and green CI.
Thread-read: none
objectstack-fleet commentedon Oct 31, 2026
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
Landed · 2026-10-31T23:31Z · PR #28467 was merged by the queue at 2026-10-31T23:30Z as
ba0084e414. The card closed byFixes, and this seat stripspm:dispatchedin the same act.What landed (epic #28236 N-08), all in spec:
overrides: { package, basedOn }on the thirteen ADR-0146 D2 types.basedOnis the base package'smanifest.version; it is recorded and never judged..custompairing helpers:customizationPackageId,customizationBasePackageId,isCustomizationPairOf,customizationNamespace(no pair namespace past 13 characters) andcustomizationDependencyRange(^<major>).OVERRIDE_TYPE_CLASSIFICATION, every row on a decided basis.__candBASE_FIELD_DISPLAY_WHITELIST.No door reads any of this yet: N-09, N-10, N-13, N-14 and N-23 consume the helpers.
Record:
manifest.namespaceis longer than 13 characters, since it can then have no paired<ns>_customcustomization namespace (ADR-0146, ruled on #28404) #28473 (the lint warning) is unblocked by this landing.