タグ必須を機械で守る
手順書(スキル)に「ハッシュタグは必須」と書いても、検査が無ければ止まらない。 投稿の手前にある検証関数でタグの個数を縛る方法と、数え方の規則を、実際に動かした結果でまとめる。 経緯は開発日記のハッシュタグ必須と書いても止まらないに書いた。
1 検査は、全部の入口が通る場所に置く
投稿の入口が複数あると、入口ごとに検査を書いて1つ抜ける。 先に、検証関数を呼んでいる場所を数える。
grep -rn "validatePost(" src/ | grep -v "^src/validate.ts"
sns-app では、呼び出しは4か所だった。コマンド1、MCPサーバ2、管理画面のサーバ1。 4か所とも同じ関数を呼ぶので、足す場所は関数1つで済んだ。
検証を呼ぶのは下書きを作る入口で、投稿を実行する側(executePost)は呼ばない。
入口を通らずに保存された下書きは、この検査では止まらない。今回の変更では、この穴は塞いでいない。
2 数え方の規則
本文のタグを数える規則は、次の順に判定する。
直前の文字で区切る考え方は、GitHubで公開されている twitter-text の定義(hashtagBoundary)を原文で読んで、それに寄せた。同じではない点が2つある。
- twitter-text は
_のほか・や〜なども「タグの一部になる文字」として、直前にあっても境にしない。こちらは文字・数字・_・&だけを境にしない - twitter-text は「文字を1つ以上含む」ことを求める(
hashtagAlpha)。こちらは数字だけのタグを除くだけで、タグに使える文字の範囲も違う。絵文字だけの#🙂は、こちらは1個と数える(実行して確認)。twitter-text の定義では数えないはずだが、これは実行していない
X本体の今の挙動も確かめていない。
3 測った結果
ビルドした関数に、実際に入力を渡して数えた。
同じ測定は、次の2行で再現できる。
import { extractHashtags } from "./dist/validate.js";
console.log(extractHashtags("使った(#Nextjs)。#AI。")); // ["#Nextjs", "#AI"]
最初の実装は「行頭か空白の直後の #」だけを数えた。2行目の入力で空になった。
日本語の文は空白を入れずに続けるので、空白の直後では足りない。
4 下限の決め方
手順書ごとに指定の個数が違っていた。開発の発信は3〜4個、短いつぶやきは2〜3個。 検査の下限は、全部の手順書を満たす値にする。ここでは2個。狙いの個数は文章に残す。
検査を狙いの個数(3個)まで厳しくすると、別の手順書の正しい投稿(2個)を止める。
5 本文に書けない媒体は、引数で縛る
noteは本文にタグを書けない。タグは公開設定の欄に入れる。本文の検査は使えない。
そこで、コマンドの引数 --tags を必須にして、3〜5個でなければ、投稿の前に止める。
npm run post -- post note --file 記事.md --tags "#AI #個人開発 #ClaudeCode" --draft
タグ欄の入力が自動化できていないときは、成功したあとに「公開設定で付けるハッシュタグ(未入力)」を出力する。手で付ける作業を落とさないためである。
拒否は終了コード1で返る。Xのタグ0個と、noteの --tags が2個の場合で確認した。
6 既存テストの直し方
- 共通の関数に検査を足すと、タグの無い本文を使うテストが全部対象になる。 管理画面のサーバのテストで、Xの本文8本にタグを足した
- HTTPステータスだけを見るテストは、失敗の理由が入れ替わっても通る。 予約日時が不正なケースは、検証関数が日時の確認より前に動くので、タグが無いと「タグ不足の400」で通る。本文にタグを足して、元の理由で失敗する状態を保つ
- 文字数の境界のテストには、タグの重みを足す。
" #AI #開発"は重み10(" #AI"が4、" #開発"が6)。全角135文字(重み270)に足すと280、136文字なら282になる
7 確かめていないこと
- X本体の今のタグの判定。twitter-text の原文を読んだだけで、挙動の突き合わせは網羅していない
- 成功する経路を、実際の投稿で動かすこと。偽のアダプタを使うテストで通ることだけ見た
- 入口を通らずに保存された下書き
- noteのタグ欄への自動入力
- タグの中身。個数を数えるだけで、投稿に向かないタグは弾かない
関連
- 開発日記: ハッシュタグ必須と書いても止まらない — 規定を投稿コマンドに移した経緯と、数え方でつまずいた点
- ガードが素通りする書き方 — 検査が効いていなかった3つの実例と、壊して確かめる手順
- 制作物の一覧 — 作っているプロダクトの一覧