# ラボに「研究員」を置く — VMOST を抽象と具体に割り、実験ログを人に帰属させた

> サイトには VMOST も Roadmap も Log もあるのに、「誰が・なぜ挑戦しているのか」を語る顔がなかった。そこで研究員ページ（/researchers）を建てた記録。単なる自己紹介ページではなく、サイト共通の VMOST を『抽象（どんな生き方を設計するか）』に寄せ、数値・行動の『具体版 VMOST』を研究員に持たせる情報設計の組み替えになった。全実験ログを研究員に帰属させ、Person の構造化データ・RSS の dc:creator・JSON Feed・llms.txt まで貫いた配線、AI 調査で実在人物の経歴を盛り込んで判断して全部剥がした persona 設計、そして「静かな脱落をビルドで落とす」チェックの追加まで、つまずきごと残します。

- 出典: SUZ-LAB（実験ログ / 一次情報）
- URL: https://suz-lab.co.jp/logs/building-the-researcher-page
- 公開: 2026-07-14
- 更新: 2026-07-14
- 著者: Hiro（主任研究員・https://suz-lab.co.jp/researchers/hiro・X: https://x.com/suz_lab_hiro）
- 柱: テクノロジー
- タグ: #VMOST #情報設計 #Next.js #構造化データ #persona #AI
- カバー画像: https://suz-lab.co.jp/assets/logs/building-the-researcher-page.png?v=ef5fb0f1

---


## 何をしたかったか — ラボに「顔」がなかった

[VMOST](/vmost) で設計図を描き、[Roadmap](/roadmap) で次の一手に落とし、
[Log](/logs) で実施結果を一次情報として残す——[この線は前に一本につないだ](/logs/connecting-vmost-roadmap-logs)。
解説トピックもショーケースもある。それでも一つ、決定的に欠けていた。

**「誰が・なぜ、これをやっているのか」を語る場がなかった。**

Vision に掲げたのは「その姿そのものを、次に続く人へのロールモデルとして示す」こと。
なのにサイトのどこにも、その「姿」の入口がない。ロードマップにも `about-profile`
（プロフィール／ロールモデルページ）が P2 で積まれたまま、[構造化データを実装したとき](/logs/making-suz-lab-machine-readable)
に「Person の JSON-LD はプロフィールページを作るときに足す」と保留したままだった。

だから顔を作ることにした。ただし、ありがちな「About Me」ページにはしたくなかった。
このサイトは VMOST で生き方そのものを設計している。その設計図と切り離した自己紹介を
別に置くと、また分断が生まれる。**顔を、VMOST の線の上に載せる**——それが狙いだった。

## 設計 — VMOST を「抽象」と「具体」に割る

考えているうちに、これは自己紹介ページの問題ではなく **VMOST の粒度の問題**だと分かった。

いまの `/vmost` には、Vision も Objectives も「週 4 件の発信」「TOEFL iBT 100」のような
**具体的な数値・期限**が書いてあった。だがサイトの VMOST は本来「どんな生き方を設計するか」
という枠組みのはず。個人の実数値がそこに混ざっていると、将来 research を担う人が増えたとき
（このラボは「一人の人間が自分の生き方を実験台にする私設ラボ」で、いまは一人だが）、
サイト共通の枠組みと個人の数値が同じ場所で二重管理になる。

そこで一本、線を引いた。

- **抽象 VMOST（[/vmost](/vmost)）** … 「どんな生き方を設計するか」の枠組み。サイト共通。
- **具体 VMOST（各研究員）** … その枠組みを本人の**数値・行動**に落とし込んだ版。

`/vmost` から具体的な数値・期限を剥がし、それを研究員の「具体 VMOST」へ移す。
両者は相互リンクで往復できるようにする。**研究員とは、抽象 VMOST を具体に落とす器**
——この見立てが、顔ページの背骨になった。

## やってみたこと

### 1. 研究員というデータモデル

研究員の唯一の出所を `content/researchers.ts` に作った。1 人分は `slug` / `name` /
`handle`（@ 抜き）/ `status`（active・alumni）/ `role` / `tagline` / `bio` / `focus`
（3 本柱に対応する注力領域）/ `vmost?`（具体版）/ `cover` を持つ。いまは Hiro 一人。

単一の出所を貫くために、地味だが効く判断を一つ。**X の URL はフィールドで持たず、handle
から導出する。**

```ts
// handle（@抜き）だけを持ち、URL は必ずここで組む。二重管理を作らない。
export const researcherXUrl = (r: Researcher): string =>
  `https://x.com/${r.handle}`;
