feat(spec): open RecordActivityProps.types to author-contributed activity kinds - #12408
Conversation
…vity kinds Fixes #11658 Executes the 2026-08-24 maintainer ruling on #11507 (direction 4: the sys_activity.type vocabulary is OPEN and author-extensible; every closed map over it is the bug) on the authoring surface: the record:activity types filter element widens from a closed z.array(FeedItemType) to z.union([FeedItemType, z.string().min(1)]) — built-in kinds stay visible as guidance (autocomplete / JSON-Schema anyOf), author-contributed kinds become nameable, nothing is validated against the built-in set. Also corrects the feed.zod.ts docblock's 'no backend dependency' framing and regenerates the derived docs pages. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E5LFCYBJ3q2s6yW6oMLxwy
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. What this run could not see
Coarse fallback — 126 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 8173cebffa09181ee0e6a243132893bea87f0983 && git checkout 8173cebffa09181ee0e6a243132893bea87f0983
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b000ab59bba509530595b1f2fb9604a4046ed55f b451c43db21de6419aa6ada1fa84b1ea0e98467a && git checkout -B drift-repro b000ab59bba509530595b1f2fb9604a4046ed55f && git merge --no-ff b451c43db21de6419aa6ada1fa84b1ea0e98467a
node scripts/docs-audit/affected-docs.mjs --json b000ab59bba509530595b1f2fb9604a4046ed55f |
Fixes #11658
What
RecordActivityProps.types— the authorablerecord:activityfeed filter, also embedded asRecordChatterProps.feed.types(thefeedprop delegates toRecordActivityProps, so the same change propagates there) — was a closedz.array(FeedItemType)over the vocabulary the 2026-08-24 maintainer ruling on #11507 declared OPEN and author-extensible (verbatim: "every closed map over this vocabulary is now the bug"). The element is now:scheduled, which hotcrm writes today, or any future kind authored through ADR-0052 §5b.2) is nameable in the filter.FeedItemTypebranch keeps the 13 built-in kinds visible as guidance — editor autocomplete, and ananyOfmember of the generated JSON Schema. Measured, not assumed:json-schema/ui/RecordActivityProps.json(generated, gitignored tree) now emitsitems.anyOf = [enum(13 built-ins), string(minLength 1)], which is how JSON-schema-driven authoring tools keep suggesting the built-ins without re-closing the domain.Also in scope (flagged on the card 2026-08-24T11:07Z): the
feed.zod.tsmodule docblock no longer claims these enums are "pure UI configuration … with no backend dependency" —FeedItemTypeis the TARGET of the map UI consumers apply to the opensys_activity.typecolumn: a backend coupling, not a backend import. The generated reference pages (content/docs/references/data/feed.mdxincluding its "Allowed Values" framing context, andcontent/docs/references/ui/component.mdxfor all threetypesrows) moved by regeneration only (check:generated --fix), never hand-edited.Alignment with the landed upstream shape
sys_activity.typea closed platform vocabulary or an author-extensible one? Both readings are true of the code today #11507 declaration half (PR fix(audit): declare sys_activity.type as an open, author-extensible vocabulary #11659): the column declared open at the source, description-slot mechanism.2d8dd8d5):SYS_ACTIVITY_BUILTIN_TYPES+SysActivityBuiltinTypepublished from@objectstack/spec/dataas a plainas consttuple, deliberately not az.enum, so the published set cannot re-close the vocabulary. This PR mirrors that logic on the authoring surface: the built-in set guides, never validates.FeedItemTypeitself stays a closedz.enum— it is the UI-kind map target, not the column vocabulary; the consumer-side feed-kind map is the ui lane's card (recordActivityFeed: pin the feed-kind map's OPEN-vocabulary relationship tosys_activity.type(ruled direction 4 on objectstack#11507) objectui#5969 remains open and is not addressed here — a read-coupling note goes there after this PR opens).Test-first verification (replaces mutate-and-restore ablation — no restore leg exists to fail silently)
New tests written on the unmodified schema with the failure predicted in writing first: the two open-vocabulary cases red, the rejection-preservation case green. Observed exactly that:
Tests 2 failed | 223 passed (225), the two reds beingaccepts author-contributed activity kindsanddoes not reject a typo of a built-in kind by name. After the schema edit:Test Files 3 passed (3) · Tests 307 passed (307)(component + feed + page suites). The replaced closed-enum pin (should reject invalid feed item type) pinned exactly the branch the ruling removed — replaced, not reworded.Verification — all at
b451c43(branch head, clean tree), heavy steps serialized throughscripts/pm/os-verify-lock.sh, every exit code captured before any pipeTest Files 3 passed (3) · Tests 307 passed (307)— os-verify-lockVERDICT command-exit 0.pnpm --filter @objectstack/spec build:VERDICT command-exit 0(dist + DTS rebuilt;.build-input-hashrefreshed).pnpm --filter @objectstack/spec typecheck:VERDICT command-exit 0, including its own verdict linecheck:test-typecheck: OK — @objectstack/spec's test layer compiles under packages/spec/tsconfig.test.json; 55 file(s) / 263 error(s) held in test-typecheck-debt.json (shrink-only)— the edited test file is inside that measured layer.pnpm --filter @objectstack/spec check:generated:✓ All 14 generated artifacts are up to date.(check:docswas the one proved stale; regenerated via--fix, diff reviewed.)pnpm lint(eslint . --no-inline-config):VERDICT command-exit 0— no narrowing needed.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackatb451c43(43 path-matched + 5 convention-triggered for the test-file kind +check:nul-bytes): all exit 0, includingcheck:authorable-surface,check:api-surface,check:slot-lookup,check:query-options-erasure,check:engine-double-contract,check:where-matcher,check:type-check-coverage,check:cross-package-test-inputs,check:changeset-no-major,check:adr-0087-registration,check:nul-bytes. Three gates (check:doc-formula-expressions,check:doc-security-posture,check:skill-examples) first returned prerequisite-not-met ("nothing was measured"); their named prerequisites were built (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --filter=@objectstack/client-react, 33 tasks,VERDICT command-exit 0) and all three re-ran green (✅ 260 prose examples type-check across 3 surface(s)).check:type-check-debt --re-measureandcheck:dev-prereqsboth require the wholepackages/**closure built (65 packages) — a farm-scale run CI'slint.ymlperforms on every PR after its own full build. Evidence the narrowing excludes nothing of this diff: ① their populations are read from the gates' own output (per-package debt ledger entries; dev-stack dist presence — a container build-state property, not a diff property); ② this diff touches onlypackages/spec(built and typechecked clean at head, test layer included per the verdict line above), generated docs, and a changeset; ③ the repo-wide grep below shows zero downstream consumers whose tsc programs could move.Consumer radius
Grep of all
packages/,apps/,examples/forRecordActivityProps/FeedItemTypeoutside the two spec sources (direction: downstream/consumers): zero — only the spec sources themselves, the type-alias-convention pin (assertsz.input ≡ z.inferonFeedItemType, which is unchanged), and generated artifacts. No example or fixture authorsrecord:activitytypes, so no fixture triage was owed. The cross-repo consumer is objectui's feed-kind map, pinned by its own queued card.Changeset
.changeset/record-activity-types-open-vocabulary.md—@objectstack/spec: minor, pure accept-set widening (every previously-legal value parses byte-identically), with an ADR-0087not-required (no-migration-prescription)disposition note.Generated by Claude Code