Rebuilt transfer (transfer API unavailable in this session). Origin: objectstack-ai/objectstack#11305, filed by os-zhuang at 2026-08-23T09:50:00Z. Moved by the triage seat (session session_01LsWeHbPzR3i6mmonFfGykk, 2026-08-23) under file-at-destination: record:activity is a console/UI renderer (source lives in this repo; packages/console in objectstack is its built artifact).
record:activity sanitises its own types prop and turns an unrecognised or empty filter into no filter — so an author who names the wrong kind is served every activity on the record, with no diagnostic anywhere at runtime.
Measured
Executed out of the shipped bundle (@objectstack/console 17.1.0, dist/assets/plugins-views-BaBStVok.js) — this is the renderer's own code, not a reimplementation:
functionQa(e){if(!Array.isArray(e))return;// absent -> undefinedlett=e.filter(e=>typeofe==='string'&&Ja.includes(e));// Ja = FeedItemType.optionsreturnt.length>0 ? t : void0;// EMPTY REMAINDER -> undefined}// caller:leti=Qa(t.types);if(i){lete=newSet(i);r=r.filter(t=>e.has(t.type));}// undefined => no filter at allRunning it over a record's feed:
Qa(undefined) = undefined -> unfiltered
Qa([]) = undefined -> unfiltered
Qa(["crm_task"]) = undefined -> unfiltered <- every member unrecognised
Qa(["task"]) = ["task"] -> filtered
So three distinct authored intents collapse into one rendering:
| authored | intent | rendered |
|---|
types omitted | "show every kind" | every kind |
types: [] | "show no kind" | every kind |
types: ['crm_task'] | "show this kind" (typo/wrong vocabulary) | every kind |
Why this is worth closing rather than documenting
PageComponentSchema.properties is an open bag (z.record(z.string(), z.unknown())), so nothing rejects the value on the storage path — build says exactly that beside its own author-time warning: "the props bag is not parsed on the storage path either, so nothing rejects this today" (objectstack-ai/objectstack#5068). That is the known half. The half this card is about is what happens after: the renderer does not merely ignore the bad value, it widens on it. A tolerant consumer is where a wrong authored value hides and spreads, and here it hides behind a plausible-looking result — a populated timeline — rather than an empty one an author would investigate.
Measured consequence in a real app: HotCRM's lead page authored types: ['crm_task'] (an object name where a feed kind belongs) and its Activity tab rendered the audit stream — Created Lead … / Updated Lead … — for as long as it shipped. The author-time warning existed and was one of 85; nothing at runtime said anything at all. (objectstack-ai/hotcrm#1209)
Suggested direction
Two independent decisions, both consumer-side tolerance to remove:
- Unrecognised members must not widen. Either keep only the recognised members and filter on those (an all-unrecognised list then renders empty — visibly wrong, which is the point), or drop the whole component to an error state. Silently falling back to "everything" is the one behaviour that cannot be noticed.
types: [] should mean what it says. If "no kinds" is not an intent worth honouring, reject it at the schema (.min(1)) rather than reinterpreting it at render time — declared = enforced.
The same question probably applies to filterMode, whose sanitiser (Za) folds any unrecognised value to 'all' — the widest option, again.
Found while fixing objectstack-ai/hotcrm#1209; not worked around there, per 「平台相关的功能应该在平台中实现」.
Generated by Claude Code
record:activitysanitises its owntypesprop and turns an unrecognised or empty filter into no filter — so an author who names the wrong kind is served every activity on the record, with no diagnostic anywhere at runtime.Measured
Executed out of the shipped bundle (
@objectstack/console17.1.0,dist/assets/plugins-views-BaBStVok.js) — this is the renderer's own code, not a reimplementation:Running it over a record's feed:
So three distinct authored intents collapse into one rendering:
typesomittedtypes: []types: ['crm_task']Why this is worth closing rather than documenting
PageComponentSchema.propertiesis an open bag (z.record(z.string(), z.unknown())), so nothing rejects the value on the storage path —buildsays exactly that beside its own author-time warning: "the props bag is not parsed on the storage path either, so nothing rejects this today" (objectstack-ai/objectstack#5068). That is the known half. The half this card is about is what happens after: the renderer does not merely ignore the bad value, it widens on it. A tolerant consumer is where a wrong authored value hides and spreads, and here it hides behind a plausible-looking result — a populated timeline — rather than an empty one an author would investigate.Measured consequence in a real app: HotCRM's lead page authored
types: ['crm_task'](an object name where a feed kind belongs) and its Activity tab rendered the audit stream —Created Lead …/Updated Lead …— for as long as it shipped. The author-time warning existed and was one of 85; nothing at runtime said anything at all. (objectstack-ai/hotcrm#1209)Suggested direction
Two independent decisions, both consumer-side tolerance to remove:
types: []should mean what it says. If "no kinds" is not an intent worth honouring, reject it at the schema (.min(1)) rather than reinterpreting it at render time — declared = enforced.The same question probably applies to
filterMode, whose sanitiser (Za) folds any unrecognised value to'all'— the widest option, again.Found while fixing objectstack-ai/hotcrm#1209; not worked around there, per 「平台相关的功能应该在平台中实现」.
Generated by Claude Code