publish-note
browser-use CLI で note.com(note.com/dobokunote)エディタを自動操作し、(1) 総監模範論文ペルソナ別マガジン記事(有料)または (2) 建設部門の無料ファネル記事(入口/キーワード, `--free`)を下書き保存・予約・即時公開する。Use when user says "note投稿", "note公開", "note下書き作成". 本文paste
SKILL.md.claude/skills/social/publish-note/SKILL.md
GitHubbrowser-use CLI(Chrome プロファイル経由)で note.com/dobokunote のエディタを自動操作し、総監模範論文ペルソナ別マガジンの記事(content/note/技術士総監/magazines/総監模範論文-<persona>/<RXX>/article.md)を下書き保存または予約投稿する。
このスキルは stats47 プロジェクトの publish-note(note.com 自動投稿の実証済みスキル)を doboku-note 用に適応したもの。note.com エディタ操作の共通ノウハウは references/ を参照し、本 SKILL.md は doboku-note 固有の規約・差分・安全ゲートを定義する。
実行環境の前提(重要)
- browser-use 経路(本スキルの新規公開フロー)は Mac 推奨。会社 PC(Windows)はプロキシが browser-use の LLM バックエンドを遮断する可能性が高く、browser-use 自体も未導入([[project_ig_api_posting_setup]] と同根)
- ただし Windows でも動く Playwright 経路がある(browser-use 不要・LLM バックエンド不要): 新規公開=
scripts/note-publish.mjs、公開済み記事への CTA 追記/ライブ反映=scripts/note-append-cta.mjs(npm run note-append-cta)、マガジン設定=note-edit-magazine.mjs。いずれも<auth root>/profiles/playwright-note-profile(永続・初回のみ手動ログイン)を使う。update 系の詳細は references/update-mode.md - browser-use CLI がインストール済みであること(Mac 経路のみ・
$HOME/.browser-use-env/等) - Chrome プロファイルが note.com/dobokunote にログイン済みであること。プロファイル名は環境変数
NOTE_PROFILEに設定(例:export NOTE_PROFILE="Profile 1")。references の例にあるProfile 5は stats47 用なので使わない - 予約投稿は現在は無料(誰でも可・note プレミアム不要)。Windows Playwright 版は
scripts/note-publish.mjs --schedule "YYYY-MM-DDTHH:MM"(JST)、時間ずらしバッチはnote-publish-magazine.mjs --list <manifest> --schedule-start ... --interval-hours N
投稿先アカウント(最重要・誤爆防止)
このスキルの投稿先は note.com/dobokunote 固定。 Phase 1 のアカウント照合ゲートを必ず通す(stats47 で 2026-05-20 に別アカウントへ誤公開した事故の再発防止策。プロファイル分離だけではセッションドリフトを防げない)。
stats47 → doboku-note 差分マップ(references を読む際の読み替え表)
| 項目 | stats47(references の記述) | doboku-note(本プロジェクト) |
|---|---|---|
| アカウント | note.com/stats47 / Profile 5 / stats47jp@gmail.com |
note.com/dobokunote / $NOTE_PROFILE |
| 記事パス | docs/31_note記事原稿/<vertical>/<slug>/{note.md,draft.md} |
content/note/技術士総監/magazines/総監模範論文-<persona>/<RXX>/article.md |
| 有料フラグ | frontmatter is_paid / price_jpy |
frontmatter notePricing: paid / price: 500 |
| 有料境界マーカー | 本文中 ここから先は有料部分: 行 |
既定=## 試験問題/## 予想問題 行の直前(intro・「この記事でわかること」・冒頭CTA は無料プレビュー、試験問題+解答以降が有料)。境界見出しはコンテンツ型ごとに上書き可=Windows/Playwright 版 note-publish.mjs が frontmatter paidBoundary(H2 見出しの先頭一致 regex)または --boundary-regex を読む。1級土木 完全攻略パック(工事別)は paidBoundary: 品質管理(導入+冒頭CTA+想定工事概要が無料・5管理の完成答案以降が有料)。境界が見つからなければ公開中断(boundaryBeforeExam ゲート) |
| マガジン名/説明/価格 | 各 vertical の設定 | 総監模範論文-<persona>/note掲載文.txt からコピペ |
| ハッシュタグ | 記事内 | <RXX>/hashtags.txt(90 個以上必須〜99・1 行 1 タグ=# 接頭辞・改行区切り。space 区切り単一行は note 側で 1 タグ扱いになる不良。npm run check-note-hashtags が機械検査、--staged で pre-commit ゲート。note-publish は 99 で打ち切り) |
| アイキャッチ | 生成画像 | <RXX>/img/cover.png |
| 購入特典PDF | なし | <RXX>/模範論文-*.pdf(有料エリアに添付。Windows/Playwright では note-attach-pdf〔note-attach-file.mjs〕で自動化済=本 browser-use 系では半自動) |
| 公開URL記録 | note-published.json(items) |
各記事 frontmatter noteUrl/noteId + src/lib/note-magazines.ts(マガジンURL・published) |
引数(バッチ対応)
/publish-note <persona> <RXX>[ <M/D> <HH:MM>] [, <persona2> <RXX2> ...]
- persona: ペルソナ名(例
自治体港湾担当。総監模範論文-接頭辞は不要) - RXX: 年度ディレクトリ(
R03〜R07/R08-yosou-1/R08-yosou-2) - M/D HH:MM: 予約日時(任意・省略時は下書き保存のみ)
例: /publish-note 自治体港湾担当 R03 7/1 08:00, 自治体港湾担当 R04 7/1 12:00
一括公開の区切りと成否判定(2026-08-22 実測)
- 1バッチは 25 本まで。実務立場別小論文集33本を連続投稿したところ、31本目から「投稿する」が完了しなくなった(画面は公開設定のまま留まり、記事は 404。再実行でも再現)。20分空けたら同じ原稿がそのまま公開できたので、note 側の連続投稿スロットルとみられる。25本で区切るか、失敗したら 20 分待って再開する。
- 成否は
RESULT行で判定しない。RESULTの"mode":"commit"は失敗時も出る。note-publish.mjsは最後に[13]で API 実体検証を行い、失敗時は[13] FAIL: ...を出すので、バッチのループはこの行の有無で判定する。2026-08-22 に RESULT 行で数えて、失敗した2本を成功として数えた。 - 公開後は API で全数実査する(
https://note.com/api/v3/notes/{key}のstatus/price/ 本文の無料プレビュー字数)。有料記事でpreview=0は有料境界の崩壊。 - 中断すると下書きが残る。frontmatter の
noteUrlを消して再実行すると新しい key で作り直すため、残骸はnote-delete-note.mjsで消す(ただし同スクリプトはnote.com/notesの1ページ目しか走査しない)。
無料記事モード(建設部門 入口/キーワード記事)— --free
総監マガジン(有料・ペルソナ/年度)とは別系統で、建設部門の無料ファネル記事(notePricing: free)を公開する。有料境界の分割・価格設定・特典PDF添付・マガジン設定をすべてスキップする分、フローは単純。content/note/技術士建設部門/{theme}/article.md(入口16本・論点キーワード6本)が対象。
引数(無料モード)
/publish-note --free <技術士建設部門配下のdir>[ <M/D> <HH:MM> | now][, <dir2> ...]
- dir:
content/note/からの相対パス(例技術士建設部門/防災・減災の論点キーワード)。スペースを含むのでカンマ区切りのバッチでは各 dir をそのまま書く - 日時・
now省略 → 下書き保存のみ(既定・最も安全。まず1本これで挙動確認するのを推奨) now→ 即時公開/<M/D> <HH:MM>→ 予約公開(note は現在 無料で予約可)- 例(下書き):
/publish-note --free 技術士建設部門/防災・減災の論点キーワード - 例(即時公開):
/publish-note --free 技術士建設部門/社会資本整備の論点キーワード now
Phase 0-free: データ読み込み(無料記事版)
/tmp/note-prepare-free-<slug>.js に書き出して実行する。notePricing: free でなければ即中断(有料記事を誤って無料公開しない安全弁)。
// /tmp/note-prepare-free-<slug>.js
const fs = require('fs');
const path = require('path');
const projectRoot = '/Users/minamidaisuke/doboku-note';
const relDir = '<DIR>'; // 例: 技術士建設部門/防災・減災の論点キーワード
const articleDir = path.join(projectRoot, 'content/note', relDir);
const articleFile = path.join(articleDir, 'article.md');
if (!fs.existsSync(articleFile)) { console.error('ERROR: article.md not found: ' + articleFile); process.exit(1); }
const raw = fs.readFileSync(articleFile, 'utf8');
const fm = (raw.match(/^---\r?\n([\s\S]*?)\r?\n---/)?.[1]) ?? '';
const fmField = (k) => { const m = fm.match(new RegExp('^' + k + ':\\s*(?:"(.*?)"|\'(.*?)\'|(.+?))\\s*$', 'm')); return m ? (m[1] ?? m[2] ?? m[3] ?? '') : ''; };
const notePricing = fmField('notePricing');
if (notePricing !== 'free') { console.error('ABORT: notePricing != free (got "' + notePricing + '"). 無料記事モードは free 専用。'); process.exit(2); }
// タイトルは本文先頭の H1(建設部門の無料記事は frontmatter に title: を持たず `# …` が表示タイトル)
const bodyForTitle = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, '');
const title = (bodyForTitle.match(/^#\s+(.+)$/m)?.[1] ?? fmField('title')).trim();
if (!title) { console.error('ABORT: タイトル(先頭 # H1)が見つからない。'); process.exit(3); }
let body = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, '');
body = body.replace(/<!--[\s\S]*?-->\n?/g, ''); // HTML コメント除去
// 本文画像  は除去せず一意トークン化して残し、paste 後に「+」メニューでアップロードする
// (2026-07-15〜。真実源: lib/note-images.mjs extractBodyImages / insertImagesAtPlaceholders)
// const { body: b2, images } = extractBodyImages(body, articleDir); body = b2;
body = body.replace(/^---$/gm, ''); // 水平線除去
body = body.replace(/^#\s+.*\n+/, ''); // 先頭 H1 除去(タイトル欄へ入れるため)
body = body.trim();
function splitSegments(text){ const segs=[]; let buf=[]; for(const line of text.split('\n')){ if(/^https?:\/\/\S+$/.test(line.trim())){ if(buf.length){segs.push({type:'text',content:buf.join('\n')});buf=[];} segs.push({type:'url',content:line.trim()});} else buf.push(line);} if(buf.length)segs.push({type:'text',content:buf.join('\n')}); return segs; }
const segments = splitSegments(body);
const tagsPath = path.join(articleDir, 'hashtags.txt');
const tags = fs.existsSync(tagsPath) ? fs.readFileSync(tagsPath,'utf8').trim().split('\n').map(s=>s.trim()).filter(Boolean).slice(0,50) : [];
const coverPath = path.join(articleDir, 'img/cover.png');
const cover = fs.existsSync(coverPath) ? coverPath : null;
const slug = relDir.replace(/[\/\s・/]+/g,'-');
const result = { slug, articleDir, articleFile, title, isPaid:false, priceJpy:0, segments, segmentsFree:segments, segmentsPaid:[], tags, cover, segmentCount:segments.length, urlCount:segments.filter(s=>s.type==='url').length };
fs.writeFileSync('/tmp/note-data-'+slug+'.json', JSON.stringify(result,null,2));
console.log(JSON.stringify({ slug, title:title.substring(0,50), notePricing, segments:segments.length, urls:result.urlCount, tags:tags.length, cover: !!cover }));
実行: node /tmp/note-prepare-free-<slug>.js。中断(exit≠0)したらその記事は公開しない。
フロー(無料記事 = 有料フローからの差分)
references/editor-operations.md の Phase を再利用しつつ、無料記事では次の差分を適用する。
- Phase 1 アカウント照合ゲート: 共通(必須)。
dobokunote不一致なら即中断 - Phase 2 アイキャッチ:
data.cover(=<dir>/img/cover.png)をアップロード。editor-operations の例にあるimages/cover-1280x670.pngは読み替える - Phase 3 タイトル:
data.title(先頭 H1 は除去済) - Phase 4 本文:
data.segments(=全文)をチャンク注入機構で 1 回 paste(無料記事は free/paid 分割なし=全文が無料)。BK-I の note URL・サイト URL 行のカード化は半手動(行末 Enter→4 秒) - Phase 4.5 目次: 見出し(H2)が 3 つ以上の記事(もくじ/総合案内/長文)は、本文先頭に note ネイティブ目次ブロック(
<button id=toc-setting>)を挿入する(references/editor-operations.md Phase 4.5)。markdown の#アンカーは note で機能しないため、見出しジャンプはこのブロックが唯一の手段。短い単一セクション記事はスキップ - Phase 5 挿絵: スキップ(図版なし方針)
- Phase 6 下書き保存: 共通
- Phase 7 公開設定: 価格設定は行わない(無料)。
<RXX>相当のタグはdata.tags(hashtags.txt)。日時指定 → 予約公開、now→ 即時公開、無指定 → 下書きのまま終了 - Phase 8 検証&記録: 偽成功の罠に従い本文文字数・カバー・タグを実体検証。公開した場合のみ 記事 URL を当該
article.mdfrontmatternoteUrl/noteId/notePublishedAtに反映(これらは空文字で用意済み)し、noteStatusを published(予約投稿時は reserved)へ更新。下書きのみなら記録しない。※予約投稿は go-live が後刻のため weekly のverify-note-statusが published へ最終是正
無料モードのガード
notePricing != free→ 即中断(有料記事の無料公開防止)- 本文に
{{MAGAZINE_URL}}等プレースホルダが残存 → 中断 - 既存の note 入口16本は
noteStatus: draft。公開順は無料入口→キーワード→有料マガジンの導線が開く順を意識する(収益導線の整合)
browser-use 共通設定
export PATH="$HOME/.browser-use-env/bin:$HOME/.browser-use/bin:$HOME/.local/bin:$PATH"
export NOTE_PROFILE="Profile 1" # note.com/dobokunote にログイン済みのプロファイル名に置換
全コマンド: browser-use --headed --profile "$NOTE_PROFILE" <command>(references の --profile "Profile 5" は "$NOTE_PROFILE" に読み替え)
⚠️ 必須: 終了時クリーンアップ
スキル完了時/中断時に daemon と使い捨て Chrome を必ず停止する。手順は references/editor-operations.md および stats47 由来の3段クリーンアップ(macOS は osascript でエディタタブも閉じる)に従う。Windows で実行する場合は pkill/osascript が無いため、Stop-Process/タスクマネージャ等で browser-use daemon と chromium を停止する(が、原則 Mac 実行)。
実行フロー
引数パース → 記事ごとにループ:
Phase 0: データ読み込み(Node.js・doboku-note 規約)
Phase 1: ブラウザ起動 → ★dobokunote アカウント照合ゲート★ → エディタ表示
Phase 2: アイキャッチ(img/cover.png・本文入力前に実行)
Phase 3: タイトル入力(frontmatter title から H1 を除いた表示タイトル)
Phase 4: 本文入力(一括 ClipboardEvent paste・URL は plain text)
Phase 4.5: 目次ブロック挿入(H2 3 つ以上=もくじ/長文記事のとき。id=toc-setting。markdown #アンカーは note で機能しないため必須手段)
Phase 5: (図版なし方針のため通常スキップ)
Phase 6: 下書き保存
Phase 7: 公開設定(有料価格・タグ・予約 or 即時)※本 browser-use 系では有料境界とPDF添付は半自動(Windows/Playwright は note-publish〔境界〕・note-attach-pdf〔PDF〕で自動化済)
Phase 8: 確認スクショ → 公開URLを frontmatter に反映
→ 全記事完了後にブラウザを閉じる + 必須クリーンアップ
Phase 0: データ読み込み(doboku-note 規約)
Node.js で対象記事を読み込み /tmp/note-data-<persona>-<RXX>.json に出力する:
content/note/技術士総監/magazines/総監模範論文-<persona>/<RXX>/article.mdを読む- frontmatter から
title(先頭#見出しを表示タイトルに採用)/notePricing/priceを抽出 - 本文を
## 試験問題行の直前で free / paid に分割(intro=無料プレビュー、試験問題以降=有料) - 本文をセグメント分割(URL 単独行 vs テキスト。URL は paste 後に plain text のまま=カード化は Phase 4-3)
<RXX>/hashtags.txt(タグ)、<RXX>/img/cover.png(アイキャッチ)、<RXX>/模範論文-*.pdf(特典)の存在を確認
Phase 0 ガード: 本文に未反映プレースホルダー({{MAGAZINE_URL}} または ※note 公開後…予定 系)が残っていたら、その記事は公開せず中断。先に .claude/scripts/note/inject-magazine-url.cjs <persona> <マガジンURL> で実 URL を注入しておく。
Phase 1: dobokunote アカウント照合ゲート(投稿前に必ず)
browser-use --headed --profile "$NOTE_PROFILE" open "https://note.com/settings/account"
browser-use --headed --profile "$NOTE_PROFILE" state 2>&1 > /tmp/note-acct.txt
state 出力からログイン中アカウントのハンドルを読み取り、dobokunote と決定論的に文字列照合する。
- 一致 → Phase 2 へ
- 不一致 / 未ログイン / 確認不能 → 即中断(1 記事も投稿しない)。「Profile が note.com/dobokunote にログインしていません。投稿を中止しました」と報告。憶測で続行しない
Phase 2〜8: エディタ操作
具体的な browser-use コマンド・要素検索・paste 機構は references/editor-operations.md、有料価格/タグ/予約は references/scheduling.md、要素ヘルパー/エラー処理は references/troubleshooting.md を参照(アカウント・パスは差分マップで読み替え)。doboku-note 固有の要点:
- Phase 2 アイキャッチ:
<RXX>/img/cover.pngをアップロード(本文入力前) - Phase 4 本文: free セグメントを一括 ClipboardEvent paste(H2/H3/太字が変換される唯一の方法。
type不可・連続 paste 不可)。URL カード化は半手動(本プロジェクトの記事はマガジンCTAの URL を単独行で持つ=ペースト後に各 URL 行を行末 Enter → 4 秒待ちでカード化。references 4-3) - Phase 7-Pricing:
notePricing: paidかつprice>0のとき有料設定。Shadow DOM 内<input id=price>に JS で価格を上書き(type不可) - 本 browser-use(Mac) 系では 有料境界の指定 と 特典PDF添付 は半自動(stats47 でも未到達領域だった)。価格設定までは自動、有料エリア境界の選択と PDF 添付は人間が手動。※Windows/Playwright 系では両方とも自動化済=有料境界は
note-publish(boundaryBeforeExam検証ゲート)、PDF 添付はnote-attach-pdf(note-attach-file.mjs/既存境界を非破壊検証して再公開)。Windows 会社PCで運用する場合はこちらを使う - Playwright 版も目次挿入に対応済(2026-07-02)=
note-publish.mjs(新規公開・Phase 6.5)とnote-update-body.mjs(全文置換後の再挿入・4.5)が H2>=3 で本文先頭に note ネイティブ目次(#toc-setting)を挿入する(--no-tocで抑止)。目次は Phase 4.5(browser-use)だけの手作業ではなくなった。検証は screenshot 目視(.tmp/np-toc.png/.tmp/nu-toc-*.png) - macOS 実行の注意(note-update-body): 本文全置換の「全選択」は Meta+A(Ctrl+A は行頭移動で空化に失敗→本文二重化)。
note-publish.mjsは /new 空エディタ paste なので影響なし。無料記事の--commitは「更新する」まで自動完走(2026-07-14 実証で旧「未対応」は解消) - メンバーシップ連携記事(price=0 だが会員限定):
note-update-bodyは3段の試し読みフロー(公開に進む → 試し読みエリアを設定 → 更新する)に対応。入口LP(まるごとパック等)の無料プレビュー復旧は--trial-line-bottom(試し読みラインを末尾-1に設置。絶対末尾は全ロックに戻る罠あり)。無料設定のまま意図して全文ロックしている記事は--keep-member-lock。無料記事でどちらも無いと試し読み画面で中断する(ラインなしの更新で全文会員限定になる事故の防止・2026-09-23〜)。試し読みの画面が直接開く回も同じく判定・中断し、反映後は[5g]で本文の更新を確かめる(2026-10-05〜)。詳細 SSOT:.claude/knowledge/reference/note-api-verification.md「メンバーシップ試し読みフロー」 - 公開済み記事へのタグ差分追加:
scripts/note-sync-tags.mjs(本文非破壊・note上限99・不足分をlive照合で追加。上限99で足せないときは--pruneで原稿に無いタグを外してから足す)。note-update-body は単発では本文専用だが、--sync(未反映の部品だけ)や--parts body,cover,tagsでカバー・タグも 1 回の更新に含められる(Mac の週次 note-sync はこちらで記事単位にまとめて反映する)。タグdrift はcheck-note-republishが surface - frontmatter の note 行*:
note-publish.mjsの公開後 writeback はnoteUrl/noteId/notePublishedAt/noteStatusを 行が無ければ挿入する(setFmField・2026-07-02 是正)。旧実装は replace-only で、テンプレに note* 行が無い記事は URL 記録が無音失敗し、note-publish-magazineの冪等ガード(noteUrl 有無)も効かず一括重複公開の危険があった - Phase 7-Tags:
<RXX>/hashtags.txtの内容を入力 - 予約日時があれば予約投稿、なければ「今すぐ公開」または下書き保存のみ
Phase 8 後: 公開 URL の反映
公開(即時/予約)したら、その記事 URL(note.com/dobokunote/n/<id>)を:
- 当該記事
article.mdの frontmatternoteUrl/noteId/notePublishedAt/noteStatus(published/予約=reserved)に記入 - マガジン単位で全 7 記事が公開済みになったら、
src/lib/note-magazines.tsの該当エントリをpublished: true+ マガジンnoteUrlに更新(マガジンURL はinject-magazine-url.cjsで本文にも反映)
下書き保存のみの場合は記録しない。
偽成功の罠(必ず実体検証)
「投稿できた」というログを信用しない。stats47/publish-x で予約ゼロの空振り事故があった([[feedback_publish_x_false_success]])。公開後は note の公開ページ(または下書き一覧)を実取得して、本文・カバー・タグ・価格・カード化が実際に反映されているか DOM/スクショで確認してから「完了」と報告する。clipboard paste 不発(本文空)も頻発するため、paste 後に本文の文字数を eval で確認する。
幻 noteId(fail=0 なのに未公開)— バッチ公開後の必須ゲート
エンジンの分界(DN-0091・2026-08-27 明示): 下記「バッチ側の一次ガード」は Playwright 系エンジン (
note-publish.mjs/note-publish-magazine.mjs=本スキルとnote-publishスキルが共用する実装)の話。 一方「バッチ後の確証ゲート」verify-note-statusはどのエンジンで公開しても共通の実在照合で、 公開経路を問わず必須。スキル名が似ているnote-publish(.claude/skills/social/note-publish/)と本スキル (.claude/skills/social/publish-note/)は同じエンジン群を呼ぶ別入口であり、ガードは実装側に置かれているため どちらから使っても効く。
note-publish.mjs の writeback はページから拾った URL の id を書くだけで、公開が実際は未完了でも幻 id を frontmatter に書きうる。note-publish-magazine.mjs は従来 noteUrl の有無だけで OK 判定していたため、fail=0 と報告しつつ一部が未公開+幻 id(API で 404) という偽成功が起きた(2026-06-30 完全攻略パック 工事82-87 の6本・[[project_civil1_flagship_pack]])。
- バッチ側の一次ガード(2026-07-01 実装済):
note-publish-magazine.mjsは即時公開分について、書き戻した noteId が note API v3 で実在するか照合する。確定 404(幻 id)ならfailで停止する(予約投稿は go-live 後刻ゆえ検証しない)。 - バッチ後の確証ゲート(必須): 公開バッチ完了後・「完了」と報告する前に必ず
npm run verify-note-statusを回す。fm=published ↔ ライブ=404 をWARNで列挙する reconciler で、幻 id・静かな未公開を全件で捕捉する。WARN が出たら該当記事の frontmatter(noteUrl/noteId/notePublishedAt)を空へリセット→--commitで再公開→再照合する。ネットワーク依存で遅いため CI ゲートには入れない(weekly-review でも定期実行)。
有料記事は「公開・更新しただけ」では未完成(2026-07-31 新設)
有料記事は 公開できたかだけでなく、購入前の読者に何が見えているかまで確認して初めて完了になる。2026-07-31 に 2 つの事故が同時に起きた。
① 無料プレビューの崩壊(paywall 事故): note-update-body --keep-boundary で本文にブロックを足したところ、有料境界が記事冒頭へ移動し、有料2本が無料プレビューほぼ0字の状態で公開された。スクリプトは「ライブ反映完了」と正常終了しており、公開ページを人が開くまで誰も気づけなかった。
--keep-boundaryは本文のブロック数が変わる更新では使わない。CTA 追加・段落追加を伴う更新は必ず--boundary-h2 "<境界H2>"で境界を再設定するassertLiveBodyがpaid: trueのとき無料プレビュー長を検査するようになった(既定 600 字未満で FAIL)。note-publish/note-update-bodyは緑で終わらない- 全件を一括で実査するスクリプトは無い。
assertLiveBody(scripts/lib/note-live-check.mjs)が公開・本文更新の都度 1 記事ずつ検査する
② 商品導線ゼロ: 単品有料記事を 8 本公開したのに、無料プレビューにマガジンへの導線が 1 本も無かった。単品 ¥500 を読んだ読者がセットに気づけない。check-note-paid-cta は「L2 もくじ未定義の資格 14 件(対象外)」として素通りさせていた=検査ゼロを PASS と呼んでいた。
- 有料記事は所属マガジン URL を無料域(有料境界の直前)に bare URL 単独行で置く。価格は本文に書かない(改訂で陳腐化・content-principles §14-c)
npm run check-note-paid-ctaが L2 未定義の資格も検査するようになった(導線ゼロは exit 1)
③ PDF 添付の消失: 全文置換で添付が消え、購入者が代金を払って PDF を受け取れなくなった(施工計画 R08予想 3 本)。note のアップロード 1 日 100 件上限に達して復元が失敗 →「保存しない」で中断 → エディタには添付削除済みの状態が残る → 失敗理由を確かめず再実行し、添付 0 を正として上書きした。
- 語句・段落・CTA・カード・節順だけなら
note-update-partial(対象要素限定・PDF 添付不変条件・dry-run は読み取りだけ) - 全文差し替えが不可避な有料記事だけ
note-update-body --reattach-pdf(置換前に添付を記録し、保存前に貼り直す) - 中断した記事をそのまま再実行しない。
note-update-bodyは中断を.claude/state/note-update-aborted.jsonに記録し、次回は SKIP する(--force-retryで解除)。解除前に必ず live を実査する - アップロードは 1 日 100 件が上限。
--reattach-pdfは done-log を見て 90 件で止まる
公開・更新後の必須4点(これを通すまで「完了」と言わない):
node scripts/check-note-structure.mjs # 無料プレビュー崩壊・境界・価格(FREE_PREVIEW_COLLAPSE/FULL_LOCK)
npm run check-note-attachments:live -- --only <noteId,...> # 約束した PDF が live に実在するか(要ログイン)
npm run check-note-paid-cta # 無料域に商品導線があるか
npm run verify-note-magazines # マガジン公開状態と SoT の突合
添付を持つ記事を更新したら check-note-attachments:live を必ず回す。2026-07-31 はこれを一度も回さなかったため、3 本の消失に気づくのが遅れた。
PDF 商品は「公開しただけ」では未完成(2026-07-28 新設)
note の PDF 添付はプラットフォーム機能で markdown に現れない。公開と添付は別工程で、添付を忘れても SoT からは分からず、購入者が代金を払って PDF を受け取れないまま売れ続ける(1級土木 一次過去問PDF ¥1,980 で発生・PDF はビルド済みでアップロードだけが漏れていた)。
note-publish --commit は記事 dir(または <dir>/pdf/)に PDF があると公開後に exit 9「未完成」 で終わり、実行すべき note-attach-file コマンドを列挙する。緑で終わるまでが公開作業。→ note-attach-pdf スキル
トラブルシューティング
要素検索ヘルパー(find_idx)・実証済み要素パターン・clipboard paste 不発時の対処は references/troubleshooting.md。既存公開記事の更新は references/update-mode.md。
[!warning] 既存公開記事の更新で paste は使えない(
/new専用。notes/<id>/editでは無音失敗)。追記は type 方式(URL は type で OGP カード化)、全面差し替えは/new作り直し、空更新事故の復旧は note の変更履歴。「全消去→再 paste」は禁止(2026-06-16 空更新事故)。詳細は update-mode.md 冒頭の danger ブロック。
参照
.claude/skills/social/publish-note/references/— note.com エディタ操作ノウハウ(stats47 由来・差分マップで読み替え).claude/scripts/note/inject-magazine-url.cjs— マガジンURLのプレースホルダ一括注入(doboku-note 版).claude/knowledge/reference/note-essay-review-checklist.mdStep 10 — 公開後 URL 反映フロー.claude/skills/social/publish-x/— 同系統のブラウザ自動投稿(persistentContext・偽成功検証の設計元)src/lib/note-magazines.ts— マガジンの published/noteUrl/price 真実源