# Changelog All notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Fixed - **`npm ci` lockfile drift fixed.** The per-platform `optionalDependencies` subentries in `package-lock.json` (`node_modules/@fateforge/wechat-mp-cli-*`) were missing their `version`, so once those platform packages were published, `npm ci` failed its consistency check (`lock file's @ does not satisfy @`). The version bump (`scripts/version-files.js`) now syncs the lockfile platform subentries too, and `check-version.js` guards them, so the drift can't silently recur. ## [1.0.10] - 2026-06-29 ### Fixed - `update` Sigstore signature verification now bounds the TUF trust-root refresh with a timeout and binds it to the command context. A hung or slow Sigstore TUF registry can no longer stall the `verify_signature` stage indefinitely, and a SIGINT during the refresh is honored instead of being swallowed until the fetch returns (CLI-SPEC §14). - `update` now classifies a TUF trust-root **refresh** failure (network/timeout) as a retryable network condition (wrapped as a bundle-download failure) instead of the non-retryable `E_INTEGRITY` reserved for a genuine signature/identity/checksum mismatch. - `update` notice cache reads are now version-aware: a cached `update_available` notice is suppressed once the running binary is already at or past the cached latest version. Business commands no longer keep advertising an update to a version that is already installed for up to the 24h cache TTL (for example, right after a successful update). ## [1.0.9] - 2026-06-25 ### Added - **Contract single-source**: vendors `ai-native-cli-spec@v1.4` into `.agent/` (specs, `contract/contract.json`, codegen scripts). `internal/contract/contract_gen.go` is now generated from `contract.json` and is the binding source for every error-code → exit-code → retryability mapping. `scripts/check-spec.js` is the CI-red guard against drift. - `ExitCodeForErrorCode(code)` and `RetryableForErrorCode(code)` functions in `internal/output`, both delegating to `internal/contract` so the output layer cannot drift from the fleet contract (CLI-SPEC §6). - **Contract conformance test** (`internal/output/contract_conformance_test.go`): asserts every emitted error code is in `contract.Codes` with exact exit+retryable, `SchemaVersion` matches, and envelope top-level/meta keys are within the canonical sets — including catching any stray `meta.timestamp`. - **npm-drive update**: `update` now detects npm installs (binary under `node_modules`) and, for those installs, **executes `npm install -g @fateforge/wechat-mp-cli@`** on the user's behalf via the testable seam `updateRunPackageManager` instead of self-replacing the npm-managed binary. `--dry-run` previews the command without executing. A PM failure emits `E_IO` (exit 1, `binary_replaced:false`). Standalone GitHub binaries keep the Sigstore self-update path. ### Changed - `meta.timestamp` removed from the JSON envelope: the canonical contract (`meta_required_keys: [duration_ms]`, `meta_optional_keys: [notices]`) does not include `timestamp`, so it was a conformance violation caught by the new conformance test. - `.agent/` specs synced from `ai-native-cli-spec@v1.4` (single-source replaces hand-maintained copies). - `SchemaVersion` in `internal/output` now references `contract.SchemaVersion` (was a hard-coded string literal). - CI: `Verify spec/contract sync` step added to `npm-audit` job, immediately after `Verify version sync`. ## [1.0.8] - 2026-06-25 ### Changed - Binary self-update on **Windows** now uses the same in-process atomic **rename trick** as Unix (write `..new` → move the in-use binary aside to `..old` → rename `.new` into place → roll back from `.old` on failure). The old `.cmd` replace-on-restart helper script is gone: `update` completes the swap in one call and reports `status: "updated"` / `binary_replaced: true` on every platform — no `scheduled` state, no restart required. ### Fixed - `update` failure classification no longer collapses every fault into one class (CLI-SPEC §6/§14). A **discover/download** HTTP failure is now mapped by upstream status — `404` → `E_NOT_FOUND` (exit 3, non-retryable; e.g. an unknown `--target-version`), `429` → `E_RATE_LIMITED`, `5xx` → `E_SERVER`, timeouts → `E_TIMEOUT` (exit 8) — instead of always reporting `E_NETWORK`. Failing to **download the signature bundle** is now a retryable network failure at the `download` stage rather than a non-retryable `E_INTEGRITY` verdict; only an actual signature/identity/checksum verification failure (or a release that ships no bundle at all) stays `E_INTEGRITY` (exit 1, non-retryable). A SIGINT during the verify stage still unwinds to the terminal `E_INTERRUPTED` (exit 130) envelope. - `update --check` (and the `doctor` / cached update notices) now report the **real `install_method`** instead of a hardcoded `github-binary`: a binary running from an npm `node_modules` tree reports `npm` and carries a `manager_command` (`npm install -g @fateforge/wechat-mp-cli@latest`), and an explicit `WECHAT_MP_CLI_INSTALL_METHOD` override wins. This stops an agent from being told to self-update a binary its package manager owns. ## [1.0.7] - 2026-06-22 ### Added - The update-available notice now also rides along on **every command's `meta.notices`** (CLI-SPEC §3/§14), read **only from the local cache** with zero network I/O. The cache is refreshed by the active-check commands (`update --check`, and a best-effort short-timeout check in `doctor`); business commands surface the cached notice without phoning home. `meta.notices` is omitted when the cache has nothing to report (or its entry is past TTL). - Update-notice **severity grading**: severity is computed at check time from the embedded CHANGELOG delta between the running version and the latest and stored in the cache. It is `warning` when the delta contains a `security` entry OR the latest crosses a major version, otherwise `info` (`critical` is reserved and not emitted). The graded notice appears both in the active-check command `data.notices` and, read-only from cache, in any command's `meta.notices`. ## [1.0.6] - 2026-06-21 ### Changed - `update` is now a **single command with no confirm token**: a bare `wechat-mp-cli update` performs the whole self-update in one call (resolve latest or `--target-version` → verify signature → verify checksum → replace binary → sync Skill). Self-update is exempt from the `--dry-run` → `--confirm ` write gate; the safety guarantee is the in-process Sigstore verification, not an agent's review of a preview. `--check` and `--dry-run` remain optional read-only flags, and `--dry-run` no longer issues a `confirm_token` / `expires_at`. `update` is idempotent: already-latest returns `ok` with a no-op result. (Other data-write commands keep their dry-run → confirm flow unchanged.) ### Added - Staged update failure & interruption envelope: every `update` failure carries `stage` (`discover|download|verify_signature|verify_checksum|replace|skill_sync`), `current_version`, `binary_replaced`, and `skill_sync_status` so an agent always knows its post-failure state. A SIGINT/SIGTERM during `update` still emits a terminal JSON envelope (`E_INTERRUPTED`, exit 130), cleans the temp dir, and states the true post-state. - Error codes `E_IO` (exit 1) and `E_INTERRUPTED` (exit 130). ### Fixed - `update` replace-stage local failures (temp dir, extract, file write/rename, disk) now classify as `E_IO` (exit 1), and permission failures as `E_FORBIDDEN` (exit 4), instead of being misreported as a retryable `E_NETWORK`. A Skill-sync failure *after* a successful binary swap is now reported as **partial success** (`ok:false`, `binary_replaced:true`, retryable, with `skill_sync_command`) instead of a hard `E_NETWORK` that lost the fact the binary already updated. Integrity failures remain non-retryable `E_INTEGRITY` (unchanged). ## [1.0.5] - 2026-06-16 ### Fixed - npm `optionalDependencies` platform-package pins now match the package version. The previous release bumped the top-level version but left the pins at the prior version, so `npm install` resolved a stale platform binary (the new wrapper with the old binary). The publish workflow now rewrites `optionalDependencies` from the package version before `npm publish`, so the pins can no longer drift from the single source of truth. ## [1.0.4] - 2026-06-16 ### Changed - `update` is rewritten from package-manager delegation (npm/go install) to a self-contained verified binary self-update: download the release archive + `checksums.txt` + Sigstore bundle, verify the signature **in-process** (embedded `sigstore-go`, embedded TUF root) against this repo's tagged release-workflow identity, verify the archive SHA256, and replace the running binary — no dependency on npm/go/pip being installed. Releases are signed with `cosign sign-blob --new-bundle-format`. ### Security - Verification is mandatory and fail-closed (no skip path); release-integrity failures return the non-retryable `E_INTEGRITY` code (exit 1) instead of a retryable network code. ## [1.0.3] - 2026-06-15 ### Added - Batch operations (CLI-SPEC §15): `message mass sendall`/`send`/`preview`/`get`/`delete` broadcast a message to all followers, a tag, or an explicit openid list (≤10000, one async job) — all gated by `--dry-run` → `--confirm` plus `--dangerous` for the critical/high writes; poll delivery with `message mass get --msg-id`. `user info-batch --openids` fetches up to 100 follower profiles per call, auto-chunking longer lists, and returns one aggregated `items[]` (keyed by openid `target`) plus a `summary{total,succeeded,failed}` so a per-item failure never fails the whole command (`--continue-on-error`, default `true`). ### Changed - npm scope 迁移 `@fatecannotbealtered-` → `@fateforge`(无横线 org 在 npm 被占,迁移到 `@fateforge`)。主包与各平台包(`@fateforge/wechat-mp-cli--`)一并改名;GitHub org / go module path(`github.com/fatecannotbealtered/...`)与 release 源保持不变。 ### Fixed - `message mass sendall`: enforce `--to-all` and `--tag-id` as mutually exclusive. Previously giving both let `--to-all` silently win; now the audience guard requires exactly one and returns `E_VALIDATION` when both are set. ## [1.0.2] - 2026-06-14 ### Added - `qrcode create` (ticket + showqrcode URL); `user info` / `user list`; `tag` CRUD + `tag members` + batch `tag tagging`/`untagging`; `menu addconditional` (personalized menus). - `reference` now exposes a real per-command `output_schema` + `examples[]`, guarded against regression. ### Changed - Confirm tokens are now single-use (E_CONFLICT on replay) for write commands. ## [1.0.1] - 2026-06-14 ### Added - Expanded WeChat `errcode` mapping so agents get an accurate `error.code` + `retryable`: `40164` (IP not in allowlist) → `E_FORBIDDEN` with an actionable hint pointing at `remote ssh-command` / `setup proxy set` (the recovery path the proxy feature exists for); `42001` (access_token expired) → `E_AUTH` retryable; `40007`/`41030` (bad media_id / invalid menu) → `E_NOT_FOUND`. ### Fixed - `changelog` now reads CHANGELOG.md embedded into the binary (`go:embed`), so it returns real content regardless of the working directory the agent runs the binary from; previously it read `./CHANGELOG.md` from the CWD and fell back to a stub everywhere else. ## [1.0.0] - 2026-06-14 First stable release: recorded live smoke against the real WeChat Official Account API (`docs/LIVE-SMOKE-EVIDENCE.md`); `release_readiness` is `stable` with `live_smoke_status: verified`. ### Added - Recorded live smoke against the real WeChat API (2026-06-14): `stable_token` lifecycle, asset/draft/menu reads, the `--dangerous` T2 write gate, and a real temporary-material upload through the dry-run/confirm chain. - Initial Go implementation for the AI-native WeChat Official Account CLI. - Self-description commands: `context`, `doctor`, `reference`, `changelog`, and `update --check`. - Local account configuration with encrypted AppSecret storage and environment variable overrides. - HMAC-backed `--dry-run` to `--confirm ` flow for write commands. - WeChat API client for token refresh, image upload, draft add/batchget/get/delete, publish submit, and publish status. - Goldmark GFM Markdown renderer with frontmatter metadata support. - Local image inspection, conversion, and compression before WeChat uploads. - Automatic local inline image upload and `` replacement during draft creation. - Draft update and draft count commands. - Published article lifecycle commands: list, get article, and delete. - Comment management commands: open, close, list, mark/unmark featured, delete, reply add, and reply delete. - Article and user analytics commands backed by WeChat datacube endpoints. - Published-content analytics commands for WeChat's `getarticleread`, `getarticleshare`, `getbizsummary`, and `getarticletotaldetail` endpoints. - API proxy support through `WECHAT_MP_CLI_API_PROXY` and `setup proxy`. - `remote ssh-command` helper for SSH SOCKS egress through an allowlisted server. - Permanent material commands: `asset count/list/get/delete`. - Temporary media commands: `asset temp upload/get/get-hd-voice`. - Draft/publish switch commands: `draft switch status/enable`. - Custom menu commands: `menu get/set/delete`. - Webhook verification command: `webhook verify`. - Official endpoint coverage documentation under `docs/`. - MVP command groups: `setup account`, `setup proxy`, `remote`, `token`, `render`, `image`, `asset`, `draft`, `publish`, `menu`, and `webhook`. - Package-level tests for confirmation tokens, config loading, rendering, API client behavior, and reference metadata. ### Changed ### Fixed ### Deprecated ### Removed ### Security - Write commands require operation-bound confirmation tokens. - Token refresh output redacts access token values by default. - Public publication, material deletion, menu changes, credential changes, and proxy changes are modeled as confirmed writes.