Commit 644d451
authored
feat(twitter): add unlike + retweet + unretweet + quote (write-action symmetry P0) (#1400)
Round 21 P0 — Twitter write-action symmetry (4 of 4: unlike, retweet, unretweet, quote).
## Scope
Closes write-action gap with existing siblings (`like`, `bookmark`, `unbookmark`, `delete`):
- `unlike` (UI strategy, navigateBefore:true)
- `retweet` (UI strategy)
- `unretweet` (UI strategy)
- `quote` (UI strategy, `/compose/post?url=` route — same family as `reply.js` `/compose/post?in_reply_to=`)
+745/-0 in initial commit, plus 3 progressive review fixes. Final: 4 adapters + 4 tests; modified `shared.js`, `shared.test.js`, manifest, docs.
## Iteration history (4 heads, 102/102 tests on final)
- `07836783` — initial 4 adapters + 4 tests, 96/96
- `55a89776` — fix #1: shared `parseTweetUrl()` URL invariant + quote post-submit verify (102/102)
- `dc9eab66` — fix #2: article-scoping for unlike/retweet/unretweet (delete.js sibling pattern)
- `8809d2c1` — fix #3: exact status-id matching (`match?.[1] === tweetId`) + quote-card exact id guard
## 4 progressive blockers caught (codex-mini0 lead + F-P-0 aux)
1. **URL validation (silent-clamp class)**: original passed any host containing `/status/<id>`. Fixed: `parseTweetUrl()` requires `https` + Twitter/X exact host + exact `/<user|i>/status/<id>` path; host-suffix, embedded URL, path-suffix all `ArgumentError` pre-nav.
2. **Quote silent-success illusion**: original click-implies-success without composer/toast verify. Fixed: pre-submit quoted-card exact id render assertion + post-submit success toast OR composer-clear assertion, otherwise return failed row.
3. **Broad querySelector scoping (delete.js sibling pattern)**: original state probe + click + post-click verify on conversation pages picked first matching button. Fixed: scope to `article` containing requested exact status id (sibling `clis/twitter/delete.js:22-23` pattern).
4. **Substring vs exact status-id matching**: `/status/123` substring-matched `/status/1234`. Fixed: regex `/\/status\/${id}(?:\/|$)/` segment-edge anchor + `match?.[1] === tweetId` exact compare.
## Cultural sediment (Round 21)
**Audit checklist 5 rules (pre-write upstream selection net)**:
1. cross-grep sibling URL-construction patterns before adopting
2. silent-clamp class detection (any normalize-then-trust path)
3. broad querySelector → article-scoping requirement
4. missing-validation early reject before navigation/IO
5. ID-based DOM/URL matching exact-not-substring
**Augment framing**: Round 21 audit-first 是 Round 18 字面量 self-check 的 **upstream pre-write 阶段**, 两者作用阶段不同, 共存比替换稳。
**Meta-anchor "Structural exactness for identity matching"** unifying:
- URL layer (#1391 isFacebookAuthRedirectPath: `\.php` + `(/|$)` segment edge)
- URL parser layer (#1392 parseGrokSessionId: bare UUID exact / URL host-exact-or-subdomain + path-exact)
- DOM layer (#1400 article-scoping: status-id `/\/status\/${id}(?:\/|$)/` regex or pathname segment-array exact compare)
Common invariant: boundary-lock structural shape, 不 trust substring 模糊 — fuzzy match 是 silent failure 温床。
## Validation gates (final head `8809d2c1`)
Local: Twitter tests 102/102, `node --check` touched files, `npx tsc --noEmit`, `npm run build`, typed-error-lint 189/189, silent-column-drop 103/103, doc-coverage 140/140, docs:build clean, listing-id advisory unchanged 13, `git diff --check` clean, merge-tree clean.
GitHub: build×3 (ubuntu/macos/windows) SUCCESS, unit-test×2 shards SUCCESS, bun-test SUCCESS, adapter-test SUCCESS, audit SUCCESS, doc-coverage SUCCESS, docs-build SUCCESS, smoke-test skipped, PR `CLEAN/MERGEABLE`.
## Strategy/UI boundary (better-solution verdict)
UI write path acceptable for P0 symmetry (matches existing Twitter write siblings). GraphQL write migration + structured `idempotent:true` flag are cross-sibling upgrades, P5 candidate, not P0 blockers.
Round 17 race-mitigation 第 8 连续 race-free execution (this round absorbed author scope-uncertainty hold-then-retract event without producing actual race).
Reviewers:
- Lead: @codex-mini0 (4-round iteration, all blockers caught)
- Aux: @First-principles-0 (better-solution triangulation, scope-discipline verdict, regression invariants)
- Author: @opencli-user1 parent 8d201ae commit 644d451
12 files changed
Lines changed: 929 additions & 1 deletion
File tree
- clis/twitter
- docs/adapters/browser
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22760 | 22760 | | |
22761 | 22761 | | |
22762 | 22762 | | |
| 22763 | + | |
| 22764 | + | |
| 22765 | + | |
| 22766 | + | |
| 22767 | + | |
| 22768 | + | |
| 22769 | + | |
| 22770 | + | |
| 22771 | + | |
| 22772 | + | |
| 22773 | + | |
| 22774 | + | |
| 22775 | + | |
| 22776 | + | |
| 22777 | + | |
| 22778 | + | |
| 22779 | + | |
| 22780 | + | |
| 22781 | + | |
| 22782 | + | |
| 22783 | + | |
| 22784 | + | |
| 22785 | + | |
| 22786 | + | |
| 22787 | + | |
| 22788 | + | |
| 22789 | + | |
| 22790 | + | |
| 22791 | + | |
| 22792 | + | |
| 22793 | + | |
| 22794 | + | |
| 22795 | + | |
| 22796 | + | |
22763 | 22797 | | |
22764 | 22798 | | |
22765 | 22799 | | |
| |||
22855 | 22889 | | |
22856 | 22890 | | |
22857 | 22891 | | |
| 22892 | + | |
| 22893 | + | |
| 22894 | + | |
| 22895 | + | |
| 22896 | + | |
| 22897 | + | |
| 22898 | + | |
| 22899 | + | |
| 22900 | + | |
| 22901 | + | |
| 22902 | + | |
| 22903 | + | |
| 22904 | + | |
| 22905 | + | |
| 22906 | + | |
| 22907 | + | |
| 22908 | + | |
| 22909 | + | |
| 22910 | + | |
| 22911 | + | |
| 22912 | + | |
| 22913 | + | |
| 22914 | + | |
| 22915 | + | |
| 22916 | + | |
| 22917 | + | |
22858 | 22918 | | |
22859 | 22919 | | |
22860 | 22920 | | |
| |||
23139 | 23199 | | |
23140 | 23200 | | |
23141 | 23201 | | |
| 23202 | + | |
| 23203 | + | |
| 23204 | + | |
| 23205 | + | |
| 23206 | + | |
| 23207 | + | |
| 23208 | + | |
| 23209 | + | |
| 23210 | + | |
| 23211 | + | |
| 23212 | + | |
| 23213 | + | |
| 23214 | + | |
| 23215 | + | |
| 23216 | + | |
| 23217 | + | |
| 23218 | + | |
| 23219 | + | |
| 23220 | + | |
| 23221 | + | |
| 23222 | + | |
| 23223 | + | |
| 23224 | + | |
| 23225 | + | |
| 23226 | + | |
| 23227 | + | |
| 23228 | + | |
| 23229 | + | |
| 23230 | + | |
| 23231 | + | |
| 23232 | + | |
| 23233 | + | |
| 23234 | + | |
| 23235 | + | |
| 23236 | + | |
| 23237 | + | |
| 23238 | + | |
| 23239 | + | |
| 23240 | + | |
| 23241 | + | |
| 23242 | + | |
| 23243 | + | |
| 23244 | + | |
| 23245 | + | |
| 23246 | + | |
| 23247 | + | |
| 23248 | + | |
| 23249 | + | |
| 23250 | + | |
| 23251 | + | |
| 23252 | + | |
| 23253 | + | |
23142 | 23254 | | |
23143 | 23255 | | |
23144 | 23256 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
0 commit comments