refactor(core)!: burn ledger batch 4 — 13 symbols stop wearing spec names (#3158) - #3188
Conversation
…core
Rescue commit after an OOM container restart — verification is NOT complete
yet (no ledger regen, no guard run, no tests, no mutation test).
Triage done, verdicts applied to source:
Derived (re-export from the spec — the only form that cannot drift):
StyleMap, ResponsiveStyles ← @objectstack/spec/ui (identical, ADR-0065)
RowHeight ← @objectstack/spec/ui (identical enum; the local
doc comment already CLAIMED spec canonicity)
CONTEXT_TOKENS ← @objectstack/spec/data (value- AND type-identical;
its comment said the copy was "temporary until the
spec exports it" — the installed spec already did)
CrudAffordances,
RowCrudPredicates ← @objectstack/spec/data (spec is strictly more
precise: Expression|ExpressionInput vs unknown)
Renamed (genuine name collisions — different concepts):
ChartSeries → ChartSeriesBinding (renderer-internal {dataKey,label}
vs spec's authored dataset binding)
ActionHandler → ActionRunnerHandler (client dispatch vs objectql server)
PluginDefinition → RegistryPluginDefinition (component registry vs package manifest)
ValidationError → SchemaNodeValidationError (schema-tree walk vs plugin manifest)
ValidationResult → SchemaNodeValidationResult
defineView → defineSystemView (freeze+mark vs spec's view-document
factory; app-shell had already
conflated the two in a comment)
resolveCrudAffordances → resolveEffectiveCrudAffordances
Its DEFAULTS table was a line-for-line copy of the spec's
CRUD_AFFORDANCE_DEFAULTS. The bucket half now DELEGATES to the spec's
resolveCrudAffordances(); only the #3391 effective-API-operation
intersection is objectui's, and the name now says so.
All new names were run through the guard's spec export set first —
SchemaValidationResult and SchemaValidationReport turned out to BE spec
exports, so the SchemaValidation* family was avoided (the PageComponentSchema
lesson from batch 3).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Jdef6ZFhJmiNRhNCd4DW3
21 assertions in packages/core/src/utils/__tests__/spec-symbol-batch4.test.ts: reference identity for CONTEXT_TOKENS (a copy passes toEqual, only toBe fails), mutual assignability for the five derived types, spec-delegation parity for the bucket table across all seven managedBy values plus the #2614 override forms, and absence-from-spec tripwires for all seven new names. The tripwire block also pins SchemaValidationResult / SchemaValidationReport as STILL spec-owned — they were the first choices for the validator pair and both turned out to be spec exports, so the next person to reach for the obvious name sees why it was not used. @object-ui/core drops out of the ledger entirely: 77 → 64 entries, 18 → 17 packages. Guard green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Jdef6ZFhJmiNRhNCd4DW3
Test files are not covered by `turbo type-check` (78/78 was green while these were still stale), so the renames surfaced only in the test run: PluginSystem.test.ts (PluginDefinition) and managedBy.test.ts (resolveCrudAffordances). Worth recording as a method note — a green type-check does NOT mean the rename is complete. The plugin-charts comments naming `ChartSeries.type` / `.stack` are left untouched: those refer to the SPEC's ChartSeries, not the renamed core type. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Jdef6ZFhJmiNRhNCd4DW3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
…symbol-ci-rule-4r3xlc-batch4
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
main moved under this branch: it bumped @objectstack/spec to 17.0.0-rc.1 and added two ALLOW entries of its own (@object-ui/types:FieldNode, @object-ui/app-shell:InboxNotification). Both preserved — the DEBT block was REGENERATED with `--ledger` rather than hand-merged, per the burn-down rules, so the ordering is canonical and no other package's entries were disturbed. The spec bump matters more than a routine merge: every one of this batch's six DERIVE verdicts was triaged against rc.0. The parity test re-checks mutual assignability against whatever spec is installed, so rc.1 was validated, not assumed — all 21 assertions still pass. Guard: 8 declared dialects, 62 untriaged collisions in 17 packages. turbo build --filter=@object-ui/core then type-check --force: 80/80. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Jdef6ZFhJmiNRhNCd4DW3
Post-merge re-verification (spec 17.0.0-rc.1)
The spec bump was the part that actually needed re-checking. All six DERIVE verdicts in this batch were triaged against rc.0, and a derive is only correct as long as the spec's shape still matches. Because the parity test asserts mutual assignability against whatever spec is installed rather than against a transcribed shape, rc.1 was validated rather than assumed — all 21 assertions pass unchanged. That is the property the burn-down was supposed to buy, tested on its first real occasion.
The two extra dialects and the two fewer collisions are The 21 file-level errors are unchanged in count and remain pre-existing (see the note in the description; Generated by Claude Code |
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
Burns down ledger batch 4 of objectstack#4115 — the thirteen
@object-ui/corecollisions of objectui#3158. Closes objectui#3158 (on merge).coreis the render kernel every plugin depends on, so the cross-package rule from objectui#3100 applied throughout:turbo build --filter=@object-ui/corefirst, thentype-check --forcedownstream — a per-packagetscresolves the pre-change dist and goes green on a break that CI would catch.Verdicts
Six derived (the name is now bound to the spec at compile time), seven renamed (genuinely different concepts), zero ALLOW.
Derived — re-exported from the spec
StyleMap,ResponsiveStyles@objectstack/spec/uiSchemaRendereras the consumerRowHeight@objectstack/spec/uiCONTEXT_TOKENS@objectstack/spec/dataCrudAffordances,RowCrudPredicates@objectstack/spec/dataRenamed — the spec owns the name for something else
ChartSeriesChartSeriesBindingname, nodata)ActionHandlerActionRunnerHandler(ctx) => unknownPluginDefinitionRegistryPluginDefinitionid/slug/staticPath/install hooks)ValidationErrorSchemaNodeValidationErrorfield, no severityValidationResultSchemaNodeValidationResultdefineViewdefineSystemViewViewSchema, returns a validatedViewresolveCrudAffordancesresolveEffectiveCrudAffordancesWhat the copies were actually hiding
Three of them said so in their own comments — which is the failure class this issue is about, not a hypothetical:
CONTEXT_TOKENScarried "mirrored here … because the installed@objectstack/specpredates that export. Keep the two in sync; the duplication is temporary until the next coordinated release." The installed spec already exported it, and the copy was byte-identical — so it passed every value comparison and every behavioural test for the entire interval in which its stated reason was false. This is the cleanest possible demonstration of why reference identity is the only check that works:toEqualpassed on the fork and would pass on a re-fork tomorrow.RowHeightadvertised itself as "the spec'sRowHeightSchemavocabulary" while being a hand-written union. It happened to still be correct; nothing would have caught the day it stopped being.managedBy.tsdescribed itself as a "UI-side mirror of the framework'sresolveCrudAffordances()" and carried its ownDEFAULTStable — a line-for-line copy of the spec'sCRUD_AFFORDANCE_DEFAULTS, plus a copy of its override parser (normalizeRowCrudOverride).And
defineViewhad already planted the premise in a neighbouring package:app-shell/…/view-item-normalize.tsdocuments a "per-objectdefineView({ list, form, listViews, formViews })document" — which is the spec'sdefineView, not core's freeze-and-mark helper that the name resolved to. Exactly the #2901 mechanism.resolveCrudAffordances— delegate, don't just renameRenaming alone would have left the copied bucket table in place. The bucket/
userActionshalf now delegates to the spec'sresolveCrudAffordances(), so the table has exactly one definition on the platform. What stays objectui's is the part the spec has no notion of — intersecting that matrix with the server-resolved effective API operation set (#3391) so the UI never offers a button the server would 405 — and the new name says that instead of claiming to be the spec's function.Pinned by a parity test that compares objectui's output to the spec's across all seven
managedByvalues plus the #2614 override forms: if anyone reintroduces a local table, it diverges the first time the spec re-buckets an object class.Deriving
RowCrudPredicatesalso tightens it — the local copy typedvisibleWhen/disabledWhenasunknownwhere the spec types themExpression | ExpressionInput. That was imprecision, not a deliberate dialect, and it caused zero downstream fallout.Method notes worth adding to the templates
1. The "check the new name first" rule fired again — twice, on the obvious names. The validator pair's first choice was
SchemaValidationError/SchemaValidationResult.SchemaValidationResultis itself a@objectstack/spec/contractsexport; the fallbackSchemaValidationReportis too. The wholeSchemaValidation*namespace is spec-occupied, henceSchemaNodeValidation*. This is the third independent confirmation afterPageComponentSchema(objectui#3074) and batch 3's three rejected candidates — and the pattern is now clear enough to state: the name that reads as most natural is the one most likely already taken, because the spec named the same concept the same way.The tripwire test inverts the pin for those two rejected names (asserting they are still spec-owned), so the next person to reach for the obvious name reads the reason instead of rediscovering it through a red guard.
2. A green
turbo type-checkdoes NOT mean a rename is complete — test files are not type-checked. 78/78 was green whilePluginSystem.test.tsandmanagedBy.test.tsstill referenced the old names; only the test run surfaced them. Worth stating explicitly in the construction rules next to the "don't global-replace" rule.3. Global replace corrupts the very comments the burn-down adds. A scoped
re.subover two files rewrote the contrast comments — "NOT the spec'sValidationError" became "NOT the spec'sSchemaNodeValidationError", i.e. a fresh misdescription manufactured by the fix for misdescription. Caught on review, restored. The existing rule says don't global-replace across the repo; it should also say don't global-replace within a file that documents the collision.Verification
Re-run in full after merging
main(which bumped the spec to 17.0.0-rc.1 and added two ALLOW entries of its own — both preserved; theDEBTblock was regenerated rather than hand-merged). All six derive verdicts still hold under rc.1 — the parity test re-checks mutual assignability against the installed spec, so the bump was validated rather than assumed.Ledger:
--ledgerregenerated and diffed line by line —removedis exactly the 13 of this batch,addedempty, and every other package byte-identical (several batches are in flight in parallel).@object-ui/coreleaves the ledger entirely: 62 untriaged collisions in 17 packages, down from 77/18 at this branch's base.Guard: green (
8 declared dialects, 62 untriaged collisions in 17 packages).Cross-package:
turbo build --filter=@object-ui/coreto rebuild dist, thenturbo type-check --force→ all packages green.Tests: 9689 assertions passing, 0 failing across 821 files. (21 files report a file-level error rather than an assertion failure;
plugin-chartswas checked against a detachedorigin/mainand shows the identical1 failed | 25 passed (26)/400 passedshape there, so these are pre-existing and unrelated — the count also drifted 23→21 across runs with no code change, under memory pressure from parallel worktrees.) The affected packages were also run in isolation and are fully green —core61 files / 1335 assertions, andapp-shell+plugin-form+plugin-grid+plugin-list347 files / 3127 assertions.New coverage: 21 assertions in
packages/core/src/utils/__tests__/spec-symbol-batch4.test.ts— reference identity forCONTEXT_TOKENS(toBe, deliberately:toEqualcannot tell a copy from a re-export), mutual assignability for the five derived types, spec-delegation parity for the affordance resolver, and absence-from-spec tripwires for all seven new names.Mutation test: two mutations, both red by name and by file:line.
ChartSeriesBindingrenamed back toChartSeries→ guard points atpackages/core/src/utils/chart-series.ts:44.CONTEXT_TOKENSre-forked as the byte-identical local copy that was just deleted → guard points atpackages/core/src/utils/filter-tokens.ts:61, and the new parity test fails withTwo identical-looking arrays, and the other 20 assertions in the same file still passed. That is objectui#3003's point reproduced on demand: a faithful copy satisfies every value comparison, and only reference identity separates a re-export from a fork.
Both reverted; guard and tests back to green.
Cross-package decisions recorded
Registered on objectstack#4115 (comment) so batches 6 and 7 inherit them:
ValidationError/ValidationResultare three different concepts, not one shared name. core validates an SDUI schema tree (path+ severity),sdui-parservalidates a compiled tree against a manifest ({diagnostics, requires, bindings}),data-objectstack's is anErrorsubclass wrapping HTTP 400. What is fixed is the convention —«what-is-validated»Validation«Error|Result»— withManifestValidationResultsuggested for batch 7 (verified free) and batch 6 to pick under the same rule, running the guard first.ChartSeriesis three different concepts, confirmed by re-reading all three: spec = authored dataset binding,@object-ui/types= inline static data (ChartDataSeries, batch 1), core = renderer-internal binding →ChartSeriesBinding. Theplugin-chartscomments namingChartSeries.type/.stackrefer to the spec's type and are deliberately untouched.Not done here
content/docs/releases/is untouched; the changeset (.changeset/core-spec-symbol-burn-down.md) is this PR's input to the release notes.