Skip to content

Commit 77efc66

Browse files
feat(platform-objects)!: Setup's organization, team, business-unit and user records carry no Discussion feed (enable.feeds: false) (#23869)
Fixes #23838 Clause-②: no (narrowing: the comment create door refuses on four platform objects that accept it today) ## What changes `sys_organization`, `sys_team`, `sys_business_unit` and `sys_user` (`packages/platform-objects/src/identity/`) declared no `enable.feeds`, so they took the spec default (`true`) and the console drew "Discussion (0)" with a comment composer on every identity record in Setup (objectui#12614 item 1). Each `enable:` block now declares `feeds: false`, with a two-line comment naming the reason. No other line in those files moves. Both consumers of the flag already exist, so nothing else changes: - the console reads `objectDef?.enable?.feeds !== false` (objectui `packages/app-shell/src/views/RecordDetailView.tsx:1754` at objectui `63d00da`) and strips the Discussion nodes when it is false; - plugin-audit's `enforceFeedsCapability` (`packages/plugins/plugin-audit/src/audit-writers.ts`, `beforeInsert` and `beforeUpdate` on `sys_comment`) refuses a comment whose `thread_id` names an object with `enable.feeds: false`, with the existing `403 FEEDS_DISABLED`. No new error code. The changeset (`.changeset/23838-identity-feeds-off.md`) is a `minor` breaking entry under the launch-window convention, with the same Clause-② line and an ADR-0087 `not-required (no-migration-prescription)` disposition: no spec key, export or stored shape moves, and stored comment rows are kept. ## Measured through the public door CRM app booted `--fresh` on a random port from this branch (platform-objects and plugin-audit closure rebuilt at `4e5399c4b`), signed in as the seeded admin: | request | answer | | --- | --- | | `POST /api/v1/data/sys_comment` with `thread_id` `sys_user:USER_ID` | `403` `{"code":"FEEDS_DISABLED","object":"sys_user"}` | | the same with `sys_organization:ORG_ID` | `403` `{"code":"FEEDS_DISABLED","object":"sys_organization"}` | | CONTROL: the same with `crm_account:ACCOUNT_ID` | `201`, row created | | `GET /api/v1/meta/object/NAME`, the four identity objects | `enable.feeds` = `false` on all four (what the console reads) | | `GET /api/v1/meta/object/crm_account` | `enable.feeds` absent (the console reads absent as on) | The server was stopped after the probe (its PID only); its tempdir is gone. ## Producer search (dispatch assumption 2): no platform code writes a comment into these threads Searched at the branch base `66d456d01`: - every quoted `sys_comment` in non-test code: the only writers are plugin-audit's own hooks (the feeds gate, `comment-access-hooks.ts`, the mention hook, which writes notifications, not comments) and the CLI's comment-reactions migration (`packages/cli/src/utils/comment-reactions-conversion.ts`), which inserts `sys_comment_reaction` rows only; - every quoted or templated thread literal naming one of the four objects (`'sys_user:`, `'sys_organization:`, `'sys_team:`, `'sys_business_unit:`): hits only in the `sys_comment.thread_id` description example and its four translation bundles, one `parseCommentThreadId` unit test and one driver-test code comment, none a writer; - every `thread_id` assignment and every `OBJECT:ID` template near comment code in non-test sources: reads and keys only (the access hooks' visibility filter, a mention dedup key, a cache key); - `examples/**` and every seed or fixture path: no `sys_comment` rows; the dogfood comment fixtures use their own four `cmt_*` objects; - objectui at `63d00da`: the record page creates `sys_comment` only from the Discussion composer, which is behind the same `feedsEnabled` read. Not searched: the `cloud` repository, which is not in this container. ## Pins One file, `packages/plugins/plugin-audit/src/identity-objects-feeds-off.test.ts`. It boots a real `ObjectQL` over a stub driver with the REAL four definitions imported from `@objectstack/platform-objects/identity` (aliased to source in this package's `vitest.config.ts`, so the verdict is about the checkout, not `dist/`) and the real `installAuditWriters` hooks: - per object (`it.each`): a new comment on one of its records rejects with `code: 'FEEDS_DISABLED'`, `status: 403` and the object named, and no row lands; - grandfathered row on `sys_user`: the gate is first shown live on that exact thread, then a row written below every hook (what a row stored before this change is) stays readable through `find`, and a body edit without `thread_id` still saves; - CONTROL: a business object that never mentions `enable.feeds` still accepts a comment. The pins live in plugin-audit because `@objectstack/platform-objects` has no runtime dependency on `objectql` or `plugin-audit` (plugin-audit depends on platform-objects; the reverse would be a cycle), so the real gate cannot be exercised there. This file is `domain:services` territory, as the claim anticipated. The generic gate stays pinned where it was: `audit-writers.test.ts` (insert) and `capability-gate-update-verb.test.ts` (update verb and the grandfathered edit). ## Ablation (one-time proof, not kept) Each leg ran after the fix was committed, through `node scripts/ablation-replace.mjs` in WRAP mode (literal anchor, on-disk counts, blob hash before and after, restore proven by blob equal to HEAD and an empty `git diff HEAD`), running `pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2 src/identity-objects-feeds-off.test.ts`. No build or `dist/` leg is owed: the identity definitions resolve to `src/` through the alias, and the gate mutation is in the package under test. | mutation | anchor | blob before, after | result | | --- | --- | --- | --- | | delete `feeds: false,` in `sys-user.object.ts` | 1 to 0 | `df41d5b3c3f3`, `84847d0be0b7` | 2 failed / 4 passed: the `sys_user` case and the grandfathered-row case | | delete it in `sys-organization.object.ts` | 1 to 0 | `cfcbe12495aa`, `33e67fccd9dd` | 1 failed / 5 passed: the `sys_organization` case | | delete it in `sys-team.object.ts` | 1 to 0 | `3a0954909643`, `df521d289587` | 1 failed / 5 passed: the `sys_team` case | | delete it in `sys-business-unit.object.ts` | 1 to 0 | `0db7f124c9d4`, `e5f6d7a3d360` | 1 failed / 5 passed: the `sys_business_unit` case | | invert the gate in `audit-writers.ts` (`feeds === false` read as `feeds !== true`) | 1 to 0 | `f556c52ac945`, `4a3379a3fc5f` | 1 failed / 5 passed: the CONTROL | Every leg restored to its HEAD blob, and the tree was clean after the run. ## Tests and gates, on head `4e5399c4b` (after merging `origin/main` `a31ff2eb5`, which carries #23845) - `pnpm --filter @objectstack/platform-objects test`: 71 files, 1103 tests passed. - `pnpm --filter @objectstack/plugin-audit test`: 52 files, 839 tests passed. - `pnpm --filter @objectstack/platform-objects --filter @objectstack/plugin-audit typecheck`: exit 0; the new file is in plugin-audit's `tsconfig.test.json` program (`--listFiles`). - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack`: 65 commands, each run with its exit code captured before any pipe, all exit 0; `--ran` reconciliation: 65 accounted for, 0 NOT-MEASURED, 0 UNRUN. - The four artifact-roster gates whose roster sits under a directory this diff touches: `check-changeset-fixed`, `check:authz-resolver`, `check:error-code-casing`, `check:filter-alias-parity`, all exit 0. - Not run locally, CI's: the declared wide-population families, the five path-scheduled jobs (Test Core, Build Core, the dogfood pair, Temporal Conformance) and the workspace type-check lanes. ## Acceptance notes - This PR also changes the `sys_comment.thread_id` example from `sys_user:abc123` to `account:9001`: the description and docblock in `packages/plugins/plugin-audit/src/objects/sys-comment.object.ts`, the `en` bundle regenerated by `node scripts/check-i18n-bundles.mjs --write`, and the zh-CN, ja-JP and es-ES leaves swapped by hand. After this change, a comment created by following the old example answers `403 FEEDS_DISABLED`. (Written by the `domain:engine` seat 2 from the dev's patch-round report; the claim's file-surface amendment is 6213345119 on #23838.) - Comments already stored on these records are kept and stay readable through the data API; the record page no longer shows them, because the console strips the Discussion section when the flag is false. The changeset says so. - Being a narrowing, this owes one review at `CONTRACT_REVIEW_TIER` before the queue. --- _Generated by [Claude Code](https://claude.ai/code/session_01TQLy3Yq6vF4SDpTXdr3Uwi)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 497518c commit 77efc66

