Repository navigation
feat(spec): one override declaration for code and Studio, with the .custom pairing helpers and the closed overridable-type list (ADR-0146) - #28467
Conversation
…e overridable-type list, the __c reservation and the display whitelist
ADR-0146 D2, D3, D6, D9, D10: one Zod field set overrides { package, basedOn }
spread on the thirteen overridable types; the pair's id, namespace and
dependency-range helpers; the closed classification of every registry type;
the __c suffix and the base-field display-property whitelist.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
…geset Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
…n, not a refinement Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
…the new tests Regenerated skill references, api-surface, export-origins, declaration-map, reference docs, the strictness-ledger counts and the authorable surface for the override declaration and the pairing helpers. The two new test files now pass the test typecheck without a debt entry. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
The merged tree's reference docs and authorable surface are regenerated in this commit (the merge took them without a text merge). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
The kernel domain now holds 31 schema modules and the package 213. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
… ledgers The full spec suite named three pins the new key reaches: - the metadata form reconciliation: `overrides` is accepted by eight schemas whose hand-written forms do not offer it. Each gets a root ledger row: the declaration is `planned` and Studio writes it on a customization, so a hand-filled control is not how it is authored. - the flows translation face: `flow.overrides` is a base package id and a version, never drawn for a person running the flow. - the description renderer's count of level-1 module openers: the new module's header now opens at level 2, so the count stands. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Takes the sibling spec cards that landed since the last merge; the generated artefacts are regenerated from the merged tree in the next commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Takes the sibling spec cards that landed since the last merge (resolving the app schema: the override spread now sits inside the landing refinement); the generated artefacts are regenerated from the merged tree in the next commit. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
📓 Docs Drift CheckThis PR changes 1 package(s): 22 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 10 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 168 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 bfbecfe77b5507f481e0fbe0095956fca7b57e52 && git checkout bfbecfe77b5507f481e0fbe0095956fca7b57e52
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5e706e7a9e938925de326ed52b6d65475f43d095 743ac88b6ce187f37978cf549c3b63288a31d7b5 && git checkout -B drift-repro 5e706e7a9e938925de326ed52b6d65475f43d095 && git merge --no-ff 743ac88b6ce187f37978cf549c3b63288a31d7b5
node scripts/docs-audit/affected-docs.mjs --json 5e706e7a9e938925de326ed52b6d65475f43d095
|
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V 维护者速读(席位终稿 · head
|
…property pins The override declaration adds one top-level property to `action`, `app`, `dashboard`, `dataset`, `flow`, `page` and `report`; the degenerate-derivation suite pins each served count, so each moves by one and the `action` sample names the new key. The excluded types keep their counts. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V Head moved by a seat test fix · 2026-10-31T20:44Z ·
The fix is one test file, with one dated comment on each pin. Seat-local: that file 18/18 (7 red before the fix). The dev's gate battery did not derive this package's suite, so CI is the first run of it. No other check was red on Contract-review-handoff: triage — CONTRACT_REVIEW_TIER unavailable (HTTP 429 "You've reached your Fable limit", 2026-10-30T04:26Z) · head e55a4e7 · supersedes the hand-off on e0253ce (no review was claimed on it) |
|
triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-hold: session_01L9qibw58ehQ2gRHP2h2Puz · head e0253ce · Test Core 7/8 red: seven /meta/types count pins in metadata-protocol are one short of the new key · until a green head Claim 6548930392 stands; no record on this head (a PASS is never written on a red head). Read in full at 2026-10-31T20:50Z: card #28404 (claim 6544804569, dev report 6548679183), the seat hand-off 6548702768, the PR body, the diff (88 files, +1912/−21 against merge-base 80a9f99, merge-tree clean; every hand-written spec hunk, tests by case, generated files by count), ADR-0146 D2 / D3 / D6 / D9 / D10 and its "Decided here" list on
Owner: |
…ation and `basedOn` The maintainer agreed the seven open points on #28404. Two wording changes follow from that ruling: - `OVERRIDE_TYPE_CLASSIFICATION`: `seed`, `datasource`, `tool` and `skill` stay `excluded`, but on a decided basis instead of `undecided`. `adr_0126_one_shot` (seed), `setup_owned_connection` (datasource: ADR-0146 D1 plus ADR-0126 §3 origin-gated) and `behavioural_switch_first` (tool, skill: action's path). The `undecided` basis is gone, and a test pins that no row carries it. - `overrides.basedOn` records the base package's `manifest.version` the override was taken from, not a history version of the item. It is still recorded and never judged. Reference pages regenerated. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
|
triage#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract review (delta)Served-tier: Delta judgment
Implemented-by: VERDICT: PASS (delta) |
|
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V Landing stroke (re-arm after the 22:28Z
|
|
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U ⛔ The scheduled full run is red after this PR landed: p0 #28491 · 2026-11-01T00:54ZThis PR's landing Landing seat ( Generated by Claude Code |
domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V
🤖 Generated with Claude Code
https://claude.ai/code/session_017ihL2fi9zfJhUTubyJYb6V
Fixes #28404
Clause-②: yes (widening)
Metadata-protocol: added
维护者速读
overrides: { package, basedOn },加在 13 种可覆盖的元数据上;另加.custom配对包的命名、命名空间、依赖范围规则,可覆盖类型的封闭清单,__c扩展字段后缀和基础字段可改的显示属性白名单。hotcrm.custom,管理员在 Studio 里定制,两边要写出同一份声明,后面的写入关、读取、导出和os validate才能读同一样东西,不会各自拼一套「这一项覆盖了 HotCRM 的哪一项」。packages/spec/src/kernel/customization-package.ts的OVERRIDE_TYPE_CLASSIFICATION(哪些类型能覆盖,有 4 个待您定);②customizationNamespace(基础命名空间超过 13 个字符就没有配对命名空间);③packages/spec/src/data/field-customization.ts的白名单。pnpm --filter @objectstack/spec exec vitest run src/kernel/override-declaration.test.ts src/kernel/customization-package.test.ts src/data/field-customization.test.ts,预期三个文件全绿。For the maintainer
ADR-0146 does not decide these. Ruled 2026-10-31 20:44Z: the maintainer agreed to all seven as built (「同意」), together with the follow-ups recorded after the list:
customizationNamespace(baseNs)answers<base_ns>_custom; a base namespace longer than 13 characters gets no pair namespace ({ ok: false, reason: 'base_namespace_too_long' }) and the ensure door will refuse that base with a sentence. No truncation, no hashing. Nothing in the repository is affected today (the longest base namespace isfeature_flags, 13).seed— runtime-creatable and shipped by packages, so D2's first sentence admits it, but ADR-0126 §3 puts it outside ("one-shot"). Classifiedexcluded, basisadr_0126_one_shot(ruled).datasource— the same reading (ADR-0126 §3: "origin-gated"); D2 names it on neither side. Classifiedexcluded, basissetup_owned_connection(ADR-0146 D1 gives datasources to Setup; ADR-0126 §3 origin-gated; ruled). The card's census listed four undecided types and missed this one: its registry entry is the only multi-line entry, which a line-wise reading cannot see (the registry's own comment warns about this).toolandskill— behavioural (ADR-0126 §3 pre-charts them for Regime C), with no ledger switch and no D9-style conditions written for them. Classifiedexcluded, basisbehavioural_switch_first:action's path, the ledger switch first and an override door later (ruled).external_catalog— decided by D2's first sentence (packages never ship one), classifiedexcluded, basisnot_package_shipped. Listed so you can confirm the reading.customizationDependencyRange('2.0.0-beta.1')answers^2(the major of the numeric core, the ADR's words read literally). The ADR-0141 D2 install check cannot read a prerelease installed version, so a pair whose base sits at a prerelease is re-installable once the base reaches a release of that major.basedOnis required inside the block. The ADR's shape names both keys; the PR requires both to be non-empty strings whenoverridesis present.Follow-ups the ruling also agreed:
OVERRIDE_TYPE_CLASSIFICATIONstaysundecided; the basis value is removed and a test pins that no row carries it (commit5d79f48d2f).basedOnrecords the base package'smanifest.versionthe override was taken from, not a history version of the item; its.describe()says so. The ADR-0146 D2 wording is 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.os lint/os validatewarn when a manifest namespace is longer than 13 characters (that package cannot be customized).What changes
overrides: { package, basedOn }(packages/spec/src/kernel/override-declaration.zod.ts,OverrideDeclarationFields): one closed field set, spread on the 13 typesOVERRIDE_TYPE_CLASSIFICATIONclassifiesoverridable:view(the ViewItem record and the flattened overlay; not thedefineViewcontainer, see the acceptance notes),dashboard,report,translation(the item),email_template,page,app,dataset,doc,book,mapping,flow,action. It is a separate spread, not part ofMetadataProtectionFields: that envelope is spread into excluded types and is what registration and clone strip as loader-stamped state.packagerefuses a.customid.basedOn's.describe()says it is recorded and never judged.packages/spec/src/kernel/customization-package.ts):CUSTOMIZATION_PACKAGE_SUFFIX,customizationPackageId,customizationBasePackageId,isCustomizationPackage,isCustomizationPairOf(ruling A: only the paired<base>.customdeclares),customizationNamespace,customizationDependencyRange. Each derivation answers{ ok: true, value }or{ ok: false, reason }. Dotted ids are covered. The namespace answer is judged againstManifestSchema.shape.namespaceand the id againstMANIFEST_ID_PATTERN, so the helpers restate neither grammar.OVERRIDE_TYPE_CLASSIFICATIONis total overMetadataTypeat compile time and pinned toDEFAULT_METADATA_TYPE_REGISTRYin both directions by a test.objectisextend_only;permission,position,capabilityare excluded (D6); the code-only types are excluded;flowandactioncarry their D9 conditions as data.OVERRIDABLE_METADATA_TYPES,getOverrideTypeClassification,isOverridableMetadataTyperead it.__cand the display whitelist (packages/spec/src/data/field-customization.ts):EXTENSION_FIELD_SUFFIX,isExtensionFieldName,BASE_FIELD_DISPLAY_WHITELIST= fieldlabel,description,inlineHelpText,placeholder, and optionlabel, each measured against D10's test. Only the class D10 names; other display-only keys may pass the same test and enter by decision.ObjectExtensionSchemarides N-23a. A slot that the merge (mergeObjectDefinitions) does not apply would be an authorable key that nothing reads, so it lands together with that merge.planned) foroverrideson the 12 ledgers that walk it, and the count shards; thellms.txtschema counts (kernel 31, total 213); regenerated spec artefacts./meta/typesproperty counts inpackages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.tsmove by one on the seven overridable types that suite pins (action,app,dashboard,dataset,flow,page,report); the excluded types keep theirs (seat commite55a4e7c6b, afterTest Core (7/8)went red one0253ce45c).omitrow foroverrideson the eight types with a hand-written metadata form (metadata-form-zod-reconciliation.test.ts; Studio writes the declaration on a customization, so no hand-filled control is offered while it isplanned), and anoReaderrow forflow.overridesin the flows translation face.Tests
packages/spec/src/kernel/override-declaration.test.ts: the shape accepts and refuses (missing, empty, unknown key,.custompackage); every overridable type keepsoverridesthrough its metadata schema (the save door's parse) and refuses a malformed block; the excluded types with a create seed refuse the key by name;defineStack()(strict) keeps it on all 11 item collections; the export'sviewItems:channel keeps it for a ViewItem and an overlay.packages/spec/src/kernel/customization-package.test.ts: table tests for the id helpers (dotted ids, a pair of a pair, a dependent package that is not the pair), the namespace (13 and 14 characters, pinned against the grammar), the range, and the type list pinned to the registry.packages/spec/src/data/field-customization.test.ts: every whitelist member exists onFieldSchema(or its option schema) and takes text; none is in the structural list, and every structural key named is a realFieldSchemakey.packages/runtime/src/domains/packages-export-override-declaration.test.ts:GET /packages/<base>.custom/exportcarriesoverrideson a flow and a view item and strips provenance; both re-parse with the block intact.@objectstack/specsuite one0253ce45c: 865 files passed, 24491 tests passed, 1 todo. An earlier run named three existing pins the key reaches (the form reconciliation, the flows translation face, the description renderer's level-1 count); they are recorded in their ledgers above and the module header opens at level 2.flow.zod.ts, the strictdefineStack()in the first file refusesoverridesonflows.0and the file goes red; restored fromHEAD.Acceptance notes
viewviadefineStack()andtranslationvia the stack. The stack authors views asdefineViewcontainers (views:) and translations as locale bundles (translations:); both are aggregates that are expanded or merged into items at load. The declaration is on the item shapes theviewandtranslationmetadata types parse, so it survives the metadata save and the export, but a repository author cannot write it through those two stack collections today. Whether a container-level declaration fans out to its expanded items is a loader decision for the read and Studio cards, not taken here.emailTemplates(assemblePackageManifestexcludes it withdatasources), so an overriding email template in.customdoes not travel throughGET /packages/:id/exporttoday. Pre-existing; the effective-read card carries it.appschema now carries the landing refinement from main (feat(spec): an app can name a landing per audience with landing #28449); the override spread sits inside it, resolved in the merge.viewliveness row: theviewledger governs thedefineViewcontainer (ViewSchema), which does not carry the key.Verification
Independent review: none run (quota); the PR waits for the seat's contract-review hand-off.
Gates (head
e0253ce45c):node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives 112 commands. All 112 ran in one pass and exited 0, each exit code recorded before any pipe.--ranreconciliation: 112 derived, 112 run, 0 not measured, 0 unrun. The full farm is CI's.Also run:
pnpm --filter @objectstack/spec check:generated: all 14 artefacts current after regeneration on each merge.pnpm --filter @objectstack/spec typecheck: exit 0.pnpm --filter @objectstack/runtime typecheck: exit 0.0f9936f445. The later merge brought only the app landing, components and docs.Earlier passes found two real fixes, both in this PR: the
llms.txtschema counts, and the three ledger pins. Gates that refused with exit 3 because a prerequisite build was missing were re-run after the build and are not counted as passes.Generated by Claude Code