All notable changes to this project are documented here. The format follows Keep a Changelog, and this project adheres to Semantic Versioning.
For the canonical narrative version of each release (rewritten after CI publishes the auto-generated notes), see the matching entry on the releases page.
- AGENTS.md kept under the 150-line index limit
- matrix-communication and matrix-announcement SKILL.md brought under the word cap
3.1.1 - 2026-08-17
- The fallback install hints in
_lib/deps.pyand the macOS workaround in the setup guide now pinmatrix-nio[e2e]<0.26like the nine E2EE scripts and the doctor already did. Following the unpinned hints landed on 0.26/vodozemac, and the nextuv runscript then failed to open the store (#109). - The backend-mismatch hint names the likely store rewriter: a script copy from an older skill version whose unpinned dependency resolves the newest matrix-nio. The generic hint had invited exactly that copy as a workaround (#113).
matrix-communicationgates multi-line sends onmatrix-announcement, so structured posts get its content rules before delivery (#110).- Device verification prefers
--listen;--requestis for the case of a single Element session (#112).
SKILL.mdandAGENTS.mdsay how to read a room log: adjacent lines from one sender are adjacent events, and a reaction or redaction line names its target only when the daemon still holds it. Written after a summary turned four unrelated events into a story (#104).
3.0.0 - 2026-08-13
-
synapse-migrate-room.pyasks before it changes anything (#102). It read no state and asked no question: invoking it started a pipeline whose last step, enabling Megolm encryption, cannot be undone. It now reads the room's current state first, prints the steps it would take with the irreversible one marked, and stops for confirmation before the first write.-y/--yesskips the question, mirroringsynapse-deactivate-user.py.Callers that run it from a script or a pipe now exit
2withRefusing to run non-interactively without --yesuntil they pass--yes. Aborting at the prompt exits1and leaves the room untouched.
- The room log records what a reaction reacts to and what a redaction removed
(#104). It recorded that they happened and nothing else, so a reader saw
reacted ✅️andremoved a messagewith no way to tell which message, or whether two such events were related.matrix-watchd.pydroppedReactionEvent.reacts_to,RedactionEvent.redactsand itsreason; records now keep them, and the display line names the target when the daemon has it —reacted ✅️ to "so bin beim RA". Beyond the last few hundred messages of a room the line falls back to its previous wording rather than printing an event id.
- README,
AGENTS.mdanddocs/ARCHITECTURE.mddescribe the watch daemon, thematrix-niopin and the store migration it forces. - The live-awareness spec describes the relation fields a record carries and the window in which a target stays nameable.
- The safety guide,
SKILL.mdandAGENTS.mddescribe the confirmation gate both destructivesynapse-*scripts now have. skills/matrix-communication/README.mdpoints at the two license files that exist; theLICENSEit linked never did.
2.0.0 - 2026-08-13
-
matrix-niois pinned below 0.26, and the pin is tied to the E2EE store. 0.26 sends the SAS commitment as a hex digest where 0.25 sent unpadded base64, so Element rejects every verification before it renders emoji (matrix-nio#570). The two releases also use different crypto backends and write incompatible stores: opening one with the other fails asOlmAccountError: BAD_ACCOUNT_KEY, which names a key that was never the problem. An installation whose store was written by 0.26 must recreate it — the store cannot be migrated:matrix-e2ee-setup.py --logout && matrix-e2ee-setup.py matrix-key-backup.py --import-keys matrix-e2ee-verify.py --request DEVICE -
Every path that opens the store now takes an exclusive lock. Commands that previously ran beside each other, or beside the daemon, now wait and then refuse with the holder's pid. That is the point: two nio processes on one store corrupt it, and until now nothing stopped them. Commands routed through the daemon — send, react, redact, edit — keep working while it runs.
-
matrix-e2ee-setup.py --logoutdeletes only its own device's store files. It globbed*.dband*_devicesacross the shared store directory, so logging one device out destroyed the megolm history of every other. Use--purge-allfor the old behaviour.
- Live room awareness.
matrix-watchd.pyholds the E2EE store, syncs, decrypts and appends every event of a watched room torooms/<slug>.jsonl;matrix-watch.pyfollows that log without touching the store, so any number of readers can run at once. Send, react, redact and edit route through the daemon's Unix socket when it is running and fall back to the direct path when it is not — no new flags, no second way to send a message. Rooms come fromwatch_roomsin the config. Design and plan indocs/specs/anddocs/exec-plans/completed/. - Real mentions.
--mention '@user:server'(repeatable) and--mention-roomsetm.mentions(MSC3952), which is what notifies a modern client; a plain@nameonly ever matched the legacy push rule on an exact localpart. The pill goes into the HTML body, the plain body keeps the bare name. - A governance section in
SKILL.mdon who turns the agent's function on, off or wider, and what a third party in a room may and may not decide. - A CI job that runs the unit tests. They existed and nothing executed them.
matrix-key-backup.py --import-keysimported nothing. It decrypted every session, discarded it, and counted it as imported. Three further defects sat in front of that: the AES-CBC IV was read off the ciphertext instead of the HKDF output, the MAC check rejected every backup written by a libolm client, and the backup key the script itself stores "for future use" had no code path that read it back.matrix-doctor.pyverified only the first token it found and never asked the homeserver about the E2EE credential at all, so a deleted device reported[OK] e2ee_setupwhile every E2EE call failed withRoom not found.- A fresh device could not verify. nio cannot build a verification for a device it has no keys for, and the resulting error named the transaction rather than the missing device.
selfin the event log marked the account, not the device. An agent and the person it works for share one account, so the flag was true for both — useless for the one thing it exists for.- The E2EE guide recommended reusing a running client's access token. That hijacks the client's device and breaks decryption in it, silently.
- The event log renders an unknown display name as the localpart rather than the whole MXID, and the daemon remembers a name once it has seen it.
ruffin.pre-commit-config.yamlmatches the version CI runs.
1.28.0 - 2026-08-08
-
matrix-doctor.pyreported a healthy setup for a token the homeserver rejects.check_configonly proved thatconfig.jsonexists, parses, and carrieshomeserveranduser_id— it never asked the homeserver anything, so an expired or revoked token still produced[OK] configandAll checks passed! Matrix Skill is ready to use.while every authenticated call returned HTTP 401M_UNKNOWN_TOKEN. A green doctor beside a 401 sends you looking for the problem everywhere except at the credential; worse, when the doctor's verdict stands in as evidence that something is fine, a dead token reads as "nothing to see" instead of "could not verify".A new
tokenrow asksGET /_matrix/client/v3/account/whoamiand reports three states: accepted (and belonging to the configureduser_id), rejected, or not verified. Not-verified renders as[??], neverOK— no token in the config is normal for E2EE use,--offlineskips the call, and an unreachable homeserver is a missing answer. The summary line now names what it could not verify instead of claiming everything passed.
matrix-doctor.py --offlineskips the token check's homeserver call for air-gapped or CI runs; the row then reads "not verified" rather than OK.
1.27.1 - 2026-07-26
- matrix-communication:
references/hookshot-integration.md— documents provisioning webhooks via the matrix-hookshot bridge bot (invite, promote to moderator,!hookshot webhook <name>command, retrieving the secret URL from the bot's admin DM), discovered via live testing (#61).
1.27.0 - 2026-07-26
- matrix-communication: room management —
matrix-create-room.py(create, with optional alias/topic/initial invites),matrix-invite.py, andmatrix-power-level.py(--show/--get/--set, GET-modify-PUT againstm.room.power_levels) (#59).
- matrix-announcement: resolve the cross-skill script path in SKILL.md (#53).
- matrix-communication:
no-editorializing.mdnow points at the canonical copy (#54). - matrix-announcement: trimmed SKILL.md generic bloat to meet the 500-word cap (#53).
- matrix-communication, matrix-announcement: added a "no editorializing" rule for messages and announcements — state what changed, not how good it is (#51).
- matrix-communication: E2EE own-device delivery and verification — don't report verification success on a MAC mismatch, and fetch room keys in
--listen(#47, #48).
- matrix-communication: gate E2EE device trust strictly on verification, with matrix-nio API notes (#49).
- matrix-communication: corrected misleading
brew install libolmguidance in the setup guide and_lib/deps.pyruntime error —python-olmhas no macOS wheel and statically links its own bundledlibolm, so Homebrew's library is never used. Documented the macOS 26 (Tahoe) / Apple Clang 17 build failure, a community-reported build-from-source workaround (GCC +CMAKE_POLICY_VERSION_MINIMUM), and the upstream status (libolm deprecated; vodozemac migration inmatrix-nioPR #555) (#43)
- matrix-administration: documented that room IDs may be passed without the
:serversuffix, and added the Synapse admin messages endpoint to the admin-API reference (#45).
1.25.0 - 2026-06-10
- matrix-announcement: entity-linking rules (every issue key linked, versions link to their release page, MRs/PRs in
project/path!N/org/repo#Nnotation) and one-item-per-line status-update layout (#41)
- matrix-communication:
[Unable to decrypt]guidance now recommendsmatrix-fetch-keys.pyfirst — resolves the common missing-room-keys case without a recovery key (#41)
1.24.0 - 2026-05-28
1.23.0 - 2026-05-15
- Ship as npm package via
@netresearch/agent-skill-coordinator(#37)
- Declare both matrix skills in
aiAgentSkill/extra.ai-agent-skill; include.claude-plugin/plugin.jsonin the npm tarball (#37)
1.22.0 - 2026-04-29
matrix-announcementskill — third skill in the plugin, alongsidematrix-communication(transport) andmatrix-administration(Synapse ops). Content guidance for composing scannable, structured Matrix room announcements: HTML subset clients render, type-tag system (Release/Patch/Heads-up/Digest/Postmortem/RFC/New skill), glyph rules,m.textvsm.noticechoice, and when to render an HTML card to PNG. Ships seven references, three rendered HTML card templates (1200×630 / 1200×1500 / 1200×900), 12 evals, and a visual gallery. No scripts. (#31)--noticeflag onmatrix-send-e2ee.pyandmatrix-send.py— sendsm.noticeinstead ofm.text, mutually exclusive with--emote. msgtype precedence:notice > emote > text. Closes the gap the announcement skill recommended but the transport scripts didn't support. (#32)
matrix-send-e2ee.pyandmatrix-send.py:--emoteand--noticeare now grouped viaargparse.add_mutually_exclusive_group. Thesend_message_e2ee()andsend_message()functions gained anotice: bool = Falsekeyword parameter.matrix-communicationquick-reference,messaging-guide.md, and the rootAGENTS.mdcheat-sheet updated to document--notice.matrix-announcement/references/image-cards.md: corrected guidance —m.noticeis text-only; for an image announcement, send the card asm.imageand a follow-up notice-flavour text message (msgtypem.notice, sent viamatrix-send-e2ee.py … --notice) rather than trying to flag the image event itself asm.notice.
1.21.1 - 2026-04-29
Maintenance release: matrix-administration script harness improvements (10 → 18 checks), CI compatibility fixes, formatter robustness for pre-wrapped links and emphasis flanking.
1.21.0 - 2026-04-26
Quality overhaul of matrix-administration: 95% faster E2EE operations, 28 evals, expanded harness, full-text formatter improvements.
1.20.1 - 2026-04-22
Security patch: URL scheme validation before urllib.request.urlopen in matrix-administration.
1.20.0 - 2026-04-16
Added the matrix-administration skill — Synapse server operations (snapshot rooms, rate room health, render Graphviz map, force-join, promote, harden, deactivate, search history). Stdlib-only Python.
Older releases (before this changelog was introduced) are documented on the releases page.