diff --git a/docs/gamecult/ratatoskr-target.md b/docs/gamecult/ratatoskr-target.md index b56c90f8..519db4b1 100644 --- a/docs/gamecult/ratatoskr-target.md +++ b/docs/gamecult/ratatoskr-target.md @@ -97,9 +97,11 @@ Probes run by Imagination on 2026-10-31, session `self-2026-10-29-eureka`. events from the Minds' stewardship documents; each project room's alias is `#` (runbook 146-150). This is the only place the client can learn a not-yet-joined room's steward today: hierarchy `canonical_alias` - joined to the root `org.gamecult.stewardships`. Follow-up - `ratatoskr:follow_up:stewardships-carry-room-ids` asks the projection to - name room ids so the client stops leaning on the alias convention. + joined to the root `org.gamecult.stewardships`. The + projection now also names room ids under `@rooms.projects` and + `@rooms.file_zones` (gamecult-ops #111); the client reads `file_zones` for + the Media row (`mediaRoom.ts`) and still joins projects by alias, since + nothing yet consumes `@rooms.projects`. 5. **Sync and storage** (`src/client/initMatrix.ts`). The client uses matrix-js-sdk 41.7.0 with `IndexedDBStore` (`web-sync-store`, 15-19) and the Rust crypto store, and `startClient({ lazyLoadMembers: true })` (43-47): v2 diff --git a/src/app/features/estate/mediaRoom.test.ts b/src/app/features/estate/mediaRoom.test.ts index af53c12b..934caaae 100644 --- a/src/app/features/estate/mediaRoom.test.ts +++ b/src/app/features/estate/mediaRoom.test.ts @@ -45,7 +45,7 @@ describe('the Media row of a Mind', () => { throw new Error('must not be asked'); }); const carried = mediaRoomIdFromStewardships( - { void: ['GameCult/x'], media: { void: '!c:gamecult.org' } }, + { void: ['GameCult/x'], '@rooms': { file_zones: { void: '!c:gamecult.org' } } }, 'void' ); expect(carried).toBe('!c:gamecult.org'); @@ -58,7 +58,23 @@ describe('the Media row of a Mind', () => { it('ignores a stewardships state that carries no id for the Mind, or something that is not a room id', () => { expect(mediaRoomIdFromStewardships({ void: ['GameCult/x'] }, 'void')).toBeUndefined(); - expect(mediaRoomIdFromStewardships({ media: { void: 'nope' } }, 'void')).toBeUndefined(); + expect( + mediaRoomIdFromStewardships({ '@rooms': { file_zones: { void: 'nope' } } }, 'void') + ).toBeUndefined(); + // the project ids beside it, another Mind's id, and the old provisional key carry nothing for this Mind + expect( + mediaRoomIdFromStewardships( + { + '@rooms': { projects: { x: '!p:s' }, file_zones: { kiko: '!k:s' } }, + media: { void: '!m:s' }, + }, + 'void' + ) + ).toBeUndefined(); + // a Mind named "constructor" must not read an inherited property as an id + expect( + mediaRoomIdFromStewardships({ '@rooms': { file_zones: {} } }, 'constructor') + ).toBeUndefined(); expect(mediaRoomIdFromStewardships(undefined, 'void')).toBeUndefined(); }); diff --git a/src/app/features/estate/mediaRoom.ts b/src/app/features/estate/mediaRoom.ts index a5f09664..5d1503b6 100644 --- a/src/app/features/estate/mediaRoom.ts +++ b/src/app/features/estate/mediaRoom.ts @@ -2,20 +2,29 @@ * A Mind's media room: the file-zone room Eureka provisions under the alias #-files:. * The navigator shows a Media row for every Mind; this module decides what that row says and where it points. * A room id carried by the root Space's org.gamecult.stewardships state wins over the alias; the alias is the - * convention until the provisioning projection carries ids (ratatoskr:follow_up:stewardships-carry-room-ids). + * fallback for a Mind whose zone the provisioning run has not recorded yet. */ import { EstateRoom } from './buildEstate'; export const STEWARDSHIPS_KEY = 'org.gamecult.stewardships'; +/** Where provisioning records room ids in the stewardships state (gamecult-ops synapse-accounts-hand-managed.py ROOMS_KEY). "@" is no legal Matrix localpart, so no Mind can own the key. */ +export const ROOMS_KEY = '@rooms'; + export const mediaAlias = (mind: string, server: string): string => `#${mind}-files:${server}`; /** - * The room id the stewardships state names for a Mind's media room, if it does. The state's mind-to-repos - * lists are not read here; the id lives under `media[]` (provisional shape, awaiting the follow-up above). + * The room id the stewardships state names for a Mind's media room, under `@rooms.file_zones[]`. + * The state's mind-to-repos lists are not read here. */ export const mediaRoomIdFromStewardships = (content: unknown, mind: string): string | undefined => { - const id = (content as { media?: Record } | null | undefined)?.media?.[mind]; + const rooms = ( + content as + | Record } | undefined> + | null + | undefined + )?.[ROOMS_KEY]; + const id = rooms?.file_zones?.[mind]; return typeof id === 'string' && id.startsWith('!') ? id : undefined; };