11 files changed

Lines changed: 285 additions & 6 deletions
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
'@objectstack/platform-objects': minor
3+
---
4+
5+
feat(platform-objects)!: Setup's organization, team, business-unit and user records carry no Discussion feed: `sys_organization`, `sys_team`, `sys_business_unit` and `sys_user` declare `enable.feeds: false`, so a comment create on one of their records answers `403 FEEDS_DISABLED`
6+
7+
Clause-②: no (narrowing: the comment create door refuses on four platform objects that accept it today)
8+
9+
<!-- adr-0087: not-required (no-migration-prescription) No metadata moves: no spec key, authorable spelling, export or stored shape is added, removed, renamed or re-shaped, and no stored row is rewritten or deleted, so there is nothing for `objectstack migrate meta` to rewrite. What narrows is one runtime write door: the existing `enable.feeds` gate now refuses a comment whose thread names one of four platform objects, and the remedy is in the request a caller sends to that door, never a rewrite of a package source or a stored row. The other categories are closed on facts: the package publishes (not unpublished); no ADR-0087 id is named or touched (not registered or already-registered); and no exported declaration is removed or narrowed (not runtime-interface-only, type-surface-only or runtime-export-retired). -->
10+
11+
**BREAKING** (an accept-set narrowing), shipped as `minor` under the launch-window convention for breaking changes.
12+
13+
Setup's identity records are directory entries, not discussion surfaces. The four objects declared no `enable.feeds`, so they took the default (`true`), and the console drew "Discussion (0)" with a comment composer on every organization, team, business unit and user record.
14+
15+
What changes:
16+
17+
- **The record page.** The console reads the flag (`enable.feeds !== false`), so it no longer draws the Discussion feed or its composer on these four objects' records. The record's activity history is unchanged.
18+
- **The data API.** A `sys_comment` create whose `thread_id` names one of these objects (`sys_user:USER_ID`, `sys_organization:ORG_ID`, `sys_team:TEAM_ID` or `sys_business_unit:UNIT_ID`) answers `403 FEEDS_DISABLED` and writes nothing, through REST, the SDK and every other caller of the data engine. An update that moves an existing comment into one of these threads is refused the same way.
19+
- **Comments already stored** on these records are kept. They stay readable through the data API, and an edit that does not change `thread_id`, such as a body edit, still saves.
20+
21+
Unchanged: every other platform object and every application object keeps its own `enable.feeds` setting, and an object that declares none still accepts comments.
22+
23+
Remedy: stop creating comments on these four objects' records. A discussion about a user, a team or an organization belongs on a record of an object that keeps its feed on.

