Skip to content

ReachWeaver 0.16.0 release notes

Released on 2026-10-22.

Much easier to use, measured. reachweaver studio opens one browser app where you write a post (your networks preselected), copy it for each network, mark it as posted with its numbers and see how it did. In the terminal, posted LINK --likes N records a post you made by hand in one step, next answers “what should I post next?” with your ideas by name, report --week gives a short summary instead of JSON, and every error says what to do. Measured against 0.15.0 with the same journeys: failed commands, dead ends, errors without a next step, hand-edited files and web steps that needed the terminal all went to zero; phone tap targets under 44 px went from 71 to 2; journey friction is about 3x lower. 133 commands (4 more than 0.15.0, none removed). No live adapter is added.

  • Composer numbers and link accounts (owner-approved write path, 2026-10-21): /api/mark-posted takes optional numbers (only likes/replies/reposts/views, each an exact JSON integer 0..10^9 or null; strings, decimals, booleans and other keys are refused before any write) and stores them with storage.store_metrics (source entered-by-hand, observed now) for the post it just created. New /api/numbers adds a snapshot only for the post named by that package network’s POSTED.json, and only when the database row is published_manual with the same platform and link (a draft, another post or a tampered record is refused, nothing written). A chosen account that the link contradicts is refused before the database is opened. New read-only /api/link-account returns which configured account a link names. All new routes sit behind the same Host/token/Origin/size/depth guards and the server write lock; nothing new can approve, schedule or send, and the dashboard stays GET/HEAD-only. Tests: tests/test_composer_ux3.py.
  • Composer: the optional account_id on /api/mark-posted is checked by the server (id pattern, present in accounts.json, same history platform) before the database is opened, and passed to record_manual_publish; nothing new can approve, schedule or send. New read-only /api/accounts route behind the same Host/token/Origin/size guards returns ids, platforms and public handles only (never env_prefix). Display names are stripped of control, format/bidi, surrogate and private-use characters, re-validated on every read, and shown with textContent.
  • posted is in mcp_server.NEVER_EXPOSED (a backstop; it is tagged writes, so it was never a tool): an MCP client never states on the owner’s behalf that a post was published or what its numbers are. It never writes the send queue, approves or contacts a network; its record is the existing never-sendable published_manual state.
  • next prints the owner’s idea text and post wording, so mcp-serve withholds it (mcp_server.WITHHELD_COMMANDS, a tightening). No approval, dry-run, pause, credential, reconciliation, Hacker News or Reddit rule changed; nothing new writes to the database or the send queue.
  • studio links the dashboard and the composer without merging them: two loopback servers, two per-run tokens, every guard unchanged (the dashboard stays GET/HEAD-only and read-only; the composer keeps its Host/Origin/token/size checks). Each learns the other’s URL only from a token-gated JSON answer in the same process (api/studio, /api/catalog), never from static HTML; both send Referrer-Policy: no-referrer, links are rel="noopener noreferrer", CSPs are unchanged. The new composer route /api/idea only reads one open idea. studio is added to mcp_server.NEVER_EXPOSED (a tightening). Details: docs/ARCHITECTURE.md “Studio”.
  • posted corrections (storage.correct_manual_publish) write only a published_manual row (the UPDATE itself is guarded by status='published_manual') and its linked mark-shared entry: send_started_at, content, account_id and the derived remote_id. Status and approval columns are never written, so the record stays outside every send path; a post ReachWeaver sent, and every other status, is refused. Nothing writes the send queue; no approval, dry-run, pause, credential, reconciliation, Hacker News or Reddit rule changed.
  • Composer: numbers in the browser. Mark as posted has optional fields Likes, Replies, Reposts, Views — optional, as shown in the app, and every network marked as posted gets Update numbers (a new hand-entered snapshot, as of now). After recording with numbers the page offers See it in your results (under studio; alone, the reachweaver ui command); without numbers it points to Update numbers or reachweaver posted LINK --likes N instead of the old metrics POST_ID command.
  • Composer: “Your networks”. The networks of your own accounts (accounts.json, example placeholders left out) are listed first and preselected on a fresh draft (you can remove any; the draft remembers the choice). Writing for your usual three networks is caption + Preview + 3 Copy = 5 clicks (was 9).
  • Composer: the account a link names. Typing a post link picks the account it names in Which account?; with “Not sure / skip” the server still uses that account, the same way reachweaver posted resolves it, and says so (“Recorded … for work-bluesky (from the link)”). The account list is read again when the page comes back into view and before recording.
  • account-remove ID (new command, offline): removes one entry from config/accounts.json (or --accounts) after a [y/N] confirmation in a terminal, or --yes elsewhere; --dry-run writes nothing. The previous file is kept as accounts.json.bak. It never touches credentials, the database, approvals, the approval ledger or disconnect state; posts recorded for the account stay in the database, are counted read-only and reported. An unknown id is refused with the list of ids. Catalog: writes. Tests: tests/test_account_add.py.
  • --version global flag: prints exactly what reachweaver version prints.
  • workspace --init DIR --with-examples seeds the four example placeholder accounts (the earlier default; see Changed).
  • “link in bio” check in share. Where a link in the post itself works, the package warns and says why: Bluesky (rich-text links, https://docs.bsky.app/docs/about-bluesky-content/post-richtext, quote “Posts in Bluesky use rich text to handle links”), Telegram (https://core.telegram.org/api/entities, “These entities turn certain sections of the message blue and make them clickable”) and Mastodon (https://docs.joinmastodon.org/user/posting/, “If you include links in your post, they must begin with http:// or https://”), all checked 2026-10-21. LinkedIn’s note says clickability is unverified. The text is never changed.
  • One labelled tip per network in share’s OPEN-FIRST.md, each restating a fact ReachWeaver already holds with a source (manual_export.FACTS, the capability registry, the draft/check limits); a suggestion, not a promise of reach.
  • report at a terminal prints a short summary (“This week (since 2026-10-14): 3 posts · 120 likes · 9 replies · best: … on Instagram (95 likes, 7 replies)”), a table per network, the top 5 posts by interactions with their first words, posts with no numbers or due for their 7-day numbers, and the early-results note. --format text asks for it explicitly; --week is --since 7d. Unknown counts print as -, never 0.
  • show POST_ID lists the post’s latest recorded numbers per source, with when they were observed and how many hours after posting (results in the JSON for published and posted-by-hand posts), or the exact metrics command when none are recorded.
  • Human times when recording (reachweaver/when.py, one parser): metrics --observed-at/--window-start/--window-end and mark-shared --at accept now, today, yesterday, YYYY-MM-DD (midday local, never in the future), 'YYYY-MM-DD HH:MM' (the computer’s time zone, its daylight-saving rules for that date) and ISO 8601 as before. Stored times are unchanged: ISO 8601 UTC with offset. import-metrics files still need an explicit offset. Tests: tests/test_when.py.
  • reachweaver posted URL [--likes N --replies N --reposts N --views N] [--text "..."] [--at WHEN] [--account ID] [--platform P] (new everyday command, offline): record a post you made by hand and, optionally, its numbers in one command. The platform comes from the link’s host through an explicit map (bsky.app, instagram.com, linkedin.com, tiktok.com, youtube.com/youtu.be, threads.net/threads.com, x.com/twitter.com, facebook.com, t.me, dev.to, reddit.com, pinterest.com, hashnode.com/hashnode.dev, and the server of a configured Mastodon account); an unknown host asks for --platform with the list and a “Did you mean”. The account is the configured one whose handle the link names, else the only configured (non-placeholder) account of that network; several and no clue asks for --account with the ids; none records the post without an account and says so. It uses storage.record_manual_publish (the same never-sendable published_manual record as mark-shared --url) and storage.store_metrics (source entered-by-hand, observed now). A link that is already recorded (by hand or sent by ReachWeaver) gets the new numbers added instead of failing (“Updated numbers for … on Bluesky”). Metric values are checked before anything is written. --text is optional: without it the link identifies the post (record_manual_publish(link_is_enough=True)), reports name it by a short link, and the repeat guard has no material to compare (an empty text never matches). Recording the J3 week (“how did my posts do this week”, 3 posts with numbers) goes from 7 commands plus copying 3 post ids (mark-shared --platform P --text ... --url ... and metrics POST_ID ... per post, then report --week) to 4 (posted URL --likes N ... per post, then report --week). Catalog: writes. Docs: docs/MANUAL-EXPORT.md. Tests: tests/test_posted.py.
  • start without an accounts file but with posts or ideas already recorded shows your progress (next, the due metrics line, report --week, the composer) instead of only “No accounts file yet.” (state posting_by_hand); a brand-new user still starts with share --web --open.
  • reachweaver studio (new command): the read-only dashboard and the browser composer in one process, linked to each other. Write a new post (sidebar, a 44 px button at the top of the Overview on phones, the next-step card, the Write page and Ctrl K) opens the composer; the composer’s See how your posts did (header, footer and after Mark as posted) opens the dashboard’s Results; each open idea on the Ideas page gets Write a post from this idea. --open opens the dashboard, or the composer when there are no posts yet; an empty database is created when none exists (as init); Ctrl+C stops both. ui and share --web alone keep the copyable-command fallback. Catalog: writes, interactive. Docs: docs/DASHBOARD.md. Tests: tests/test_studio.py.
  • share --web --idea ID / --text T start the composer draft from an open idea (read-only) or your own text, through the draft store’s allowlisted fields. An unsaved draft is never replaced: the terminal says so and the page offers the text once.
  • Plain-language help for every readiness code on the dashboard (REASON_HELP), including linkedin_version_unsupported (“LinkedIn API version too old (only for automatic sending)” with the fix) and the approval-ledger codes; tests/test_reason_help.py enumerates the codes health can emit and fails on one without help.
  • reachweaver next (new everyday command, read-only): “What should I post next?” At most five ranked, concrete steps in plain language, each with one command: safety issues first, drafts waiting (review), posts due for their 7-day numbers (the exact metrics POST_ID --likes N --replies N --reposts N line), each open idea by name with share --web --open --idea ID (the browser composer with that idea as the caption; see Changed), days since you last posted per network, and the top “what works for you” line. --json (schema reachweaver.next.v1). Reads the ideas inbox and an existing database mode=ro; never creates the database, drafts, approves, schedules or sends. Docs: docs/NEXT-ACTIONS.md. Tests: tests/test_whats_next.py.
  • “Did you mean” for mistyped input. An unknown option names the closest one of that command (unrecognized arguments: --platfrom bluesky. Did you mean --platform?, the error and its hint only, without the usage block) and says when a global option such as --db was put after the command; an invalid choice (account-add blusky) names the closest choice; an unknown network in share says Unknown network 'blusky'. Did you mean bluesky?; common words suggest the real commands (post/write/compose → share, draft; undo → cancel, unapprove; results/stats → report). Tests: tests/test_ux_errors.py.
  • posted LINK --at/--text/--account again corrects a post you recorded by hand (storage.correct_manual_publish, UX re-measure 2026-10-21, J6 “I posted yesterday; record it”): “Corrected: posted 2026-10-20 (was 2026-10-21)”, and the 7-day reminder follows the corrected date. Only a published_manual record is corrected: same row, status and empty approval columns unchanged (still never approved, scheduled, claimed or sent), the linked mark-shared entry gets the same values, and one manual_publish_corrected event records the old and new time and account (never the text). A post ReachWeaver sent (published) keeps its time, text and account; only numbers are added. A future time, or one after numbers already entered, is refused with nothing changed. Before, re-running said “–text, –at and –account were not changed” and there was no way to fix a wrong day. Tests: tests/test_ux3_cli.py.
  • posted asks when you posted it at a terminal without --at (When did you post it? [today] (e.g. yesterday, 2026-10-19), Enter = today, a wrong answer is asked again); a script still gets now and the output says posted today (2026-10-21) — if not, run again with --at yesterday.
  • reachweaver idea "text" is short for idea add "text" (when the word after idea is not one of its actions); idea add now ends with Saved. When you're ready: reachweaver next.
  • next offers a terminal alternative for each idea: Or in the terminal: reachweaver share --guide --text '<idea>' (key alternative in --json, additive). share --guide --text shows that caption and asks Keep this caption? [Y = keep / e = edit].
  • Bare reachweaver and start end with an “Everyday” footer (six lines): write (studio --open, or share --guide), record (posted LINK --likes N), results (report --week), ideas (idea "text"), next (next), with where --help and --help all are.
  • version --verbose prints the install kind and skill folder; --version and version print only reachweaver 0.15.0 (--json unchanged).
  • The dashboard names a post recorded from its link without text the way the terminal does (“Instagram post of 20 Oct”) instead of “(empty)”; the composer no longer asks for numbers it just kept; account-add asks each network for its own kind of username and points LinkedIn questions to the online guide; posted recognises mastodon.social links without a configured Mastodon account; start describes workspace --init correctly.
  • Composer: ideas, posted packages and theme (UX round 4, Playwright under studio at 1280 and 375 px, synthetic workspace). Write a post from this idea while a saved package is open and unchanged now starts a new post with the idea as the caption in 1 click (“your saved package … is still in Saved packages, unchanged”); before, nothing visible happened (the choice rendered at y≈1202/2656 px). With unsaved changes the choice Use it as my caption / Keep my draft appears at the top of the editor (sticky, scrolled into view, focus on it; 44 px buttons, were 32 px): 2 clicks. Update this package is disabled with a reason whenever a network in the open package is marked as posted, and “Yes, update it” is never offered then (the server’s 409 refusal is unchanged). Preview cards show “Changed, not saved” once the editor differs from the saved package instead of keeping “Posted by you”. The media label and file field are one 44 px+ tap area. The dashboard’s Settings theme reaches the composer through its links (&theme=… in the fragment; the composer keeps it in its own browser storage). New read-only package_unchanged on /api/draft/load (share_composer_store.draft_matches_package); nothing new writes, approves, schedules or sends. Tests: tests/test_composer_ux4.py.
  • UX round 3 (measured with Playwright in Chrome under studio on a synthetic workspace, 1280 and 375 px; clicks before → after): compose from the dashboard 1 → 1, weekly results 1 → 1, what next via ideas 2 → 2, compose and copy for the 3 configured networks 9 → 5, mark as posted with the right account and numbers 4 → 3; no horizontal overflow, no console errors, no target under 44 px at 375. Duplicate as new names the new package in its message, adds “ (copy)” to the copy’s name and labels it New copy. Ideas are named by their first words (about 60 characters), never by id, on the page and in the terminal. On phones the composer’s brand bar is sticky with a Results link (under studio) and the tagline wraps instead of being cut off. Dashboard Overview: one write action only (the card’s Or write a new post next to a terminal-only step such as waiting drafts, which stays terminal-only; the top button otherwise; “Also” no longer repeats it); the ideas hint reads “Pick one and write it in the composer.” Skip links (focused), the sidebar’s Write a new post and the composer’s post links on phones are at least 44 px tall.
  • start now offers reachweaver studio --open first (write a post and see how it did, in one browser app), and studio replaces ui in the everyday --help list (ui and share --web still open one app each). The composer and dashboard hints for recording a post made by hand now use reachweaver posted LINK --likes N.
  • share prepares what it can. When some chosen networks are blocked and others are fine, the fine ones are written and each skipped one is listed with its fix (YouTube skipped: add --title "…" and run again with --platform youtube). Exit codes: 0 all prepared, 1 some skipped, 2 blocked (nothing written). An error that belongs to no single network, or every network blocked, still blocks everything; new --strict keeps the old all-or-nothing behaviour. Docs: docs/EASY-SHARING.md.
  • share --guide takes a caption of several lines: type at the > prompt and finish with an empty line or Ctrl-D (an empty first line asks again).
  • share --attach errors say what to do: Attachment not found: nothere.mp4 (looked in /abs/folder). Check the name or leave it out. (exit 2), with separate plain messages for a folder and a symbolic link.
  • connect before any account exists is a status, not an error: No accounts yet, so there is nothing to connect. plus the next steps (account-add bluesky, the other networks, share --guide for posting by hand) and exit 0 (--json: "accounts": []). In a terminal it asks Add one now? Which network? and runs the same account-add flow in-process (public details only, same checks and [Y/n]); connect --guide then continues with that account’s steps. A broken accounts file still stops. The guide’s “More detail” and failure hints link the online docs (https://github.com/MSKazemi/reachweaver/blob/main/docs/...) instead of a docs/ path that a workspace folder does not have, and its “Next” draft line uses --text instead of the repository’s examples/ file. The hidden-input read -r -s -p credential lines are unchanged.
  • account-add asks again on a blank answer in a terminal (with an example; three blanks stop, writing nothing). Without a terminal the message is plain: Bluesky needs your handle (like name.bsky.social). Add --handle name.bsky.social.; those example shapes (and yourname, your_bot, @yourchannel…) are refused like the example file’s placeholders if copied as they are.
  • The composer and the dashboard suggest the short reachweaver metrics POST_ID --likes N --replies N (the time defaults to now and the source to entered-by-hand), and the dashboard’s “no accounts” texts point to reachweaver account-add instead of copying an example file. share --web now honours the global --accounts option.
  • Browser composer (share --web) is easier to use, especially on phones: title “ReachWeaver — Write a post”; the header shrinks to one line under 820 px so the caption box is visible on first load (375×667: caption top at 372 px, was 783 px); the six static step chips are replaced by a Write → Choose → Preview → Save → Post progress bar that follows the real editor state (aria-current="step") with a “Next:” hint; dark mode by prefers-color-scheme; the dashboard’s colours, font, radius and “RW” mark; a focus ring of 6.1:1 (light) and 8.3:1 (dark) on panels, was 2.6:1; a screen-reader status region for Copied/Saved/Recorded; 44 px targets under 640 px (375 px with one saved package: 0 of 34 smaller, was 15 of 31). Saved packages get a readable display name from the caption and time (in PACKAGE.json; the folder name is unchanged), and the save message reads “Saved: (in state/shares/…)” with a Copy folder path button. “Exported” reads “Ready to post”; technical warnings and errors are reworded in plain language with the same meaning. The header and footer show “See how your posts did: run reachweaver ui” with a Copy button. docs/BROWSER-SHARE-COMPOSER.md.
  • Composer “Mark as posted” checks the link in the page first (adds a missing https://, plain error “Paste the full link to your post, starting with https://”), offers an optional account choice from accounts.json for that network, and ends with the next step: the exact reachweaver metrics POST_ID --observed-at TIME --source native-export --likes N --replies N command with a Copy button.
  • Errors say what to do next. A missing accounts file now reads Missing config/accounts.json: no accounts yet. Add one: reachweaver account-add bluesky (public details only), or set up a folder: reachweaver workspace --init . (the example file is mentioned only when it sits next to the missing file); connect with no accounts prints next steps instead (see above). A file named on the command line that cannot be opened gives File not found: nope.txt (looked in /abs/folder). Check the name, or use --text "..." instead. (or a plain line for a folder or a permission problem) instead of [Errno 2] ..., still exit 2 and still not_found/permission in --json errors. share with YouTube and no title says YouTube needs a video title: add --title "…". The terminal-only Next: hints cover these cases too.
  • share --guide: Save these files? [Y/n] (Enter saves; saving only writes private local files), a new Attach a photo or video? (path, or Enter to skip) question passed as --attach (a missing path is asked again), and an empty or unknown network answer is asked again with the list instead of stopping.
  • Caption fitting in share cuts at the end of the last sentence that fits when that keeps at least half of what a word cut keeps, else at a word with “…”. A closing link (the last word, or a short last line such as Read more: https://...) is kept and the text before it is shortened. The SHORTENED note names the removed passage (its start and end), every removed link (LINK REMOVED:) and cut hashtags. fit() returns cut_at, kept_closing, removed_text and removed_links.
  • Plain-language checklists (share, manual-export, generic manual packages): Status: Not posted yet. Nothing was sent, uploaded or scheduled.; “Why you post this yourself” says ReachWeaver cannot post to Instagram; you post it yourself in the app. (or that a Bluesky/Mastodon/Telegram/LinkedIn package is copy-only, or that a venue is human-only) before the registry details; a needed photo or video reads Instagram needs a photo or video: attach it in the app. ReachWeaver has not checked the file ...; the last step says to keep the post’s link as the proof the post exists. manual-export.json keeps status: manual_export_not_published and api_capability.reason unchanged; the golden fixtures were regenerated for the wording. The Bluesky/Mastodon/Telegram/LinkedIn checklist’s text-limit line now shows the draft/check rule instead of “limit unknown”.
  • account-add shows the entry as plain lines (Network, handle or other public identity, Account name, Credential prefix) instead of raw JSON, its terminal confirmation is [Y/n] (a local write, kept as .bak), and it ends with the exact next command.
  • workspace --init no longer seeds four example accounts. config/accounts.json starts as an empty list; in a terminal it asks Which networks do you use? (e.g. bluesky,instagram — Enter to skip) and adds one placeholder per named network, then prints the account-add command for each. --with-examples keeps the old behaviour. The Python function workspace.init_workspace(root) without networks still copies the example file.
  • Dashboard (reachweaver ui) for creators who post by hand; still GET-only and read-only. The Overview leads with one “Your next step” card (the start guide’s first step with a Copy button, or “Write a new post” with share --web --open when nothing is waiting, plus at most three others) instead of three lists of the same drafts; a yellow or red banner appears only for a real problem (emergency stop, uncertain or unfinished sends, anything blocked) and is otherwise one calm line (“Automatic sending isn’t set up — you don’t need it to write or post by hand.”); readiness reasons, accounts, the folder check and “Sends that failed” (was “Dead letters”) are folded into “Setup and safety details”. Results gains “This week / 30 days / All” (?since=7d|30d|all), a summary line of recorded exact values and the top three posts, and sits in the phone’s bottom bar (Schedule moves under More when nothing is scheduled). Search lists a post you recorded with mark-shared --url once, with the time you posted it. Plain language throughout (“posted by you”, “Not connected (only needed for automatic sending)”, timeline events as sentences, the post’s first words as its heading, handles as typed); a post you posted yourself belongs to Posted and is not offered “Voice-check and tighten this draft”. New “Write a new post” page. Phone fixes: no page wider than a 375 px screen (long labels in owner cards made two pages 62 to 92 px too wide), 44 px touch targets, 44 x 44 menu button; card titles are headings, Esc in Ctrl K returns focus, example dates are computed in the browser. Brand tokens (ui_assets.BRAND, --rw-* CSS properties) for other local pages to share. New tests/test_ui_narrow.py checks every page at 375 and 390 px in headless Chrome when one is installed.
  • metrics defaults --observed-at to now and --source to entered-by-hand (you typed the numbers from the platform’s own insights; never presented as an API or export source). Explicit values work as before. It then prints what was stored and, for a post younger than 6 days, when to record again for the 7-day comparison.
  • report without --format: text at a terminal, JSON when piped or redirected (unchanged for scripts).
  • mark-shared --url prints one id, the measurable post id, with its exact metrics line; without --url it adds “Not counted in reports. Add –url to measure it.”
  • insights and the report say plainly when results exist but none at the matched age (“You recorded results N hours after posting; insights compares results about 7 days after posting. Record again around DATE or use –age-hours N”; JSON cohorts[].results_at_other_ages), and a cohort without an account is “posted by you (no account)” instead of “account unknown”.
  • Bare suggest prints the same view as next (its --json keeps the suggestions schema, counts only); suggest --include-db now includes the open-ideas signal, which points at next.
  • Recording a post made by hand is easy to find. --help everyday list: posted replaces check (still 16 commands; review already shows each draft’s checks before you decide, and check stays in --help all). An empty report (also --week), an empty next, an empty list at a terminal and the missing-database note say “Posted something yourself? Record it from its link: reachweaver posted LINK –likes N”; start shows it once anything was posted (and in the by-hand path without an accounts file); share ends with “After you post, record it: reachweaver posted LINK –likes N” and OPEN-FIRST.md says the same (was a pointer to mark-shared).
  • Bare metrics (no post id) explains instead of the following arguments are required: post_id: give a by-hand post’s link to reachweaver posted LINK --likes N, or find an id with reachweaver list. Nothing is created.
  • Reports name a post recorded without an account “posted by you (no account)” (was “(no account recorded)”); the terminal report shows a configured account by its public @handle (example placeholders excluded). list at a terminal shows “—” in the ACCOUNT column for such a post; piped and --plain output is unchanged for scripts.
  • --help everyday list: next replaces suggest (bare suggest now prints the same view, so the list would show one answer twice; suggest stays in --help all). 132 commands with account-remove and posted.
  • next: each open idea’s step is now reachweaver share --web --open --idea ID (the composer with that idea as the caption; the id, never the text, is in the command) instead of share --platform ... --text '...'.
  • Composer: when exactly one configured account matches a network, Mark as posted preselects it (still changeable; “Not sure / skip” stays); “Your post changed. Preview again” now appears only after a real change since the last preview or save, not after a reload; the chosen networks show as removable chips above the list, and on phones the list flows in the page instead of a small inner scroll box.
  • Dashboard Overview: waiting drafts are shown once (the next-step card, plus the count in the All posts bar); the lead line and the extra “posts are waiting” link no longer repeat them.
  • account-add linkedin warns (it does not refuse) when linkedin_version is ageing or 12 months old or more, using the same check that makes health report linkedin_version_unsupported.
  • Plain words in share (UX re-measure 2026-10-21, J2/J4/J7): the guide’s YouTube title prompt says (required for YouTube; Enter skips YouTube), asks once more on an empty answer, and a skip reads YouTube skipped (no title). Run reachweaver share --guide again and choose only YouTube to add it. (no flag syntax inside the guide). Media-check lines list only problems found (and sourced advice), else no problem found, plus one line some details could not be checked from the file header (that's normal) instead of the checker’s field names (moov_before_mdat, subtitle_formats) and size unknown, duration unknown; the full list stays in manual-export.json. Length counters read 56 of 2200 characters allowed instead of 56/2200 UTF-16 units, and 56 of 5000 bytes allowed where the sourced YouTube rule is in bytes. The web composer shows the same media lines.
  • An unknown option prints only the error and its hint (one line), not the command’s usage block first.
  • An empty list says No posts yet. Write one: reachweaver studio --open (or reachweaver share --guide) (stderr when piped; stdout stays empty for scripts).
  • A post recorded without text is named by network and day in report and next (Instagram post of 18 Oct; the year is added when it is not this year) instead of a link fragment such as instagram.com/…/DemoPost123.
  • The idea help line reads Save a post idea for later: reachweaver idea "text"; list, drop or promote it.
  • share shows the final text in the terminal after the review: per network a header with its length against that network’s limit in plain words (Instagram (photo post): 72 of 2200 characters allowed, Bluesky: 72 of 300 characters allowed; YouTube’s sourced description rule is in bytes, so 72 of 5000 bytes allowed; TikTok’s UTF-16 rule reads as characters and says when an emoji counts as 2) and the text, at most three lines or about 280 characters each with … full text in instagram/caption.txt, 40 lines in all. When every copy is the same it says so once (Same text for all 3: it fits every network, so nothing was changed) instead of implying per-network rewrites; no rewrite is made without a sourced rule (hashtags still never move; Instagram’s existing caption-link note is unchanged).
  • Terminal UX round 3 fixes (strict re-measure 2026-10-21). (1) A suggested next step repeats the global options you gave (home.command): --accounts myaccounts.json draft ... now says Add one: reachweaver --accounts myaccounts.json account-add bluesky, and account-add, connect, start, workspace and the error hint carry --accounts/--db/--home/--profile the same way, so following them no longer writes config/accounts.json and loops (the workspace alternative is left out when you named a file). (2) posted and metrics accept --comments as --replies; “Did you mean” tries the apps’ words first (--comments → --replies, --shares/--retweets/--boosts → --reposts, --impressions → --views where the command has it) before spelling (was Did you mean --account?). (3) share --attach: findings about the file itself, findings every chosen network shares word for word and the header-check note print once under Attached media: before the networks, not once per network (each CHECKLIST.md keeps them). (4) Plain words: Check what will be created: (was Review the handoff plan:), Copy and post it yourself (text) (was MANUAL COPY; format text), Prepared 1 private package to copy from (was 1 private manual packages), account-add ends with After that, write your first post: reachweaver draft --account ID --text '...' (was Then draft and review as usual.), connect with no accounts says reachweaver account-add mastodon (or telegram, linkedin). (5) The terminal report drops post ids from the top-posts list and says Record numbers again around DATE to compare after 7 days: reachweaver posted LINK --likes N (or the metrics form for posts ReachWeaver sent) instead of insights --age-hours. (6) show on a post made by hand prints Posted by you on DATE · LINK, its text or (text not recorded — add it with reachweaver posted LINK --text "..."), its latest numbers with a posted LINK line to update them, and no length meter or approval section (--plain/piped JSON unchanged). (7) draft at a terminal adds Next: reachweaver review (go through your drafts) or reachweaver show ID (scripts keep the one line). (8) Wording only, credential handling unchanged: connect --guide says in your own terminal window (not inside an AI chat) and that the variables last only until that window closes (with the ACCOUNT-ONBOARDING.md link); account-add says Set these yourself with reachweaver connect --guide (it shows the exact lines); the read -r -s lines are byte-identical.