```

当初は `x: "https://..."` を手書きしていたが、handle 表記とズレても型では気づけない。
URL を組む場所を一箇所に寄せ、詳細ページの X ボタン・JSON-LD の `sameAs`・llms.txt の
著者行が、すべてこの関数を通るようにした。

### 2. VMOST 抽象/具体分離

`/vmost` の `objectives` から個人固有の数値・期限を剥がし、抽象文言へ書き換えた
（「週 4 件の発信を定常化」→「発信の定常化」、期限 `2027-06` → `中期`）。数値が抽象側から
消えたので、「この数値は暫定」を示していた `Objective.provisional`（暫定バッジ）フィールドは
**型ごと削除**した。抽象化がデータモデルを一段単純にした。

剥がした具体は研究員の `vmost.concrete` へ移した。`concrete` は V/M/O/S/T ごとに散文でも
箇条書き配列でも持てる形にし、表示側が配列なら `<ul>` に開く。

表示フォーマットは分けた。トップの VMOST サマリーは各階層を一行で並べる `VmostStepList`、
研究員ページは各階層を 1 枚のカードにして、上に小さく「抽象 / SUZ-LAB」、下に大きく
「具体 / 本人」を縦に対比させる専用の `ResearcherVmost`。**抽象と具体を隣り合わせて見せる**
のが研究員ページの主役なので、共有をあえてやめた（この判断の紆余曲折は後述）。

導線も往復で閉じた。`/vmost`（抽象）→「具体的な VMOST は各研究員のページへ」→ `/researchers`、
研究員カードの各階層「抽象 / SUZ-LAB」行 → `/vmost#{anchor}`（具体 → 抽象）。

### 3. 実験ログを研究員に帰属させる

「実験は研究員が回す」という見立てを、ログのメタデータに落とした。ログ frontmatter に
任意の `author`（研究員 slug）を足し、**省略時は既定の研究員（いまは Hiro）に帰属**させる。

```ts
export const DEFAULT_LOG_AUTHOR_SLUG = "hiro";

// frontmatter の author か、省略時は既定の研究員に解決する。
export const logAuthor = (log: LogFrontmatter): Researcher | null =>
  getResearcher(log.author ?? DEFAULT_LOG_AUTHOR_SLUG);
```

既存 12 本のログには `author` を書いていない——全部が既定の Hiro に帰属する。1 人のうちは
これで十分で、増えたら書くだけ。この帰属を、体裁と機械可読の両レイヤーへ貫いた。

- **ログ詳細**の byline に「研究員 {name}」を出し、`/researchers/{slug}` へ逆リンク。
- **研究員ページ**は `getLogsByAuthor(slug)` で「その研究員の実験ログ」を並べる。
- **JSON-LD** … BlogPosting の `author` を Person 化し、`@id: /researchers/{slug}#person`
  で研究員の Person ノードと連結（[構造化データ](/topics/json-ld-structured-data)で保留にした
  Person をここで出した）。トピックは発行元を Organization のままにした。
- **RSS** … `<author>` は仕様上メールアドレス必須なので、名前だけ出せる Dublin Core の
  `<dc:creator>` を採用した。
- **JSON Feed** … `authors`（名前＋プロフィール URL）と、拡張名前空間で著者の X URL
  （一次情報の入口）を添えた。
- **llms.txt** … 各ログの Markdown 版に「著者: {name}（{role}・プロフィール・X）」を追記。

これで「ログ ⇄ 研究員 ⇄ VMOST 具体」がひとつながりになった。

### 4. ロードマップを追随させる

顔ができたので、ロードマップを実態に合わせた（この更新はこの記事と同じ変更に含めた）。

- `about-profile` を **未着手 → 完了**に。frontmatter の `roadmap: "about-profile"` で、
  この記事がその実施 Log として逆引きされる。
- `objectives-smart`（Objectives を SMART に）と `dashboard`（進捗ダッシュボード）の本文を、
  **具体的な数値・期限の出所が研究員の具体 VMOST（`content/researchers.ts`）に移った**ことに
  合わせて書き直した。SMART 化もダッシュボードの計測も、これからは抽象 VMOST ではなく
  研究員の具体側を対象にする。

顔を足すだけのつもりが、VMOST → Roadmap → Log の背骨に「研究員」というノードを一つ挿し、
数値の置き場所を組み替える変更になった。

## つまずき

### 調査を入れて、全部剥がした

一番の紆余曲折はここ。研究員に厚みを持たせようと、まず AI に Web 調査（一次情報中心・
敵対的検証つき）をやらせ、実在の経歴——本名、CTO 経験、技術書の共著、大学院——を bio・
「経歴・実績」節・VMOST へ盛り込んだ。**「まず、やってみる」で、いったん全部入れた。**

