Commit A of the Toast pilot: introduce the trigger() contract end-to-end
without migrating any provider yet. Toast's actual migration follows in a
separate commit so that bugs (if any) can be attributed to one layer at a
time.
App layer
- New `AppSemantic` contract in `$lib/ext/app/types`: a single-method
interface (`emit(signal): Promise<void>`) implemented by SemanticEngine
and accepted by the App constructor.
- App now owns `app.semantic`, defaulting to a fresh SemanticEngine wired
to `app.dom` when the consumer doesn't pass one. Fallback App also gets
`fallbackSemantic` so context-less calls don't throw.
- Soma re-exposes it as `soma.semantic` so providers reach it the same
way they reach `soma.dom` / `soma.langs`.
MorfoRuntime
- New `runtime.trigger(eventName): Promise<void>` with the full sequence
the architecture closed:
1. apply `prewrite` attrs imperatively (transient markers)
2. await `semantic.emit({ target, name, family, intent? })` — resolves
the intent from `event.semantic.intent` (literal SemaIntent or
SemaIntentBinding via `resolveSemaIntent`)
3. invoke the provider's handler from `sources.events[name]`
4. effects on the affected parts re-derive structural attrs and
write them via `dom.apply` (automatic — not awaited here)
- New optional `sources.semantic` and `sources.events` on the runtime.
Without `semantic`, step 2 is skipped. Without an events handler for
the name, step 3 is skipped.
- `morfo.events.commits` is intentionally NOT executed by the runtime —
it remains descriptive metadata documenting what an external observer
will see (per the architecture's "state is the source of truth" rule).
Tests
- 8 new trigger unit tests using the toast and dialog morfos:
- throws on undeclared event
- throws when target part has no DOM
- emits with intent resolved from prop (toastMorfo.announce)
- falls back to declared default intent when prop missing
- omits intent for transitional families (toastMorfo.present)
- prewrite applied before semantic.emit (dialogMorfo.close-cancel)
- handler invoked only after semantic.emit resolves
- skips semantic.emit when no semantic source provided
Verification
- 84/84 focused unit tests green (full adom/sema/lib/dom/morfo/soma).
- 66/66 morfo-check.
- 68/68 smoke routes.
- svelte-check unchanged at 155 (no new errors introduced).
Next (commit B): migrate Toast to use the contract — wire close.onclick,
swipe gesture, auto-timer, and Escape keydown to runtime.trigger().