Adapter authoring
@sectile/core/adapter-runtime is the stable boundary for adding a host without importing Core internals.
text
host input
→ decode
semantic event
→ reduce
semantic command
→ project
host effect
→ execute in the host1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Core owns revision ordering, semantic reduction, controlled-state reconciliation, and command projection. The adapter owns host input decoding and effect execution. A decoder returns null for input outside its semantic domain; this is an ignored host input, not a failed semantic transition.
Minimal adapter
ts
import { createHostAdapter } from '@sectile/core/adapter-runtime'
type KeyInput = { readonly key: string }
type Event = { readonly type: 'next' }
type Command = { readonly type: 'announce'; readonly index: number }
type Effect = { readonly type: 'speak'; readonly text: string }
const result = createHostAdapter({
initial: { ok: true, value: { index: 0 } },
decode: (input: KeyInput): Event | null =>
input.key === 'ArrowDown' ? { type: 'next' } : null,
reducer: (state, event): {
readonly ok: true
readonly value: {
readonly state: { readonly index: number }
readonly commands: readonly Command[]
}
} => {
const index = event.type === 'next' ? state.index + 1 : state.index
return {
ok: true,
value: {
state: { index },
commands: [{ type: 'announce', index }],
},
}
},
project: (command: Command): Effect => ({
type: 'speak',
text: `Item ${command.index + 1}`,
}),
})
if (result.ok) {
const transition = result.value.handleInput({ key: 'ArrowDown' })
if (transition?.ok) {
for (const effect of transition.commands) executeHostEffect(effect)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
The commands field of the returned revision result contains projected host effects. Core never executes them.
Contract
decodemust be deterministic for the same normalized host input.reducer,reconcile, andprojectmust remain pure.notifyreports a proposed semantic state; it does not transfer controlled-state ownership.replacesynchronizes externally owned state and advances the revision.- Pass the observed revision to
handleInputwhen input may race with external state. Stale input is rejected without changing the snapshot. - Execute effects only after an
okresult. Preserve their array order. - Release host listeners and resources in the host package. Core owns no platform lifecycle.
Use createSemanticController directly only when host decoding is already handled by another boundary.