そのあと、判断して全部剥がした。段階的に、実名表示 → 「経歴・実績」節と `background`
フィールド → bio の職歴段落 → focus / VMOST の職歴語、の順で。理由は二つ。

- **実在人物のプライバシー。** 実名と勤務先を紐づけた経歴を、本人の生き方の実験ログと
  同じ場所に常設で置く必然性はない。ラボの語り口は persona「Hiro」で通すことにした。
- **AI 調査は裏取りしても残余リスクがある。** 敵対的検証の過程で「ある受賞歴」が本人には
  無いと反証され、記載を見送った一件があった。個別には潰せても、実在人物の経歴を丸ごと
  載せる限り「もっともらしい誤り」の混入リスクはゼロにならない。

残したのは、VMOST の具体と、手を動かした実験ログそのもの。**経歴で語るのではなく、
やったことで語る**——このラボの Strategy「一次情報で語る」と、結果的にきれいに揃った。

### VMOST 表示フォーマットの二転三転

<Details summary="共有（DRY）を狙って、分離に転じた話">

最初は `VmostStepList` を「本文だけ差し替えれば抽象版と具体版で使い回せる」共有部品に
しようとした。DRY の直感としては正しい。だが作ってみると、研究員ページで見せたいのは
「抽象と具体の**対比**」で、トップで見せたいのは「抽象版の一覧」——**そもそも見せ方が違う**
と気づいた。無理に一つの部品へ畳むと、両方に効く分岐が増えて読みにくくなる。

そこで割り切った。`VmostStepList` は抽象版専用に戻し、研究員の対比表示は専用の
`ResearcherVmost` を新設。「同じに見えるから共有」ではなく「意図が同じなら共有」。
一度 DRY を狙って、意図が違うと分かって分離した——この往復自体が判断の記録になる。

</Details>

### 静かな脱落をビルドで落とす

`author` を導入して怖かったのは、**解決に失敗したとき静かに消えること**。`logAuthor` が
null を返すと、byline も JSON-LD も RSS も Feed も llms.txt も研究員一覧も、
エラーを出さずにただ「著者なし」になる。壊れているのに気づけない。

そこで `check:content`（`pnpm build` に組み込み済みのコンテンツ検査）に一節足した。

- 既定著者 slug と、全ログの `author` が、実在する研究員に解決することを検査する。
- `author: 123` のような**文字列以外は型エラーで落とす**。空文字・空白は「未指定」として
  既定へフォールバックするが、誤った型は静かに既定化させず、はっきり落とす。

TypeScript では守れない「Markdown の値が実在する slug を指しているか」を、ビルド時に
潰す。[公開 MCP サーバ](/logs/building-the-public-mcp-server)の slug 検証と同じ思想で、
**静かな脱落は実行時に晒す前にビルドで殺す**。

## 検証

- `pnpm typecheck` / `pnpm lint` / `pnpm format:check` … いずれもパス。
- `pnpm build`（`check:content` 込み）… `/researchers` と `/researchers/hiro`、全ログが
  静的生成され、`author` と既定著者が実在研究員へ解決することを確認。
- 生成物を突き合わせ、**双方向リンクが実際に描画される**ことを確認した——ログ詳細から
  研究員への byline、研究員ページの「Hiro の実験ログ」、`/vmost` ⇄ `/researchers` の
  抽象／具体リンク、JSON-LD の Person が `@id` で BlogPosting の author と連結、RSS の
  `<dc:creator>`。

## 学び

- 「自己紹介ページが無い」という UI の欠落に見えたものは、掘ると **VMOST の粒度（抽象と
  具体の混在）** という情報設計の問題だった。器を足す前に「何をどの層で持つか」を割ると、
  ページは自然に決まる。
- **単一の出所は、剥がすときにも効く。** 数値を抽象 VMOST から研究員の具体へ移すのも、
  経歴を丸ごと抜くのも、出所が `content/*` の一箇所に寄っていたから安全に動かせた。
- **「まず、やってみる」は「まず、残す」ではない。** AI 調査で経歴を入れてみて、判断して
  剥がす——やってみたからこそ、persona で通すという線引きに確信が持てた。やってみることと、
  出すことは別の判断。
- 顔をこのサイトで作る営みそのものが、[ドッグフーディング](/topics/dogfooding)だった。
  次はこの研究員の具体 VMOST を出所に、`objectives-smart`（数値の SMART 化）と `dashboard`
  （進捗の可視化）へ進む。