‎packages/platform-objects/src/identity/sys-business-unit.object.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ export const SysBusinessUnit = ObjectSchema.create({
270270
trackHistory: true,
271271
searchable: true,
272272
apiEnabled: true,
273+
// Setup's identity records are not discussion surfaces (objectui#12614 item 1):
274+
// no Discussion feed, and a comment create on one answers 403 FEEDS_DISABLED.
275+
feeds: false,
273276
// No `apiMethods` — default-open (#3543 audit). The HRIS org-tree sync
274277
// scenario (#3025) needs every primitive, and a whitelist naming all six is
275278
// equivalent to no whitelist while NOT tracking future primitives; the P1

‎packages/platform-objects/src/identity/sys-organization.object.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,9 @@ export const SysOrganization = ObjectSchema.create({
376376
trackHistory: true,
377377
searchable: true,
378378
apiEnabled: true,
379+
// Setup's identity records are not discussion surfaces (objectui#12614 item 1):
380+
// no Discussion feed, and a comment create on one answers 403 FEEDS_DISABLED.
381+
feeds: false,
379382
// #1591 closed the generic writes on this table — "refused by the identity
380383
// write guard (ADR-0092 D2) and owned by better-auth; HTTP answers 405
381384
// before the 403" — and that reasoning still holds for better-auth's own

‎packages/platform-objects/src/identity/sys-team.object.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ export const SysTeam = ObjectSchema.create({
194194
trackHistory: true,
195195
searchable: true,
196196
apiEnabled: true,
197+
// Setup's identity records are not discussion surfaces (objectui#12614 item 1):
198+
// no Discussion feed, and a comment create on one answers 403 FEEDS_DISABLED.
199+
feeds: false,
197200
// Generic writes are refused by the plugin-auth identity write guard
198201
// (ADR-0092 D2) and better-auth owns create/update/delete via the
199202
// team actions above — so the generic data API exposes reads only.

‎packages/platform-objects/src/identity/sys-user.object.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,9 @@ export const SysUser = ObjectSchema.create({
10471047
trackHistory: true,
10481048
searchable: true,
10491049
apiEnabled: true,
1050+
// Setup's identity records are not discussion surfaces (objectui#12614 item 1):
1051+
// no Discussion feed, and a comment create on one answers 403 FEEDS_DISABLED.
1052+
feeds: false,
10501053
// #1591 — create/delete are refused by the identity write guard
10511054
// (ADR-0092 D2) and owned by better-auth (Invite / Create User / admin
10521055
// actions), so they are not exposed. `update` stays: it is the ONE
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* Setup's four identity objects — `sys_organization`, `sys_team`,
5+
* `sys_business_unit` and `sys_user` — declare `enable.feeds: false`, so a
6+
* comment cannot be created on their records (objectui#12614 item 1: the
7+
* record page drew a Discussion feed with a composer on each of them).
8+
*
9+
* The gate itself is generic and pinned elsewhere: `audit-writers.test.ts`
10+
* (#2707, insert) and `capability-gate-update-verb.test.ts` (#10170, the
11+
* update verb and the grandfathered body edit). What this file pins is the
12+
* COMPOSITION — the REAL platform-object definitions, imported rather than
13+
* restated, registered in a REAL `ObjectQL` with the real `installAuditWriters`
14+
* hooks — so a `feeds: false` dropped from any one of the four reds its own
15+
* case below. `@objectstack/platform-objects/identity` resolves to source
16+
* (this package's vitest alias), so the verdict is about the checkout, not
17+
* about `platform-objects/dist`.
18+
*
19+
* Every refusal asserts the ADR-0112 envelope (`code` AND `status`) plus the
20+
* object the gate named, and the CONTROL proves the harness does not refuse
21+
* everything: a business object that never mentions the flag still takes a
22+
* comment.
23+
*/
24+
25+
import { describe, it, expect } from 'vitest';
26+
import { ObjectQL } from '@objectstack/objectql';
27+
import type { EngineQueryOptions, EngineUpdateOptions } from '@objectstack/spec/data';
28+
import {
29+
SysBusinessUnit,
30+
SysOrganization,
31+
SysTeam,
32+
SysUser,
33+
} from '@objectstack/platform-objects/identity';
34+
import { installAuditWriters } from './audit-writers.js';
35+
36+
type Row = Record<string, unknown>;
37+
38+
const text = (name: string, primaryKey = false) => ({
39+
name,
40+
label: name,
41+
type: 'text' as const,
42+
...(primaryKey ? { primaryKey: true } : {}),
43+
});
44+
45+
const fieldMap = (...names: string[]) =>
46+
Object.fromEntries(names.map((n) => [n, text(n, n === 'id')]));
47+
48+
/**
49+
* A stub driver with just enough storage for the write paths under test. Its
50+
* matcher implements the operators these reads send and REFUSES any other, so
51+
* it can never answer a query it does not understand silently wrong
52+
* (`check:where-matcher-conformance`); `find` applies the caller's `limit` by
53+
* presence, after the filter (`check:objectql-double-limit`).
54+
*/
55+
function makeStubDriver() {
56+
const stores = new Map<string, Map<string, Row>>();
57+
const storeFor = (o: string) => {
58+
let s = stores.get(o);
59+
if (!s) {
60+
s = new Map();
61+
stores.set(o, s);
62+
}
63+
return s;
64+
};
65+
let nextId = 0;
66+
const matches = (row: Row, where: unknown): boolean => {
67+
if (!where || typeof where !== 'object') return true;
68+
for (const [k, v] of Object.entries(where as Record<string, unknown>)) {
69+
if (k === '$and' && Array.isArray(v)) {
70+
if (!v.every((sub) => matches(row, sub))) return false;
71+
continue;
72+
}
73+
if (k === '$or' && Array.isArray(v)) {
74+
if (!v.some((sub) => matches(row, sub))) return false;
75+
continue;
76+
}
77+
if (k.startsWith('$')) throw new Error(`stub driver: unsupported where operator '${k}'`);
78+
if (v && typeof v === 'object') {
79+
const ops = Object.keys(v);
80+
if (ops.length === 1 && ops[0] === '$eq') {
81+
if ((row[k] ?? null) !== ((v as { $eq: unknown }).$eq ?? null)) return false;
82+
continue;
83+
}
84+
if (ops.length === 1 && ops[0] === '$in' && Array.isArray((v as { $in: unknown }).$in)) {
85+
if (!(v as { $in: unknown[] }).$in.includes(row[k])) return false;
86+
continue;
87+
}
88+
throw new Error(`stub driver: unsupported where predicate on '${k}': ${JSON.stringify(v)}`);
89+
}
90+
if ((row[k] ?? null) !== (v ?? null)) return false;
91+
}
92+
return true;
93+
};
94+
const driver: any = {
95+
name: 'memory',
96+
version: '0.0.0',
97+
supports: {},
98+
async connect() {},
99+
async disconnect() {},
100+
async checkHealth() {
101+
return true;
102+
},
103+
async execute() {
104+
return null;
105+
},
106+
async syncSchema() {},
107+
async find(o: string, ast: any) {
108+
const rows = Array.from(storeFor(o).values()).filter((r) => matches(r, ast?.where));
109+
return typeof ast?.limit === 'number' ? rows.slice(0, ast.limit) : rows;
110+
},
111+
async findOne(o: string, ast: any) {
112+
for (const r of storeFor(o).values()) if (matches(r, ast?.where)) return r;
113+
return null;
114+
},
115+
async create(o: string, data: Row) {
116+
nextId += 1;
117+
const id = (data.id as string) ?? `r_${nextId}`;
118+
const row = { ...data, id };
119+
storeFor(o).set(id, row);
120+
return row;
121+
},
122+
async update(o: string, id: string, data: Row) {
123+
const s = storeFor(o);
124+
const cur = s.get(id);
125+
if (!cur) return null;
126+
const u = { ...cur, ...data, id };
127+
s.set(id, u);
128+
return u;
129+
},
130+
async delete() {
131+
return true;
132+
},
133+
async count(o: string, ast: any) {
134+
return (await driver.find(o, { where: ast?.where })).length;
135+
},
136+
async aggregate() {
137+
return [];
138+
},
139+
};
140+
return { driver, storeFor };
141+
}
142+
143+
/** The four Setup identity objects, as `platform-objects` declares them. */
144+
const IDENTITY_OBJECTS = [SysOrganization, SysTeam, SysBusinessUnit, SysUser] as const;
145+
146+
/**
147+
* Boot a real engine carrying the four REAL identity definitions, a business
148+
* object that never mentions `enable.feeds`, `sys_comment`, and the audit
149+
* sinks (registered so the `afterInsert` writers have somewhere real to land —
150+
* their failures are swallowed by design, and a swallowed failure would make
151+
* this harness lie about which write happened).
152+
*/
153+
async function boot() {
154+
const engine = new ObjectQL();
155+
const { driver, storeFor } = makeStubDriver();
156+
engine.registerDriver(driver, true);
157+
await engine.init();
158+
159+
const reg = engine.registry;
160+
for (const def of IDENTITY_OBJECTS) reg.registerObject(def as any, 'test.fixture');
161+
reg.registerObject({
162+
name: 'crm_account',
163+
label: 'Account',
164+
fields: fieldMap('id', 'name'),
165+
} as any, 'test.fixture');
166+
reg.registerObject({
167+
name: 'sys_comment',
168+
label: 'Comment',
169+
fields: fieldMap('id', 'thread_id', 'body'),
170+
} as any, 'test.fixture');
171+
reg.registerObject({
172+
name: 'sys_audit_log',
173+
label: 'Audit Log',
174+
fields: fieldMap(
175+
'id', 'action', 'user_id', 'actor', 'object_name', 'record_id', 'old_value', 'new_value', 'tenant_id',
176+
),
177+
} as any, 'test.fixture');
178+
reg.registerObject({
179+
name: 'sys_activity',
180+
label: 'Activity',
181+
fields: fieldMap(
182+
'id', 'type', 'timestamp', 'summary', 'actor_id', 'object_name', 'record_id', 'record_label', 'metadata',
183+
),
184+
} as any, 'test.fixture');
185+
186+
installAuditWriters(engine as any, 'test.audit');
187+
return { engine, driver, storeFor };
188+
}
189+
190+
describe('Setup identity objects declare enable.feeds: false — a comment create on their records is refused', () => {
191+
it.each(IDENTITY_OBJECTS.map((def) => [def.name] as const))(
192+
'%s: a new comment on one of its records → 403 FEEDS_DISABLED, and no row lands',
193+
async (objectName) => {
194+
const { engine, storeFor } = await boot();
195+
196+
await expect(
197+
engine.insert('sys_comment', { thread_id: `${objectName}:rec-1`, body: 'hello' } as any),
198+
).rejects.toMatchObject({ code: 'FEEDS_DISABLED', status: 403, object: objectName });
199+
200+
expect(storeFor('sys_comment').size).toBe(0);
201+
},
202+
);
203+
204+
it('a comment row stored before the flag stays readable, and an ordinary body edit on it still saves', async () => {
205+
// The grandfather rule in `audit-writers.ts` (`enforceFeedsCapability`):
206+
// on update, an ABSENT `thread_id` is not a re-thread, so the narrowing
207+
// reaches new comments and re-points only — never a row that already
208+
// targets the object. The row is written straight through the driver,
209+
// below every hook, which is what a row stored before this change is.
210+
const { engine, driver } = await boot();
211+
const stored = await driver.create('sys_comment', { thread_id: 'sys_user:usr-1', body: 'before the flag' });
212+
const id = String(stored.id);
213+
214+
// The gate is live on this very thread — so what follows is a grandfathered
215+
// row surviving the flag, not a harness in which the flag never applied.
216+
await expect(
217+
engine.insert('sys_comment', { thread_id: 'sys_user:usr-1', body: 'after the flag' } as any),
218+
).rejects.toMatchObject({ code: 'FEEDS_DISABLED', status: 403, object: 'sys_user' });
219+
220+
const read: any[] = await engine.find('sys_comment', {
221+
where: { thread_id: 'sys_user:usr-1' },
222+
} satisfies EngineQueryOptions);
223+
expect(read.map((r) => [r.id, r.body])).toEqual([[id, 'before the flag']]);
224+
225+
await expect(
226+
engine.update('sys_comment', { body: 'edited after the flag' } as any, {
227+
where: { id },
228+
} satisfies EngineUpdateOptions),
229+
).resolves.toBeTruthy();
230+
231+
const after: any = await engine.findOne('sys_comment', { where: { id } } satisfies EngineQueryOptions);
232+
expect(after).toMatchObject({ id, thread_id: 'sys_user:usr-1', body: 'edited after the flag' });
233+
});
234+
235+
it('CONTROL: a business object that never mentions enable.feeds still accepts a comment', async () => {
236+
const { engine, storeFor } = await boot();
237+
238+
await expect(
239+
engine.insert('sys_comment', { thread_id: 'crm_account:acc-1', body: 'hello' } as any),
240+
).resolves.toBeTruthy();
241+
242+
expect(Array.from(storeFor('sys_comment').values()).map((r) => r.thread_id)).toEqual(['crm_account:acc-1']);
243+
});
244+
});

‎packages/plugins/plugin-audit/src/objects/sys-comment.object.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
77
*
88
* Generic threaded discussion attached to any record via `thread_id`.
99
* `thread_id` is conventionally formatted as `{object}:{record_id}`
10-
* (e.g. `sys_user:abc123`, `account:9001`) so a single index covers
10+
* (e.g. `account:9001`) so a single index covers
1111
* "all comments on this record" lookups across every object.
1212
*
1313
* Distinct from `feed_item` (Salesforce-style Chatter timeline that
@@ -126,7 +126,7 @@ export const SysComment = ObjectSchema.create({
126126
searchable: true,
127127
maxLength: 255,
128128
description:
129-
'Thread identifier — conventionally `{object}:{record_id}` (e.g. `sys_user:abc123`)',
129+
'Thread identifier — conventionally `{object}:{record_id}` (e.g. `account:9001`)',
130130
group: 'Thread',
131131
}),
132132

‎packages/plugins/plugin-audit/src/translations/en.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
202202
},
203203
thread_id: {
204204
label: "Thread",
205-
help: "Thread identifier — conventionally `{object}:{record_id}` (e.g. `sys_user:abc123`)"
205+
help: "Thread identifier — conventionally `{object}:{record_id}` (e.g. `account:9001`)"
206206
},
207207
parent_id: {
208208
label: "Parent Comment",

‎packages/plugins/plugin-audit/src/translations/es-ES.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export const esESObjects: NonNullable<TranslationData['objects']> = {
202202
},
203203
thread_id: {
204204
label: "Hilo",
205-
help: "Identificador del hilo; por convención `{object}:{record_id}` (p. ej. `sys_user:abc123`)."
205+
help: "Identificador del hilo; por convención `{object}:{record_id}` (p. ej. `account:9001`)."
206206
},
207207
parent_id: {
208208
label: "Comentario principal",

‎packages/plugins/plugin-audit/src/translations/ja-JP.objects.generated.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ export const jaJPObjects: NonNullable<TranslationData['objects']> = {
202202
},
203203
thread_id: {
204204
label: "スレッド",
205-
help: "スレッド識別子 — 通常は `{object}:{record_id}`(例: `sys_user:abc123`)"
205+
help: "スレッド識別子 — 通常は `{object}:{record_id}`(例: `account:9001`)"
206206
},
207207
parent_id: {
208208
label: "親コメント",

0 commit comments

Comments
 (0)