|
| 1 | +commy plugin: release 0.20.0 |
| 2 | + |
| 3 | +Minor release of the commy plugin, **0.19.1 → 0.20.0**. |
| 4 | + |
| 5 | +This release changes the behaviour of `post` against a resolved thread. It is a |
| 6 | +bug fix, but it is a bug fix you must sequence — read **Upgrading** before you |
| 7 | +take it. |
| 8 | + |
| 9 | +## Highlights |
| 10 | + |
| 11 | +**`post` no longer forks a resolved thread into a sibling topic.** Posting to a |
| 12 | +thread by name after that thread had been resolved silently created a *new* |
| 13 | +topic under the bare name instead of appending to the original. The conversation |
| 14 | +split at the resolve, and the caller got back a well-formed `MessageRef` |
| 15 | +pointing at the wrong topic — so nothing looked wrong at the call site. Zulip has |
| 16 | +no resolved flag; resolution *renames* the topic to `✔ <name>`, and Zulip creates |
| 17 | +topics implicitly on send. Two of commy's three topic-addressing paths already |
| 18 | +knew that (`read_thread`, `resolve_thread`); the write path did not, so it |
| 19 | +addressed a name that no longer existed and Zulip helpfully created it. |
| 20 | + |
| 21 | +`post` now probes for the thread's current substrate form — the same probe |
| 22 | +`resolve_thread` already used — and addresses that. The probe tries the plain |
| 23 | +name first and the `✔` form second, matching `read_thread`, so the two verbs can |
| 24 | +never disagree about which conversation a name refers to (in a realm this bug has |
| 25 | +already forked, both forms exist, and a different precedence would have `post` |
| 26 | +writing to one and `read_thread` reading the other). |
| 27 | + |
| 28 | +**The behaviour change: a post into a resolved thread appends to it, and the |
| 29 | +thread stays resolved.** `MessageRef.resolved` now reports what the probe found |
| 30 | +rather than a hardcoded `false`. **`post` never mutates thread state** — if you |
| 31 | +want a reply to re-open a thread, call `unresolve_thread` yourself. Auto-unresolve |
| 32 | +on post was considered and rejected: it would make `post` a two-request mutating |
| 33 | +verb on the substrate's hottest write path, one that can partially fail (the |
| 34 | +message lands, the unresolve doesn't). |
| 35 | + |
| 36 | +**Seats no longer hang on their first `post`/`react` after an unlucky resume.** |
| 37 | +A returning ephemeral seat reports its resume verdict — "did my event queue |
| 38 | +survive, or do I need history catch-up?" — off its first `/events` poll. A poll |
| 39 | +that reached neither a success nor a dead-queue answer (a response-decode error, |
| 40 | +a non-`BAD_EVENT_QUEUE_ID` API error, a wedged `429` loop) retried forever under |
| 41 | +the never-give-up reconnect schedule, so the verdict was never reported. The |
| 42 | +seat's first attribution-producing call awaits that verdict inline, so it hung |
| 43 | +indefinitely. A bounded 60s fallback now guarantees the latch always completes, |
| 44 | +falling back to the duplicate-tolerant history catch-up. A healthy poll returns |
| 45 | +in well under a second and always wins the race; the retry loop underneath is |
| 46 | +untouched. |
| 47 | + |
| 48 | +## Upgrading |
| 49 | + |
| 50 | +**If any of your flows treat "unresolved" as "needs attention", make the |
| 51 | +caller-side change *before* you take this release, not after.** |
| 52 | + |
| 53 | +The old fork was **loud**: you got two topics and a visibly wrong conversation. |
| 54 | +The new append is **quiet**: the item simply stops asking. A flow that relied on |
| 55 | +"a reply re-opens the thread" — where the fork happened to leave an unresolved |
| 56 | +bare-name topic that *looked* like an open item — will, on this build, post into |
| 57 | +the resolved thread and leave it resolved. You trade a visible bug for an |
| 58 | +invisible one. The fix is to call `unresolve_thread` explicitly wherever a reply |
| 59 | +is meant to re-open the item; do that first, then upgrade. |
| 60 | + |
| 61 | +**Note that the plugin's launcher is not version-pinned.** `.mcp.json` launches |
| 62 | +`npx -y @codeforbreakfast/commy-mcp`, which resolves npm's `latest` at every MCP |
| 63 | +start — so seats pick this release up on their next restart without an explicit |
| 64 | +opt-in. If you need to gate the upgrade behind your own caller-side change, do it |
| 65 | +with npm's release-age soak (`COMMY_NPM_MIN_RELEASE_AGE`, which this plugin |
| 66 | +threads into its own `npx` launch as `npm_config_min_release_age`) rather than |
| 67 | +assuming a restart won't take it. |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +Seven-way version parity bumped in lockstep (enforced by `manifests.test.ts`). |
0 commit comments