Skip to content

Commit 2daa8e2

Browse files
os-zhuangclaude
andauthored
refactor(audit): retire service-feed; sys_comment is the canonical backend (ADR-0052 §5) (#1955)
Ends the split-brain. There were two parallel record-collaboration/timeline implementations: sys_comment/sys_activity (DB-backed, default-loaded, the ChatterPanel's actual source of truth) and @objectstack/service-feed (an in-memory, non-durable, UI-unconsumed parallel feed whose nested REST route was never even mounted). Decision (ADR-0052 §5): keep sys_comment as canonical and retire service-feed. - Delete the @objectstack/service-feed package (in-memory adapter, feed_item/ feed_reaction/record_subscription objects, FeedServicePlugin). - Remove the `feed` capability from the CLI CAPABILITY_PROVIDERS resolver and the cli → service-feed dependency; drop it from the changeset package list. - Fix a stale comment in system-file.object.ts. - ADR-0052 §5 records the decision + rationale (durable/UI-wired wins over an unshippable better-model); the unified typed timeline lives on sys_activity. No app depended on service-feed (CLI was the only referencer). The vestigial spec contracts (feed.zod/feed-api.zod/IFeedService) are woven into component.zod/ protocol.zod/objectql and are a separate type-surface cleanup (follow-up). NOTE: the CLI build is red on main from a pre-existing CEL drift in validate-expressions.ts (#1933 area, in-flight in another worktree), unrelated to this change — this commit touches none of those files. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent bb66231 commit 2daa8e2

19 files changed

Lines changed: 45 additions & 2475 deletions

.changeset/config.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
"@objectstack/service-cluster",
6666
"@objectstack/service-cluster-redis",
6767
"@objectstack/service-datasource",
68-
"@objectstack/service-feed",
6968
"@objectstack/service-i18n",
7069
"@objectstack/service-job",
7170
"@objectstack/service-knowledge",

docs/adr/0052-audit-is-not-the-activity-feed.md

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -161,39 +161,44 @@ boundary — which is only possible once mutable co-tenants (`sys_comment`) leav
161161
Audit **remains a default platform capability**: compliance is foundational,
162162
durable, and HA-safe. It just stops being the dumping ground.
163163

164-
## 5. Collaboration / activity: one backend, and the `ALWAYS_ON` question
165-
166-
There are two implementations; we must converge to one. The recommended target
167-
is **`service-feed`'s model** — a single unified, *typed* timeline
168-
(`FeedItemType` already enumerates `comment | field_change | task | event |
169-
email | call | note | …`), with reactions, mentions, threaded replies, and
170-
record subscriptions — because it is the right long-term shape (a business event
171-
stream, not a CRUD log). But it is **not shippable as-is**, and therefore **must
172-
not be added to `ALWAYS_ON_CAPABILITIES` today**. Admission criteria
173-
(`ALWAYS_ON` = foundational **and** durable/HA-safe **and** actually consumed):
174-
175-
1. **Durable adapter.** `service-feed` currently ships an in-memory,
176-
single-instance, non-durable adapter ("v1: single-instance only; data lost on
177-
restart"). A DB-backed adapter is a precondition for default-on.
178-
2. **REST surface mounted.** The nested `/api/v1/data/{object}/{recordId}/feed`
179-
contract (`feed-api.zod.ts`) returns `404` — the route is unimplemented in
180-
the rest server. Mount it.
181-
3. **UI consumes the service.** The console **ChatterPanel** is hard-wired to
182-
`sys_comment` / `sys_activity` / `sys_user`. Point it at `IFeedService`.
183-
4. **Migration / aliasing.** `sys_comment` rows (DB-backed, in use) must be
184-
migrated or aliased onto the canonical store so no comment is lost and
185-
`thread_id = {object}:{recordId}` continues to resolve.
186-
187-
Until 1–4 land, the **DB-backed `sys_comment` + `sys_activity` remain the
188-
canonical, default-available collaboration surface** (they already work), and
189-
the short-term, highest-ROI UI win is to render the `reactions` / `parent_id`
190-
fields `sys_comment` **already declares** — no `feed` flag required.
191-
192-
> Counter-option: make **`sys_comment`** the canonical model and retire
193-
> `service-feed` into it (it already has `thread_id`, `parent_id`, `reply_count`,
194-
> `mentions`, `reactions`). Cheaper, durable today, but keeps three separate
195-
> objects instead of one unified typed feed. Decision deferred to the build spec;
196-
> either way, **the terminal state is one backend, not two.**
164+
## 5. Collaboration / activity: one backend — **DECIDED: `sys_comment`**
165+
166+
There were two implementations; we converge to one. **Decision: `sys_comment` /
167+
`sys_activity` is canonical; `@objectstack/service-feed` is retired.**
168+
169+
The originally-recommended target was `service-feed`'s single unified *typed*
170+
timeline. But weighing it against the implementation reality reversed that lean:
171+
172+
| | `sys_comment` / `sys_activity` (chosen) | `service-feed` (retired) |
173+
|---|---|---|
174+
| Durability | ✅ DB-backed | ❌ in-memory only ("v1: single-instance; data lost on restart") |
175+
| Default-loaded | ✅ (via audit slate) | ❌ opt-in capability |
176+
| UI consumes it | ✅ ChatterPanel reads/writes it | ❌ never consumed (enabling `feed` was a verified no-op) |
177+
| REST | ✅ generic data API | ❌ nested `/data/{obj}/{id}/feed` route unmounted (404) |
178+
| threads/mentions/reactions | ✅ fields already declared (`parent_id`, `reply_count`, `mentions`, `reactions`) | ✅ (but unreachable) |
179+
180+
Picking the durable, default, UI-wired system reaches "one backend" **now**, at
181+
near-zero risk. `service-feed`'s only real edge — one unified *typed* stream —
182+
is obtained on the chosen family by treating **`sys_activity` as the unified
183+
typed timeline** (its `type` enum already carries the event kinds; extend it to
184+
`email | call | event | note` as needed). The two remaining UI niceties
185+
(reactions, threaded replies) are a render of fields `sys_comment` **already**
186+
has — an objectui enhancement, not a backend change.
187+
188+
Rejected alternative — invest in `service-feed`: building a DB adapter + mounting
189+
the REST route + repointing ChatterPanel + migrating `sys_comment` rows is weeks
190+
of cross-repo work to **duplicate a system that already works durably**. That is
191+
the split-brain this ADR exists to end, not extend.
192+
193+
> Superseded note (kept for history): an earlier draft deferred this to a build
194+
> spec and leaned toward `service-feed`. Implementation reality (in-memory,
195+
> UI-unused, REST-unmounted) decided it the other way.
196+
197+
The terminal state is **one backend, not two** — now realized: `service-feed`'s
198+
runtime (the package + the `feed` capability) is removed; `sys_comment` /
199+
`sys_activity` stand alone. (The vestigial spec *contracts*`feed.zod` /
200+
`feed-api.zod` / `IFeedService` — are a separate type-surface cleanup, since they
201+
are woven into `component.zod` / `protocol.zod` / objectql; tracked as follow-up.)
197202

198203
## 5b. Declarative activity — the platform generates it, apps don't code it
199204

packages/cli/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
"@objectstack/service-automation": "workspace:*",
7979
"@objectstack/service-cache": "workspace:*",
8080
"@objectstack/service-datasource": "workspace:*",
81-
"@objectstack/service-feed": "workspace:*",
8281
"@objectstack/service-job": "workspace:*",
8382
"@objectstack/service-messaging": "workspace:*",
8483
"@objectstack/service-package": "workspace:*",

packages/cli/src/commands/serve.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1524,11 +1524,11 @@ export default class Serve extends Command {
15241524
export: 'RealtimeServicePlugin',
15251525
nameMatch: ['service-realtime', 'RealtimeServicePlugin'],
15261526
},
1527-
feed: {
1528-
pkg: '@objectstack/service-feed',
1529-
export: 'FeedServicePlugin',
1530-
nameMatch: ['service-feed', 'FeedServicePlugin'],
1531-
},
1527+
// `feed` removed (ADR-0052 §5): `sys_comment`/`sys_activity` (durable,
1528+
// default-loaded, UI-wired) is the canonical record collaboration +
1529+
// timeline backend. `@objectstack/service-feed` was an in-memory,
1530+
// non-durable, UI-unconsumed parallel implementation — retired to end
1531+
// the split-brain. The unified typed timeline lives on `sys_activity`.
15321532
mcp: {
15331533
pkg: '@objectstack/mcp',
15341534
export: 'MCPServerPlugin',

0 commit comments

Comments
 (0)