Cover the remaining role-carrying events with test_sink assertions:
session.started (ctx-held role), session.resumed and session.ended
(session-held role), turn.interrupted and turn.failed. Correct the
mcp_factory race comment (the loser fires a second plain connected and
overwrites the winner's map entry, not a reconnect) and the role_extras
doc (derived roles fall through to the session-held role).
- McpFactory::acquire now reads the live probe and the reconnect flag
under one lock section so a racing first connect cannot be
misreported as COYOTE_MCP_RECONNECT=true; the residual window across
the spawn await (no singleflight) is documented. try_get_active,
now unused, is removed.
- All three RAG sync funnels (init, init_with_config,
refresh_document_paths) fire rag.sync.completed only after a
successful save(), matching the documented contract; a save error
routes through rag.sync.failed with COYOTE_ERROR before propagating.
- New funnel-level tests pin init_with_config's started-then-completed
ordering, the failed event on a save error, and the failed event on
the sync Err arm that a user abort surfaces through.
- Register five new events (26 -> 31): rag.sync.started/completed/failed
and mcp.server.connected/failed, whitelist-gated for agent global hooks
like every non-escalation event
- Bracket every real RAG document sync with rag.sync.* via pre-resolved
RagSyncHooks snapshots carried into the build funnels; cache hits, pure
loads, and attaches fire nothing, and a user abort mid-sync counts as a
failure
- Fire mcp.server.connected only on real spawns in McpFactory::acquire
(reusing a live server is silent), marking respawns of a previously
live key with COYOTE_MCP_RECONNECT; mcp.server.failed carries
COYOTE_ERROR and COYOTE_MCP_AUTH_REQUIRED on auth failures. Boot-time
registry connects deliberately fire nothing
- Carry COYOTE_ROLE on session.* and turn.* events, resolved at fire
time so mid-session role switches and temp roles are reflected
The advisory flock in run_checks.py uses one system-wide lock path
(~/.cache/coyote/review-stack.lock). Concurrent runs of the timeout/deadline
tests contend on it: the losers sleep in the bounded retry loop, the total
deadline (computed before lock acquisition) expires, and commands record
'skipped: deadline' instead of the expected timeout/kill behavior.
- run_checks.py: ADVERSARY_STACK_LOCK_PATH env override relocates the lock
file (isolation seam, documented alongside the other env seams); every
other lock semantic (default path, bounded wait, proceed-unlocked warning,
fd held for process lifetime, skip when no commands) is unchanged.
- run_checks.py: guard the POSIX-only fcntl import — windows-latest CI has
python3 and runs these tests, so the unconditional import crashed the
script there; on non-POSIX the lock is now skipped with a stderr WARN,
matching the script's documented non-POSIX limitations.
- tests: the shared adversary_script_command helper points every spawned
script at its own throwaway lock path, isolating the run_checks tests from
each other and from any real review running on the same machine.
- tests: adversary_run_checks_stack_lock_path_is_env_overridable pins the
override (lock file appears at the overridden path across two sequential
runs; no lock file is created when no commands are declared).