diff --git a/.agents/rules/README.md b/.agents/rules/README.md
index 2493e41..1089382 100644
--- a/.agents/rules/README.md
+++ b/.agents/rules/README.md
@@ -12,9 +12,12 @@
service.
- Do not consume alternate event/history services or Host-private transport
contracts. `ctx.agents` owns create/resume/get and returns live Agent handles.
-- Agent Trace consumes only `@cordisx/protocol/sessions/v1` and is honestly
- unavailable when the Host service, exact route permission, or subscription
- is unavailable or replaced.
+- Agent Trace consumes `@cordisx/protocol/sessions/v1` plus only the
+ `EntityDefinitionBoundSessionEvent` extension from
+ `@cordisx/protocol/entities/v1`. It never queries `ctx.entities` or relabels
+ Session history from a mutable current registry. It is honestly unavailable
+ when the Host service, exact route permission, or subscription is unavailable
+ or replaced.
- Every Session capability must be optional and dynamically bound by the Host
to the active same-plugin route's exact `:sessionId`; never use an empty or
wildcard scope.
diff --git a/README.md b/README.md
index 03c569a..e0f0287 100644
--- a/README.md
+++ b/README.md
@@ -21,11 +21,17 @@ The Agent/Session Runtime ownership model is:
- environment composition and transport bindings remain Host-private and do
not change plugin contracts, permissions, or Session facts.
-The contract is `@cordisx/protocol/sessions/v1` at Protocol main commit
-`8891722a7735a3bd00bdd5315084b35b748f5e7f`. The Timeline reads a fixed
-Session snapshot, pages through its immutable watermark, then follows the
-atomic replay/live subscription. Subscription termination clears the view and
-reports the terminal code.
+The contracts are `@cordisx/protocol/sessions/v1` and the additive
+`EntityDefinitionBoundSessionEvent` from `@cordisx/protocol/entities/v1` at
+Protocol main commit `c96c290697f9e802a68c6d3bb094fd27d8d00d1e`.
+The Timeline reads a fixed Session snapshot, pages through its immutable
+watermark, then follows the atomic replay/live subscription. Subscription
+termination clears the view and reports the terminal code.
+
+For an entity-backed Session, `entity/definition-bound` is the durable source
+of the exact historical definition identity, digest, and definition bytes.
+The Timeline projects that persisted binding and never queries `ctx.entities`
+or relabels prior Session history from the latest local entity revision.
The plugin consumes no alternate event service, Host-private import, unsafe
cast, raw bridge, adapter, or second ledger.
@@ -68,6 +74,7 @@ npm run check
`npm run check` runs typecheck, build, focused tests, and
`npm pack --dry-run`. `cordisx.plugin.json` is the immutable package manifest.
The Protocol dependency is pinned to an exact remotely resolvable main commit.
-The Host runtime is formally available at CordisX main commit
-`d11df2e0d4a2557dc184f4fcec0b7cd8659e289b`. The package remains private until
-real-App integration verification is complete.
+The entity-backed Host runtime is formally available at CordisX main commit
+`ff9cf8b1ba4e4caffa23abbd767dbba0b8884c8a`; Chatroom's entity-backed consumer
+is at `dc1d672b93e6b6a3a29961561546957d66955a1a`. The package remains private
+until real-App integration verification is complete.
diff --git a/README.zh-Hans.md b/README.zh-Hans.md
index f08638d..9f9580b 100644
--- a/README.zh-Hans.md
+++ b/README.zh-Hans.md
@@ -20,11 +20,18 @@ Agent/Session Runtime 的 ownership model 如下:
Session 事实。
正式契约是 Protocol main commit
-`8891722a7735a3bd00bdd5315084b35b748f5e7f` 的
-`@cordisx/protocol/sessions/v1`。Timeline 先读取固定 Session snapshot,
+`c96c290697f9e802a68c6d3bb094fd27d8d00d1e` 的
+`@cordisx/protocol/sessions/v1`,以及
+`@cordisx/protocol/entities/v1` 中增量定义的
+`EntityDefinitionBoundSessionEvent`。Timeline 先读取固定 Session snapshot,
分页读取 immutable watermark,再接续 atomic replay/live subscription;订阅
终止时会清空视图并报告 terminal code。
+对 entity-backed Session,`entity/definition-bound` 是历史定义 identity、
+digest 与 definition bytes 的唯一持久事实。Timeline 只投影该 Session 事件中
+持久化的 binding,绝不查询 `ctx.entities`,也不按最新 local entity revision
+重标历史。
+
插件不消费替代 event service,也不使用 Host-private import、unsafe cast、
raw bridge、额外 adapter 或第二 ledger。
@@ -54,6 +61,7 @@ npm run check
`npm run check` 包含 typecheck、build、focused tests 与
`npm pack --dry-run`。Protocol dependency 精确 pin 到可从远端解析的 main
-commit。Host Runtime 已在 CordisX main commit
-`d11df2e0d4a2557dc184f4fcec0b7cd8659e289b` 正式提供;真实 App 集成验证闭合前,
-package 保持 private。
+commit。entity-backed Host Runtime 已在 CordisX main commit
+`ff9cf8b1ba4e4caffa23abbd767dbba0b8884c8a` 正式提供;Chatroom 的 entity-backed
+消费已在 main commit `dc1d672b93e6b6a3a29961561546957d66955a1a`
+落地。真实 App 集成验证闭合前,package 保持 private。
diff --git a/cordisx.plugin.json b/cordisx.plugin.json
index f6b9756..c02b053 100644
--- a/cordisx.plugin.json
+++ b/cordisx.plugin.json
@@ -20,7 +20,8 @@
"https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/session-snapshot.v1.schema.json",
"https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/session-subscribe-request.v1.schema.json",
"https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/session-subscription-page.v1.schema.json",
- "https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/session-subscription-close.v1.schema.json"
+ "https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/session-subscription-close.v1.schema.json",
+ "https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/entity-session-definition-binding.v1.schema.json"
]
},
"dependencies": [],
diff --git a/docs/MIGRATION-CHECKPOINT.md b/docs/MIGRATION-CHECKPOINT.md
index 1f59503..f1e861f 100644
--- a/docs/MIGRATION-CHECKPOINT.md
+++ b/docs/MIGRATION-CHECKPOINT.md
@@ -50,7 +50,9 @@ This repository currently owns:
- Host-neutral Timeline model, filtering, grouping, and read-only body view;
- `TraceShowcaseStore`, which is the read-only Timeline state boundary;
- `src/session-store.ts`, which consumes only
- `@cordisx/protocol/sessions/v1`, performs immutable snapshot paging, atomic
+ `@cordisx/protocol/sessions/v1` plus the
+ `EntityDefinitionBoundSessionEvent` extension from
+ `@cordisx/protocol/entities/v1`, performs immutable snapshot paging, atomic
replay/live subscription, and fail-closed generation/terminal fencing;
- truthful unavailable state whenever the public Session service or exact
route-bound permission is absent;
@@ -65,10 +67,12 @@ adapter/ledger, access a raw bridge, or depend on concrete `ctx.agentLoop`.
Formal Protocol dependency:
- repository: `CordisX/cordisx-protocol`;
-- main commit: `8891722a7735a3bd00bdd5315084b35b748f5e7f`;
+- main commit: `c96c290697f9e802a68c6d3bb094fd27d8d00d1e`;
- pull requests: `https://github.com/cordisx/cordisx-protocol/pull/77` and
- `https://github.com/cordisx/cordisx-protocol/pull/78`;
-- public entrypoint: `@cordisx/protocol/sessions/v1`;
+ `https://github.com/cordisx/cordisx-protocol/pull/78`, plus entities/v1
+ `https://github.com/cordisx/cordisx-protocol/pull/79`;
+- public entrypoints: `@cordisx/protocol/sessions/v1` and
+ `@cordisx/protocol/entities/v1`;
- status: merged to remote main; focused type, conformance, distribution,
diff, naming, and formal-main export readback passed. The broad Protocol
workflow retains its pre-existing Manager v2 AJV strict-union failure. Mono
@@ -90,11 +94,11 @@ must never manufacture a durable cursor or ledger.
Formal Host dependency:
- repository: `CordisX/cordisx`;
-- main commit: `d11df2e0d4a2557dc184f4fcec0b7cd8659e289b`;
+- main commit: `ff9cf8b1ba4e4caffa23abbd767dbba0b8884c8a`;
- pull requests: `https://github.com/cordisx/cordisx/pull/224` through
- `https://github.com/cordisx/cordisx/pull/231`;
+ `https://github.com/cordisx/cordisx/pull/236`;
- status: merged to remote main and pinned to Protocol
- `8891722a7735a3bd00bdd5315084b35b748f5e7f`; focused Agent/Session runtime,
+ `c96c290697f9e802a68c6d3bb094fd27d8d00d1e`; focused Agent/Session runtime,
Shell v4 terminal, permission/scope, no-check type, package allowlist, and
diff checks passed. The #231 composition focus passed 14/14 with CLI
`tsc --noCheck`. Its broad workflow stopped at
@@ -124,6 +128,29 @@ excluded from filesystem watch/import graphs so Vite cannot reinterpret
`cordisx/react`, `cordisx/ui`, or JSX-runtime virtual sources as absolute file
imports; the real transitive source watch graph remains intact.
+## ENTITY_DEFINITION_BINDING_READY handoff
+
+Formal entity-backed consumers:
+
+- Host main: `ff9cf8b1ba4e4caffa23abbd767dbba0b8884c8a`
+ (`https://github.com/cordisx/cordisx/pull/236`);
+- Chatroom main: `dc1d672b93e6b6a3a29961561546957d66955a1a`
+ (`https://github.com/cordisx/plugin-chatroom/pull/10`);
+- Protocol main: `c96c290697f9e802a68c6d3bb094fd27d8d00d1e`.
+
+Host persists `entity/definition-bound` as an ignorable SessionEvent when it
+creates an entity-backed Session and resumes only from that persisted binding.
+Chatroom creates against the exact current Entity identity and resumes an
+existing Session with `definitionSource: 'session-persisted'`.
+
+Agent Trace recognizes that one durable extension during the existing
+snapshot/replay/live path. Its projection exposes
+`binding.resolution.identity`, `binding.resolution.digest`, and
+`binding.resolution.definition`. It does not inject or query `ctx.entities`,
+consult Chatroom configuration, or relabel historical runs from a current
+registry snapshot. Other unknown required events still fail closed; unrelated
+extensions remain skippable only when explicitly marked `ignorable: true`.
+
## Deferred owner actions
Until real-App integration verification succeeds:
diff --git a/package-lock.json b/package-lock.json
index 2fc0cae..2ee5675 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,7 +14,7 @@
"cordisx": "0.1.0-beta.1"
},
"devDependencies": {
- "@cordisx/protocol": "github:cordisx/cordisx-protocol#8891722a7735a3bd00bdd5315084b35b748f5e7f",
+ "@cordisx/protocol": "github:cordisx/cordisx-protocol#c96c290697f9e802a68c6d3bb094fd27d8d00d1e",
"jsdom": "^26.1.0",
"typescript": "^5.9.2",
"vitest": "^3.2.4"
@@ -48,8 +48,8 @@
},
"node_modules/@cordisx/protocol": {
"version": "0.1.0-alpha.0",
- "resolved": "git+ssh://git@github.com/cordisx/cordisx-protocol.git#8891722a7735a3bd00bdd5315084b35b748f5e7f",
- "integrity": "sha512-y8AYpz29Dgpfo6YXgH7FWUMdApMNH3424PycwHcCIdHtp6AJqL7OyYVJgiWr5Yp4KUdfFbwlFvWAy/dqxeHpKw==",
+ "resolved": "git+ssh://git@github.com/cordisx/cordisx-protocol.git#c96c290697f9e802a68c6d3bb094fd27d8d00d1e",
+ "integrity": "sha512-Q9S3YT30akTq29g3cwe4JRvJ5uRJuNEeOk5Sds8OCjNliOKBnyAewHNmNesN2DQL509sqGX8LZVVFMhnay5j4A==",
"dev": true,
"license": "AGPL-3.0-or-later",
"engines": {
diff --git a/package.json b/package.json
index e710db6..f19136e 100644
--- a/package.json
+++ b/package.json
@@ -29,7 +29,7 @@
"cordisx": "0.1.0-beta.1"
},
"devDependencies": {
- "@cordisx/protocol": "github:cordisx/cordisx-protocol#8891722a7735a3bd00bdd5315084b35b748f5e7f",
+ "@cordisx/protocol": "github:cordisx/cordisx-protocol#c96c290697f9e802a68c6d3bb094fd27d8d00d1e",
"jsdom": "^26.1.0",
"typescript": "^5.9.2",
"vitest": "^3.2.4"
diff --git a/src/react-view.tsx b/src/react-view.tsx
index d7dad04..503d406 100644
--- a/src/react-view.tsx
+++ b/src/react-view.tsx
@@ -23,11 +23,14 @@ function options(values: readonly string[], all: string) {
function Detail({ event }: { readonly event: TraceEvent | undefined }) {
if (event === undefined) return
{JSON.stringify(event.payload, null, 2)}}>
diff --git a/src/session-store.ts b/src/session-store.ts
index 09d19aa..cd9683c 100644
--- a/src/session-store.ts
+++ b/src/session-store.ts
@@ -10,6 +10,7 @@ import type {
SessionSubscriptionPage,
UserMessage,
} from '@cordisx/protocol/sessions/v1'
+import type { EntityDefinitionResolution } from '@cordisx/protocol/entities/v1'
import type {
TraceEvent,
TraceLane,
@@ -72,6 +73,7 @@ function lifecyclePhase(event: SessionEvent): TracePhase | undefined {
function lane(event: SessionEvent): TraceLane {
switch (event.type) {
case 'user/message': return 'input'
+ case 'entity/definition-bound':
case 'agent/inbox/spliced': return 'injection'
case 'tool/call':
case 'tool/result':
@@ -109,6 +111,10 @@ function summary(event: SessionEvent): string {
case 'agent/inbox/spliced': return `Agent ${event.data.target} inbox changed.`
case 'approval/asked': return `Approval requested for ${event.data.toolName}.`
case 'approval/decided': return `Approval ${event.data.outcome}.`
+ case 'entity/definition-bound': {
+ const { identity, digest, definition } = event.data.resolution
+ return `Session definition bound to ${definition.name ?? identity.agentId} (${identity.agentId}@${identity.revision}; ${digest}).`
+ }
case 'session/end-seed': return 'Persisted Session seed ended.'
default: return unknownEvent(event)
}
@@ -171,6 +177,9 @@ export function projectSessionEvent(event: SessionEvent): TraceEvent | undefined
: event.type === 'tool/result'
? event.data.message.source.callId
: undefined
+ const definitionResolution: EntityDefinitionResolution | undefined = event.type === 'entity/definition-bound'
+ ? event.data.resolution
+ : undefined
return Object.freeze({
id: `session:${event.sessionId}:${event.seq}`,
sessionId: event.sessionId,
@@ -186,6 +195,7 @@ export function projectSessionEvent(event: SessionEvent): TraceEvent | undefined
...(step === undefined ? {} : { stepId: step }),
...(messageId === undefined ? {} : { messageId }),
...(toolCallId === undefined ? {} : { toolCallId }),
+ ...(definitionResolution === undefined ? {} : { definitionResolution }),
payload: event.data,
})
}
diff --git a/src/types.ts b/src/types.ts
index 98bdc56..955ee06 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,3 +1,5 @@
+import type { EntityDefinitionResolution } from '@cordisx/protocol/entities/v1'
+
export type TraceLane = 'input' | 'model' | 'tools' | 'injection'
export type TracePhase =
| 'opened' | 'resumed' | 'started' | 'updated' | 'completed'
@@ -28,6 +30,7 @@ export interface TraceEvent {
readonly messageId?: string
readonly toolCallId?: string
readonly contextId?: string
+ readonly definitionResolution?: EntityDefinitionResolution
readonly payload?: unknown
}
diff --git a/test/architecture-guard.test.ts b/test/architecture-guard.test.ts
index d1680cf..10f1c45 100644
--- a/test/architecture-guard.test.ts
+++ b/test/architecture-guard.test.ts
@@ -13,6 +13,7 @@ describe('architecture guard', () => {
'ctx.agentEvents',
'ctx.agentHistory',
'ctx.agentLoop',
+ 'ctx.entities',
'agentEvents',
'agentHistory',
'electronBridge',
@@ -23,6 +24,7 @@ describe('architecture guard', () => {
'getElementById',
'packages/cli/src',
'packages/host',
+ 'EntityRegistry',
]) expect(source).not.toContain(forbidden)
})
diff --git a/test/session-store.test.ts b/test/session-store.test.ts
index 20430db..1741b93 100644
--- a/test/session-store.test.ts
+++ b/test/session-store.test.ts
@@ -10,9 +10,11 @@ import type {
SessionSubscriptionClosed,
SessionSubscriptionPage,
} from '@cordisx/protocol/sessions/v1'
+import type { EntityDefinitionBoundSessionEvent } from '@cordisx/protocol/entities/v1'
import { SessionTraceStore, projectSessionEvent } from '../src/session-store.js'
const schema = 'https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/session-event.v1.schema.json' as const
+const persistedDigest = `sha256:${'a'.repeat(64)}` as const
function event(seq: number, type: SessionEvent['type'], data: unknown): SessionEvent {
return {
@@ -27,6 +29,40 @@ function event(seq: number, type: SessionEvent['type'], data: unknown): SessionE
} as unknown as SessionEvent
}
+function entityDefinitionBoundEvent(seq: number): EntityDefinitionBoundSessionEvent {
+ const identity = { agentId: 'chatroom.generalist', revision: persistedDigest }
+ return {
+ $schema: schema,
+ contract: 'cordisx.session-event/v1',
+ schemaVersion: 1,
+ sessionId: 'session-a',
+ seq,
+ time: Date.UTC(2026, 0, 1, 0, 0, seq),
+ type: 'entity/definition-bound',
+ ignorable: true,
+ data: {
+ source: 'entity-registry',
+ owner: { profileId: 'profile-a', installationId: 'installation-a', pluginId: 'chatroom' },
+ resolution: {
+ identity,
+ digest: persistedDigest,
+ definition: {
+ $schema: 'https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/agent-definition.v1.schema.json',
+ contract: 'cordisx.agent-definition/v1',
+ schemaVersion: 1,
+ identity,
+ name: 'Persisted Generalist',
+ inherit: {
+ promptSections: 'none', rules: 'none', skills: 'none',
+ tools: 'none', mcpServers: 'none', runtimeDefaults: 'none',
+ },
+ promptSections: [{ sectionId: 'role', kind: 'role', text: 'Persisted role bytes.' }],
+ },
+ },
+ },
+ }
+}
+
function page(afterSeq: number, snapshotSeq: number, events: readonly SessionEvent[]): SessionEventPage {
const nextAfterSeq = events.at(-1)?.seq ?? afterSeq
return {
@@ -166,6 +202,33 @@ describe('SessionTraceStore', () => {
expect(harness.unsubscribe).toHaveBeenCalledOnce()
})
+ it('carries the persisted entity binding through the same snapshot, replay, and live cursor path', async () => {
+ const harness = sessionHarness({
+ snapshotSeq: 0,
+ pages: [page(-1, 0, [entityDefinitionBoundEvent(0)])],
+ replay: [event(1, 'turn/start', { turn: 1 })],
+ })
+ const store = new SessionTraceStore(harness.sessions, 'session-a', 50)
+ await store.settled()
+
+ expect(store.getSnapshot().events.map(item => item.type)).toEqual([
+ 'entity/definition-bound',
+ 'turn/start',
+ ])
+ expect(store.getSnapshot().events[0]?.definitionResolution?.definition.name).toBe('Persisted Generalist')
+
+ await harness.observer()!(subscriptionPage('live', 1, [
+ event(2, 'user/message', {
+ id: 'message-user', role: 'user', content: [{ type: 'text', text: 'Continue' }], source: { kind: 'user' },
+ }),
+ ]))
+ expect(store.getSnapshot()).toMatchObject({
+ events: [{ type: 'entity/definition-bound' }, { type: 'turn/start' }, { type: 'user/message' }],
+ range: { totalAvailable: 3 },
+ })
+ store.dispose()
+ })
+
it('fails closed and clears rows on a sequence or generation fence violation', async () => {
const harness = sessionHarness({
snapshotSeq: 1,
@@ -245,6 +308,29 @@ describe('SessionTraceStore', () => {
})
describe('SessionEvent projection', () => {
+ it('attributes entity-backed history only to the Session-persisted definition binding', () => {
+ const projected = projectSessionEvent(entityDefinitionBoundEvent(0))
+
+ expect(projected).toMatchObject({
+ type: 'entity/definition-bound',
+ lane: 'injection',
+ definitionResolution: {
+ identity: { agentId: 'chatroom.generalist', revision: persistedDigest },
+ digest: persistedDigest,
+ definition: {
+ name: 'Persisted Generalist',
+ promptSections: [{ text: 'Persisted role bytes.' }],
+ },
+ },
+ payload: {
+ source: 'entity-registry',
+ resolution: { identity: { agentId: 'chatroom.generalist', revision: persistedDigest } },
+ },
+ })
+ expect(projected?.summary).toContain(`chatroom.generalist@${persistedDigest}`)
+ expect(projected?.summary).not.toContain('Latest local revision')
+ })
+
it('projects every durable core variant from the formal v1 vocabulary', () => {
const variants: readonly [SessionEvent['type'], unknown][] = [
['turn/start', { turn: 1 }],
diff --git a/test/view.test.tsx b/test/view.test.tsx
index e11393f..d34ce76 100644
--- a/test/view.test.tsx
+++ b/test/view.test.tsx
@@ -5,7 +5,7 @@ import { describe, expect, it, vi } from 'vitest'
import type { CordisXReactPageProps } from 'cordisx/contracts'
import { UnavailableTraceStore } from '../src/unavailable-store.js'
import { createTraceReactPage } from '../src/react-view.js'
-import type { TraceShowcaseStore } from '../src/types.js'
+import type { TraceEvent, TraceShowcaseStore } from '../src/types.js'
vi.mock('cordisx/ui', async () => {
const React = await import('react')
@@ -86,6 +86,20 @@ function availableStore(): TraceShowcaseStore {
}
}
+function eventStore(event: TraceEvent): TraceShowcaseStore {
+ const baseline = availableStore().getSnapshot()
+ const snapshot = Object.freeze({
+ ...baseline,
+ events: Object.freeze([event]),
+ range: Object.freeze({ loaded: 1, totalAvailable: 1, renderedLimit: 100 }),
+ })
+ return {
+ getSnapshot: () => snapshot,
+ subscribe: () => () => {},
+ dispose: () => {},
+ }
+}
+
describe('Host-owned Agent Trace body', () => {
it('renders SessionEvent rows, detail, and no Host chrome', async () => {
const dom = new JSDOM('')
@@ -116,6 +130,41 @@ describe('Host-owned Agent Trace body', () => {
root.unmount()
})
+ it('shows the Session-persisted entity identity, digest, and definition', async () => {
+ const dom = new JSDOM('')
+ const digest = `sha256:${'a'.repeat(64)}` as const
+ const identity = { agentId: 'chatroom.generalist', revision: digest }
+ const store = eventStore(Object.freeze({
+ id: 'session:session-a:0', sessionId: 'session-a', seq: 0,
+ recordedAt: '2026-01-01T00:00:00.000Z', lane: 'injection',
+ type: 'entity/definition-bound', semanticType: 'entity/definition-bound',
+ summary: 'Session definition bound to Persisted Generalist.',
+ source: Object.freeze({ kind: 'session', id: 'session-a', label: 'Host Session authority' }),
+ definitionResolution: {
+ identity,
+ digest,
+ definition: {
+ $schema: 'https://raw.githubusercontent.com/cordisx/cordisx-protocol/main/schemas/agent-definition.v1.schema.json',
+ contract: 'cordisx.agent-definition/v1', schemaVersion: 1,
+ identity, name: 'Persisted Generalist',
+ inherit: {
+ promptSections: 'none', rules: 'none', skills: 'none',
+ tools: 'none', mcpServers: 'none', runtimeDefaults: 'none',
+ },
+ },
+ },
+ }))
+ const root = await mount(dom, store)
+
+ dom.window.document.querySelector