openvibers/openvibe.chat Commits
Contract: drop the idle live_mirror_outbox (Live read mirror retired) (#58)
Changes
@@@ -68,7 +68,7 @@ before changing behaviour.** Browser JavaScript does not change; nginx routes th`DATABASE_URL` outside production it runs on an embedded PGlite in `data/pglite` (`CHAT_PGLITE_DIR`).`migrations/0001_initial.sql` is the whole schema (timestamps stay SQLite-format text through`ov_now()`/`datetime()`); `0006_stop_live_mirror_triggers.sql` dropped the twelve read-mirror capture-- triggers, and `live_mirror_outbox` stays, idle, until a contract migration drops it.++ triggers, and migration 0008 dropped the idle `live_mirror_outbox` table.`VALKEY_URL`/`VALKEY_PREFIX` put the per-actor rate-limit counters on Valkey instead of this process.- **The six chat tables** (`channel_moderators`, `channel_moderation_settings`, `emotes`, `user_tags`,`chat_ai_summaries`, `chat_timeline_events`): Chat's own since the C-04 cutover — Chat is their onlynnew file mode 100644@@@ -0,0 +1,7 @@++-- phase: contract++-- after: 0006++--++-- The Live read mirror is retired (2026-10-05, Live #31): 0006_stop_live_mirror_triggers.sql dropped the twelve++-- capture triggers, and nothing has written or read live_mirror_outbox since. Its last rows (frozen before++-- 2026-10-07) never had a drain: they only ever fed Live's mirror, which no longer exists.++DROP TABLE IF EXISTS live_mirror_outbox;@@@ -3,7 +3,7 @@* The PostgreSQL schema (plan T3, decision 1). migrations/0001_initial.sql opens a real PostgreSQL* (PGlite in-process, or the containers with OV_TEST_STORE=pg) and is checked here: every Chat table* exists, the keys/indexes the queries rely on are present, and the retired Live read mirror captures-- * nothing any more (0006 dropped its twelve triggers; live_mirror_outbox stays until a contract migration).++ * nothing any more (0006 dropped its twelve triggers; 0008 dropped live_mirror_outbox).*/const assert = require('assert');const path = require('path');@@@ -24,7 +24,7 @@ const TABLES = ['chat_messages', 'dm_conversations', 'dm_participants', 'dm_messages', 'dm_blocks', 'tts_voice_overrides','channel_sounds', 'relay_users', 'hidden_relay_users', 'pending_ip_messages', 'stream_first_chats', 'moderation_actions','channel_moderators', 'channel_moderation_settings', 'emotes', 'user_tags', 'chat_ai_summaries', 'chat_timeline_events',-- 'ctx_users', 'ctx_streams', 'ctx_managed_streams', 'ctx_channels', 'ctx_sync', 'events_outbox', 'live_mirror_outbox',++ 'ctx_users', 'ctx_streams', 'ctx_managed_streams', 'ctx_channels', 'ctx_sync', 'events_outbox','chat_ingress_applied', 'audio_requests', 'import_hold', 'import_runs', 'chat_meta', 'deploy_releases','chat_event_inbox', 'calls', 'rooms', 'room_members', 'room_messages', 'room_attachments', 'token_revocations','network_blocks', 'account_data_events', 'ticket_conversations', 'ticket_messages',@@@ -59,7 +59,7 @@ t('every table exists', async () => {// 0005 (contract) drops the retired Live chat bridge: the tables exist on an N-1 database, but// this release migrates them away (ADR-028).t('the retired bridge tables are gone', async () => {-- for (const name of ['bridge_applied', 'bridge_refs']) {++ for (const name of ['bridge_applied', 'bridge_refs', 'live_mirror_outbox']) {assert.strictEqual((await get(`SELECT to_regclass('${name}') AS t`)).t, null, `${name} is dropped`);}});@@@ -82,12 +82,10 @@ t('relay_users has a real identity id', async () => {assert.strictEqual(r.is_identity, 'YES', 'id is an identity column');});--t('the retired Live mirror captures nothing (0006 dropped its triggers)', async () => {++t('the retired Live mirror is gone (0006 dropped its triggers, 0008 its outbox)', async () => {const triggers = await all("SELECT tgname FROM pg_trigger WHERE NOT tgisinternal AND tgname LIKE 'mirror\\_%'");assert.deepStrictEqual(triggers, [], 'no mirror_* capture trigger remains');-- const before = (await get('SELECT COUNT(*) AS n FROM live_mirror_outbox')).n;-- await db.prepare('INSERT INTO chat_messages (message) VALUES (?)').run('hello');-- assert.strictEqual((await get('SELECT COUNT(*) AS n FROM live_mirror_outbox')).n, before, 'a Chat write queues nothing');++ await db.prepare('INSERT INTO chat_messages (message) VALUES (?)').run('hello'); // a Chat write needs no mirror table});t('cleanup', async () => { await close(); });
@@ -68,7 +68,7 @@ before changing behaviour.** Browser JavaScript does not change; nginx routes th
@@ -68,7 +68,7 @@ before changing behaviour.** Browser JavaScript does not change; nginx routes th
`DATABASE_URL` outside production it runs on an embedded PGlite in `data/pglite` (`CHAT_PGLITE_DIR`).
`DATABASE_URL` outside production it runs on an embedded PGlite in `data/pglite` (`CHAT_PGLITE_DIR`).
`migrations/0001_initial.sql` is the whole schema (timestamps stay SQLite-format text through
`migrations/0001_initial.sql` is the whole schema (timestamps stay SQLite-format text through
`ov_now()`/`datetime()`); `0006_stop_live_mirror_triggers.sql` dropped the twelve read-mirror capture
`ov_now()`/`datetime()`); `0006_stop_live_mirror_triggers.sql` dropped the twelve read-mirror capture
- triggers, and `live_mirror_outbox` stays, idle, until a contract migration drops it.
+ triggers, and migration 0008 dropped the idle `live_mirror_outbox` table.
`VALKEY_URL`/`VALKEY_PREFIX` put the per-actor rate-limit counters on Valkey instead of this process.
`VALKEY_URL`/`VALKEY_PREFIX` put the per-actor rate-limit counters on Valkey instead of this process.
- **The six chat tables** (`channel_moderators`, `channel_moderation_settings`, `emotes`, `user_tags`,
- **The six chat tables** (`channel_moderators`, `channel_moderation_settings`, `emotes`, `user_tags`,
`chat_ai_summaries`, `chat_timeline_events`): Chat's own since the C-04 cutover — Chat is their only
`chat_ai_summaries`, `chat_timeline_events`): Chat's own since the C-04 cutover — Chat is their only
new file mode 100644
new file mode 100644
@@ -0,0 +1,7 @@
@@ -0,0 +1,7 @@
+-- phase: contract
+-- after: 0006
+--
+-- The Live read mirror is retired (2026-10-05, Live #31): 0006_stop_live_mirror_triggers.sql dropped the twelve
+-- capture triggers, and nothing has written or read live_mirror_outbox since. Its last rows (frozen before
+-- 2026-10-07) never had a drain: they only ever fed Live's mirror, which no longer exists.
+DROP TABLE IF EXISTS live_mirror_outbox;
@@ -3,7 +3,7 @@
@@ -3,7 +3,7 @@
* The PostgreSQL schema (plan T3, decision 1). migrations/0001_initial.sql opens a real PostgreSQL
* The PostgreSQL schema (plan T3, decision 1). migrations/0001_initial.sql opens a real PostgreSQL
* (PGlite in-process, or the containers with OV_TEST_STORE=pg) and is checked here: every Chat table
* (PGlite in-process, or the containers with OV_TEST_STORE=pg) and is checked here: every Chat table
* exists, the keys/indexes the queries rely on are present, and the retired Live read mirror captures
* exists, the keys/indexes the queries rely on are present, and the retired Live read mirror captures
- * nothing any more (0006 dropped its twelve triggers; live_mirror_outbox stays until a contract migration).
+ * nothing any more (0006 dropped its twelve triggers; 0008 dropped live_mirror_outbox).
*/
*/
const assert = require('assert');
const assert = require('assert');
const path = require('path');
const path = require('path');
@@ -24,7 +24,7 @@ const TABLES = [
@@ -24,7 +24,7 @@ const TABLES = [
'chat_messages', 'dm_conversations', 'dm_participants', 'dm_messages', 'dm_blocks', 'tts_voice_overrides',
'chat_messages', 'dm_conversations', 'dm_participants', 'dm_messages', 'dm_blocks', 'tts_voice_overrides',
'channel_sounds', 'relay_users', 'hidden_relay_users', 'pending_ip_messages', 'stream_first_chats', 'moderation_actions',
'channel_sounds', 'relay_users', 'hidden_relay_users', 'pending_ip_messages', 'stream_first_chats', 'moderation_actions',
'channel_moderators', 'channel_moderation_settings', 'emotes', 'user_tags', 'chat_ai_summaries', 'chat_timeline_events',
'channel_moderators', 'channel_moderation_settings', 'emotes', 'user_tags', 'chat_ai_summaries', 'chat_timeline_events',
- 'ctx_users', 'ctx_streams', 'ctx_managed_streams', 'ctx_channels', 'ctx_sync', 'events_outbox', 'live_mirror_outbox',
+ 'ctx_users', 'ctx_streams', 'ctx_managed_streams', 'ctx_channels', 'ctx_sync', 'events_outbox',
'chat_ingress_applied', 'audio_requests', 'import_hold', 'import_runs', 'chat_meta', 'deploy_releases',
'chat_ingress_applied', 'audio_requests', 'import_hold', 'import_runs', 'chat_meta', 'deploy_releases',
'chat_event_inbox', 'calls', 'rooms', 'room_members', 'room_messages', 'room_attachments', 'token_revocations',
'chat_event_inbox', 'calls', 'rooms', 'room_members', 'room_messages', 'room_attachments', 'token_revocations',
'network_blocks', 'account_data_events', 'ticket_conversations', 'ticket_messages',
'network_blocks', 'account_data_events', 'ticket_conversations', 'ticket_messages',
@@ -59,7 +59,7 @@ t('every table exists', async () => {
@@ -59,7 +59,7 @@ t('every table exists', async () => {
// 0005 (contract) drops the retired Live chat bridge: the tables exist on an N-1 database, but
// 0005 (contract) drops the retired Live chat bridge: the tables exist on an N-1 database, but
// this release migrates them away (ADR-028).
// this release migrates them away (ADR-028).
t('the retired bridge tables are gone', async () => {
t('the retired bridge tables are gone', async () => {
- for (const name of ['bridge_applied', 'bridge_refs']) {
+ for (const name of ['bridge_applied', 'bridge_refs', 'live_mirror_outbox']) {
assert.strictEqual((await get(`SELECT to_regclass('${name}') AS t`)).t, null, `${name} is dropped`);
assert.strictEqual((await get(`SELECT to_regclass('${name}') AS t`)).t, null, `${name} is dropped`);
}
}
});
});
@@ -82,12 +82,10 @@ t('relay_users has a real identity id', async () => {
@@ -82,12 +82,10 @@ t('relay_users has a real identity id', async () => {
assert.strictEqual(r.is_identity, 'YES', 'id is an identity column');
assert.strictEqual(r.is_identity, 'YES', 'id is an identity column');
});
});
-t('the retired Live mirror captures nothing (0006 dropped its triggers)', async () => {
+t('the retired Live mirror is gone (0006 dropped its triggers, 0008 its outbox)', async () => {
const triggers = await all("SELECT tgname FROM pg_trigger WHERE NOT tgisinternal AND tgname LIKE 'mirror\\_%'");
const triggers = await all("SELECT tgname FROM pg_trigger WHERE NOT tgisinternal AND tgname LIKE 'mirror\\_%'");
assert.deepStrictEqual(triggers, [], 'no mirror_* capture trigger remains');
assert.deepStrictEqual(triggers, [], 'no mirror_* capture trigger remains');
- const before = (await get('SELECT COUNT(*) AS n FROM live_mirror_outbox')).n;
- await db.prepare('INSERT INTO chat_messages (message) VALUES (?)').run('hello');
- assert.strictEqual((await get('SELECT COUNT(*) AS n FROM live_mirror_outbox')).n, before, 'a Chat write queues nothing');
+ await db.prepare('INSERT INTO chat_messages (message) VALUES (?)').run('hello'); // a Chat write needs no mirror table
});
});
t('cleanup', async () => { await close(); });
t('cleanup', async () => { await close(); });