Skip to content

[11.0][A2] Remove dead author-facing metadata properties (ADR-0049 enforce-or-remove) #2377

Closed

Description

@os-zhuang

Part of 11.0 breaking-change batch (#2364), ADR-0049 enforce-or-remove.

Remove author-facing metadata properties that pass schema validation but have no runtime consumer (verified against packages/spec/liveness/*.json + grep of plugins/services/objectql/runtime). Authoring them is a false affordance — especially dangerous for AI-authored metadata.

Verified-dead set (remove)

  • field (spec/src/data/field.zod.ts): vectorConfig, fileAttachmentConfig, dependencies, caseSensitive
  • object (spec/src/data/object.zod.ts): versioning, partitioning, softDelete, recordName, keyPrefix, tags, abstract, isSystem, active, dead enable.* flags (trackHistory, searchable, files, feeds, activities, trash, mru), search, defaultDetailForm
  • action (spec/src/ui/action.zod.ts): type: 'form' (enum value, no renderer)
  • agent (spec/src/ai/agent.zod.ts): tenantId, planning.strategy, planning.allowReplan (keep planning.maxIterations — live)
  • dataset (spec/src/ui/dataset.zod.ts): measures.certified

Explicitly KEEP (LIVE — do not remove)

field.currencyConfig, object.apiEnabled, action.disabled/shortcut/bulkEnabled, agent.access/permissions, skill.triggerPhrases/permissions.

Why this cascades

zod schemas are strict — if any example sets one of these, removing the field breaks the example build. Each property: grep examples first (e.g. showcase may set dataset.measures.certified: true).

Per-property checklist

  • Remove zod property (+ its dedicated sub-schema export + inferred type if only used here)
  • Flip the packages/spec/liveness/<type>.json entry to removed
  • Update/remove spec tests asserting the property
  • Migrate any example that set it
  • pnpm --filter @objectstack/spec gen:api-surface + commit snapshot
  • Add a 11.0 changelog/migration note (esp. aspirational versioning/partitioning/softDelete)

Risk: low–medium; many entries → split into 1–2 PRs (field/dataset first, object aspirational blocks with migration note).

Activity

  1. os-zhuang commented on Jun 27, 2026

    @os-zhuang
    ContributorAuthor

    De-risking complete — scope split (safe vs risky) + execution plan

    Verified across framework + objectui + cloud + hotcrm + templates (BSD-grep caveat: must use [[:space:]], not \s; control label: = 9887 confirms the grep works):

    Every dead candidate is set 0× and read 0× in configs/UI across all repos. So removal breaks no config. The real risk is spec's own tests + TS read-access — and that splits the set cleanly:

    ✅ Safe to remove (unique names, findable test footprint)

    • field: vectorConfig (+VectorConfigSchema+types), fileAttachmentConfig (+FileAttachmentConfigSchema+types), maxRating, caseSensitive, referenceFilters — test footprint only in field.test.ts/widget.test.ts (vectorConfig + fileAttachmentConfig have whole describe blocks to delete; maxRating/caseSensitive trivial; referenceFilters = 2 fixture lines).
    • object: partitioning, softDelete, recordName, keyPrefix, defaultDetailForm (+ their sub-schemas) — 0–1 spec test files.
    • dataset: measures.certified.
    • agent: tenantId — findable in agent.zod but the NAME is overloaded in tests (see risky).

    ⚠️ Risky — DO NOT bulk-remove (names overloaded; can't separate spec-prop test usage from noise)

    field.index, field.columnName, field.dependencies, object.versioning, object.tags, object.active, object.abstract, object.isSystem, object.search, object.enable.* (trackHistory/searchable/files/feeds/activities/trash/mru), agent.tenantId, action.timeout.

    • dependencies 18 test files, versioning 8, tenantId 21, tags 26, active 21, search 9, index 5 — almost all unrelated (package deps, storage versioning, tenant context, array index, full-text search). Removing risks breaking tests that can't be reliably located.
    • These are unenforced but harmless; recommend either leaving them or handling each with per-occurrence inspection in a dedicated pass — NOT worth the blast radius in a batch.

    Recommended execution

    Per-area PRs for the safe set only (field → object → dataset), each: remove zod prop + sub-schema + type exports → update packages/spec/liveness/<type>.json → delete the matching test cases → gen:api-surface → full vitest run in spec (not just the file). Leave the overloaded-name props.

  2. os-zhuang commented on Jun 27, 2026

    @os-zhuang
    ContributorAuthor

    First batch landed → #2402 (merged). Removed the truly-trivial, zero-non-spec-footprint dead props: field caseSensitive, maxRating; object partitioning (+PartitioningConfigSchema), defaultDetailForm. Ledgers + api-surface updated; spec 6595 + Test Core + liveness all green.

    Lesson / scope refinement: softDelete and measures.certified were in my 'safe' list but turned out to be set in non-spec test fixtures (mcp; analytics ×7) → Test Core went red; both deferred (need fixture migration). So the remaining #2377 work = those two + the overloaded-name set (tags/active/versioning/dependencies/index/columnName/enable.*/agent.tenantId/action.timeout). Reconfirms: for spec-prop removal, grep ALL package tests (not just spec) for set-usage before removing.

  3. self-assigned this
    on Jul 18, 2026
  4. os-zhuang commented on Jul 18, 2026

    @os-zhuang
    ContributorAuthor

    Re-evaluation + next slice → #3176 (draft).

    Re-audited the remaining dead set against the current liveness ledgers on main. Findings that update the earlier triage:

    • The "overloaded-name / hard to grep" blocker is gone: every dead prop now carries a status: dead + evidence entry in packages/spec/liveness/*.json, so removal no longer depends on grepping ambiguous names.
    • The real remaining gate is the Studio metadata-authoring forms (*.form.ts) + their generated i18n bundles. Many dead props are surfaced in the authoring form, so removing them cascades into i18n regeneration — those are deferred as a coordinated follow-up.

    Removed in #3176 (dead and not surfaced in any authoring form → self-contained)

    • field: vectorConfig, fileAttachmentConfig, dependencies
    • object: versioning, softDelete, search, recordName, keyPrefix (+ sub-schemas; each now a rejecting tombstone in UNKNOWN_KEY_GUIDANCE)
    • action: timeout
    • agent: planning.strategy, planning.allowReplan (keep planning.maxIterations)
    • dataset: measures.certified

    Ledgers + README table + api-surface.json + json-schema.manifest.json updated; fixtures migrated (analytics/rest/lint/cli). Spec 6875 green, liveness gate ✓, api-surface ✓. Cuts 16.0.0 (major changeset).

    Deferred (dead, but entangled — need a follow-up)

    • Authoring-form-surfaced (removal ⇒ i18n bundle regen): field index/columnName/referenceFilters; object tags/active/isSystem/abstract/enable.searchable/enable.trash/enable.mru; agent tenantId.
    • action.type:'form' — has a dedicated build-time lint (lint-view-refs.ts) + a first-party app-showcase action; removing it needs a UX decision (what the "Log Time" action becomes), not a mechanical strip.
    • field.columnName — additionally referenced by the ADR-0062 D7 external-object lint (whose comment frames it as the "managed-object mechanism", contradicting the ledger's dead), so it needs an owner call, not a sweep.

    All deferred entries stay dead + authorWarn so authors still get the build-time warning. Note: several items in the original issue body are already LIVE and must not be removed — enable.trackHistory/files/feeds/activities (plugin-audit enforced) and agent.planning.maxIterations.


    Generated by Claude Code

  5. os-zhuang commented on Jul 22, 2026

    @os-zhuang
    ContributorAuthor

    Final slice → #3414 (draft). This closes out the A2 removal scope; the issue auto-closes on merge.

    Full disposition of the original set across the three waves (#2402 → #3176 → #3414):

    Removed

    Reclassified LIVE — kept (2026-06 audit misses, correctness fixes to this issue's original list)

    • object.isSystem (plugin-sharing effectiveSharingModel default), enable.searchable (global-search opt-out), enable.trackHistory/files/feeds/activities (plugin-audit enforced), action.type:'form' (objectui ActionRunner.executeForm — enforce won over remove; also fixed "Log Time does nothing"), agent.planning.maxIterations, field.currencyConfig, object.apiEnabled, action.disabled/shortcut/bulkEnabled, agent.access/permissions, skill.triggerPhrases/permissions.

    Result

    packages/spec/liveness/object.json (and field/action/agent/dataset) now carry 0 dead entries in this issue's scope. Remaining aspirational-config work for other types (theme, translation, webhook, portal, …) stays tracked in #1893.

    Bonus from #3414's .strict(): two latent unknown-key bugs surfaced immediately — sys_secret and sys_setting_audit had been authoring a never-existent enable.audit flag, silently stripped until now.


    Generated with Claude Code


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions