Replace the single file-structure.md with a stratified layout designed
for AI/agent skill consumption: tables and concrete identifiers over
prose, files loaded on demand, content separated by churn rate.
Layers:
- architecture/ stable structural reference (overview, source-map,
test-map, data-flow)
- patterns/ memory, commands, autocomplete, events, xmpp,
encryption, ui, plugins
- testing/ unit-tests, stubs, functional-tests, bench
- build/ local, docker, ci
- playbooks/ add-command, add-test, add-autocomplete,
add-event-handler, add-encryption
- gotchas.md append-only dated entries (seven seed entries)
- wip/ branch-specific notes; deleted on merge to master
Stable layers describe cproof on master only. In-flight feature
branches (currently feat/ai) get a single file under wip/.
INDEX.md is the entry map with churn labels; SKILL.md is the
always-loaded skill hint pointing to it.
110 lines
3.0 KiB
Markdown
110 lines
3.0 KiB
Markdown
# Playbook: add an event handler
|
|
|
|
`src/event/` mediates between `src/xmpp/` (and locally-triggered actions)
|
|
and the rest of the codebase. Two directions:
|
|
|
|
- **Server events** (`sv_ev_*`) — triggered by inbound stanzas.
|
|
- **Client events** (`cl_ev_*`) — triggered by local actions (UI, command).
|
|
|
|
See `patterns/events.md` for the conceptual split.
|
|
|
|
## Decide direction
|
|
|
|
| Trigger | Direction | File |
|
|
|---|---|---|
|
|
| Inbound stanza parsed in `src/xmpp/`. | Server. | `src/event/server_events.c/h` |
|
|
| User typed a command, UI key, or internal timer. | Client. | `src/event/client_events.c/h` |
|
|
|
|
## Add the handler — server example
|
|
|
|
`src/event/server_events.h`:
|
|
|
|
```c
|
|
void sv_ev_foo_received(const char* const from, const char* const payload);
|
|
```
|
|
|
|
`src/event/server_events.c`:
|
|
|
|
```c
|
|
void
|
|
sv_ev_foo_received(const char* const from, const char* const payload)
|
|
{
|
|
// 1. Update persistent state.
|
|
chatlog_msg_in(from, payload);
|
|
|
|
// 2. Push UI update.
|
|
ProfChatWin* win = wins_get_chat(from);
|
|
if (win) {
|
|
chatwin_incoming_msg(win, payload, NULL, FALSE);
|
|
} else {
|
|
cons_show_incoming_message(from, payload);
|
|
}
|
|
|
|
// 3. Plugin callbacks last — state must be consistent.
|
|
plugins_post_chat_message_received(from, payload);
|
|
}
|
|
```
|
|
|
|
## Wire the trigger site
|
|
|
|
For a server event, the matching parser in `src/xmpp/` calls the new
|
|
handler. For instance, if `<foo>` stanzas are parsed in
|
|
`src/xmpp/message.c`:
|
|
|
|
```c
|
|
// ... after parsing the stanza ...
|
|
sv_ev_foo_received(from, payload);
|
|
```
|
|
|
|
For a client event, call from `cmd_funcs.c` (or wherever the user-facing
|
|
trigger lives).
|
|
|
|
## Stub the handler in tests
|
|
|
|
`tests/unittests/event/stub_*.c` — add a stub for the new function so units
|
|
that exercise the trigger site link cleanly:
|
|
|
|
```c
|
|
void
|
|
sv_ev_foo_received(const char* const from, const char* const payload)
|
|
{
|
|
// pass-through (or check_expected, depending on test needs)
|
|
}
|
|
```
|
|
|
|
If a test needs to verify the handler is called with specific args, switch
|
|
the stub to `check_expected` and the test queues `expect_string` / `expect_value`.
|
|
|
|
See `testing/stubs.md`.
|
|
|
|
## Plugin hook (if applicable)
|
|
|
|
If the event should fire a plugin callback, add a dispatcher in
|
|
`src/plugins/plugins.c/h` (e.g. `plugins_post_foo_received`) and call it
|
|
**last** in the handler — after state and UI are consistent. Keep C and
|
|
Python plugin APIs in sync (`c_api.h` / `python_api.h`).
|
|
|
|
## Unit test the handler
|
|
|
|
`tests/unittests/test_server_events.c` (or `test_client_events.c`) holds
|
|
event-side tests. Pattern:
|
|
|
|
```c
|
|
void
|
|
test_sv_ev_foo_received_writes_chatlog(void** state)
|
|
{
|
|
expect_string(chatlog_msg_in, jid, "alice@example.com");
|
|
expect_string(chatlog_msg_in, msg, "hello");
|
|
sv_ev_foo_received("alice@example.com", "hello");
|
|
}
|
|
```
|
|
|
|
## Conventions
|
|
|
|
- One handler, one event. Don't pile multiple unrelated events into the same
|
|
function.
|
|
- Order inside the handler: state → UI → plugins. Never call plugins before
|
|
state is committed.
|
|
- `sv_ev_*` and `cl_ev_*` should be straight-line — no XMPP protocol parsing,
|
|
no UI rendering. They orchestrate; they don't implement.
|