作る前に分母を決める。企画の手と意図を 1 行ずつ結ぶ「手の台帳」
Fix the denominator before building: a ledger linking each planned move to an intent, line by line
D23-3-T45-ledger-traceability・技術・日本語入力でプレイする仕組み
「どこまで作ったか」が作り手の記憶にあるうちは、進み具合は分かりません。
企画の手を 1 行ずつ書き出した表を先に置き、作品データの意図と前後両方から突き合わせて、作った手・統合した手・捨てた手・まだの手を毎回コマンドで数えます。
T35 の clearpath は「クリアに要る手」を設計データから逆算しました。今日の台帳は、その手前の「どの手を作ると決めたか」を数える表です。
例は、霧走町ではなく、ファンタジーの 6 本から取ります。霧走町は台帳を持っていないからです(理由は後で書きます)。その 6 本の中身や台詞は写さず、仕組みと数だけを書きます。
この回の位置と、前後の回#
区切り: T34〜T49 確かめ方(16 回のうち 12 回目)
先に読むとよい回: T35(解ける道を逆算する)
次に読む回: T46(検査の目録を作品に置く)
名前 — requirements traceability matrix#
- 正式名
- requirements traceability matrix
- 略称
- RTM
- 日本語での呼び名
- 要求の追跡表、トレーサビリティマトリクス
- もとの概念
- requirements traceability(要求の追跡可能性)
- 初出
- requirements traceability matrix という語の初出は確かめられませんでした。
requirements traceability の定義として広く引かれるのは、Orlena C. Z. Gotel と Anthony C. W. Finkelstein の "An Analysis of the Requirements Traceability Problem"(First International Conference on Requirements Engineering, ICRE 1994, pp. 94–101)の、
the ability to describe and follow the life of a requirement, in both a forwards and backwards direction
です。要求が仕様に入る前の追跡(pre-RS traceability)と、入った後の追跡(post-RS traceability)を分け、追跡の問題の多くは前者から来る、と論じています(本文は読んでいません。書誌と要旨で確かめました)。
一般の解説 — requirements traceability matrix#
requirements traceability matrix(要求の追跡表)とは、行に要求、列に設計・実装・検査を置いて、どの要求がどこで作られ、どこで確かめられているかを結ぶ表です。
安全に関わる分野の開発では、規格や監査で求められることが多い成果物です。
二つの向きがあります。
- 前向き(forward traceability): 要求から実装・検査へ。「この要求は作られたか、確かめられたか」。たどれなければ、作り忘れ・確かめ忘れ
- 後ろ向き(backward traceability): 実装・検査から要求へ。「このコードは、どの要求のためにあるか」。たどれなければ、頼まれていない実装(orphan、孤児)
表から出る数も決まっています。
- 要求の被覆率: 実装された要求 / 全部の要求。分母は要求の行の数
- 検査の被覆率: 検査で確かめられた要求 / 全部の要求
- 孤児の数: どの要求にも結ばれていない実装・検査
よく知られた弱点もあります。
- 表を手で保守すると、すぐ古くなる。実装が変わっても表が変わらない
- 粒度がそろわない。1 行が 1 画面のこともあれば、1 つの条件のこともある
- 結んであることと、正しく作ったことは別。表は「どこにあるか」しか言わない
- 要求そのものの出どころ(pre-RS)は、表の外にある
うちの台帳 — requirements traceability matrix#
推理アドベンチャーの作品は、企画(Plots/<作品>/)から起こします。
企画の手(s_09_beats.json の beats。プレイヤーが体験する手の列)から、台帳を機械で起こします。
node Scripts/adv-ledger-from-beats.mjs --beats Plots/<作品>/s_09_beats.json --out Game/adv-<作品>/ledger.tsv
... --data Game/adv-<作品> 既に実装済みの id を「作った」で出す
... --merge 既存の台帳を残したまま、企画で増えた手だけ足す
頭の注記には、こうあります。
// **台帳は「作る前に分母を決める」表**(08 §11 手順1)。これが無いと、
// どこまで作ったのかが人の記憶になる。lint L35 が台帳と実データを突き合わせるので、
// 台帳を先に置くと「未着手が何本残っているか」が毎回コマンドで出る。
形はタブ区切りの 5 列です。
# 原典の手 動詞 対象 intent id 状態
— examine (対象の文) scene.xxx 作った
— ask (対象の文) person.yyy 作った
(対象の文と id は伏せました。)
- 原典の手: 手本の作品がある再現試作では、手本の手。原典を持たない作品は「—」
- 動詞: examine・ask・call など。手本の作品の動詞の手を写すときに使う
- 対象: 何をする手か(人が読む説明)
- intent id: その手を表す作品データの意図
- 状態: 作った・統合した・捨てた・未着手 の 4 つだけ
突き合わせ(lint の L35)#
台帳は、lint に --ledger で渡したときだけ検査します。台帳を持たない作品でも同じバイナリが通るように、ファイル名はコードに書きません。
前向きの検査は、行ごとに状態と id を見ます(写す。表示の文は一部を省きます)。
const bool bExists = !Id.empty() && Intents.findById(Id) != nullptr;
if (State == "作った" || State == "統合した")
{
// 統合した手は「統合先の intent」を指す。どちらも実在が要る
if (!bExists) { std::printf("L35: …状態=%s なのに intent が見つからない: '%s'\n", …); ++Errors; continue; }
InLedger.insert(Id);
if (State == "作った") { ++Made; }
}
else if (State == "未着手")
{
// **未着手には id を書いてよい。** 台帳は「作る前に分母を決める」表なので、
// これから使う id を先に置ける。ただし**実在していたら状態が嘘**——作ったなら「作った」に直す。
if (!Id.empty() && Intents.findById(Id) != nullptr) { … ++Errors; }
}
else if (State == "捨てた")
{
if (!Id.empty()) { std::printf("L35: …状態=捨てた なのに intent id が入っている: '%s'\n", …); ++Errors; }
}
else { std::printf("L35: …知らない状態 '%s'(作った/統合した/捨てた/未着手)\n", …); ++Errors; }
状態と実データが食い違う 3 通りを、全部エラーにしています。
- 作ったと書いたのに、意図が無い(作り忘れ。前向きに切れている)
- 未着手と書いたのに、意図がある(台帳が古い)
- 捨てたと書いたのに、id が入っている(どちらなのか分からない)
後ろ向きの検査は、作品データの意図を全部回します。
// 逆向き: 台帳に無い本筋の意図(雑談は原典の手ではないので対象外)
for (const kaede::adv::Intent& It : Intents.intents)
{
if (It.bSmalltalk) { continue; }
if (InLedger.count(It.id)) { continue; }
std::printf("L35: %s が台帳に無い(どの手を表しているのか宣言すること)\n", It.id.c_str());
++Errors;
++Orphan;
}
一般の解説の orphan です。雑談の意図は、企画の手ではないので数えません。
最後に 1 行の集計を出します。
L35: 台帳 %d 行 / 作った %d 手 / 台帳外の意図 %d 本 / 原典 %d 手(移動 %d / 移動以外 %d)
原典の手は、移動を含む数と含まない数を両方出します。手本の作品と手数を比べるとき、どちらで数えたかを取り違えやすく、実際に取り違えたことがあるからです。
今日の値 — requirements traceability matrix#
ファンタジーの 6 本に、それぞれの台帳を渡して lint を掛けました(adv-sim は 2026-10-27 のビルド)。
| 作品 | 起こした直後の台帳 | 今の台帳 | 作った | 台帳外の意図 |
|---|---|---|---|---|
| 1 本目 | 12 行・全部未着手 | 21 行 | 21 | 0 |
| 2 本目 | 12 行・全部未着手 | 47 行 | 47 | 0 |
| 3〜6 本目 | 各 12 行・全部未着手 | 各 12 行 | 各 12 | 0 |
起こした直後の台帳は、骨組みを起こす道具が作った写し(baseline/)にそのまま残っています。
1 本目と 2 本目は、その後に企画の手が増え(2 本目の台帳の頭には、全 3 章へ広げたときの日付が書いてあります)、分母が 12 から 21・47 に変わっています。分母が増えたことも、台帳の diff に出ます。
2 本目の意図は 57 本あり、台帳は 47 行です。差の 10 本は雑談の意図(kind が smalltalk)で、後ろ向きの検査の対象外です。台帳の行どうしで id の重なりはありません。
L35 は 6 本とも 0 件でした。6 本のうち 1 本は lint 全体で 15 件のエラーがありますが、どれも L35 ではない規則のものです。台帳が「全部作った」と言っていても、作ったものが正しいとは言っていない。一般の解説の弱点の 3 つ目そのものです。
手本の作品がある再現試作の台帳は、もう少し複雑です。
L35: 台帳 322 行 / 作った 203 手 / 台帳外の意図 0 本 / 原典 246 手(移動 76 / 移動以外 170)
322 行の内訳は、作った 203・統合した 30・捨てた 89 です。捨てた手を消さずに行として残すので、手本のどの手を、なぜ作らなかったかが表に残ります。
霧走町には台帳が無い#
霧走町(Game/adv)は台帳を持っていません。
企画から骨組みを起こす道具も台帳の方法も、霧走町を手で書いた後に、別の作品の再現試作のために作ったからです(L35 は 2026-08-16 から)。作品を作る手順書にも「手書きの既存作品。骨組みの写しは無い」とあります。
ところが、霧走町の検査の目録(runs/suite.json)には、こうあります。
"ledger": "ledger.tsv",
ファイルは、git の履歴にも一度もありません。目録を作品フォルダへ移した 2026-08-17 の commit から、この欄は無いファイルを指しています。
作品非依存の検証スイート(Scripts/adv-verify-work.ps1)は、目録から台帳の場所を読み、ファイルがあるときだけ lint に --ledger を渡します。
if ($Ledger -and (Test-Path $Ledger)) { $lintArgs += @('--ledger', $Ledger) }
…
$ledgerLine = $lint | Select-String -Pattern '^L35: 台帳'
if ($ledgerLine) { Check 'ledger' $ledgerLine.Line.Substring(5) }
elseif (-not $Ledger) { Skip 'ledger' '-Ledger を渡していない(台帳を持たない作品)' }
台帳の場所は空でないので SKIP の分岐にも入らず、ファイルが無いので PASS の行も出ません。何も言わずに次へ進みます。
霧走町は台帳を持たない作品として扱うのが正しいので、合否は変わりません。ただ、目録が無いファイルを宣言していて、それを誰も言わない、という形です。今日は直さず、事実だけを書いておきます。
名前どおりでない所 — requirements traceability matrix#
名前の対照表は、台帳を「要求 ↔ 実装 ↔ 検査の対応表そのもの」として ◎ にしています。
ソースを読むと、台帳にあるのは要求(企画の手)と実装(意図)の二項で、検査の列はありません。
どの手を、どの台本・どの期待(T37)・どの凍結した問い(T38)が確かめているかは、台帳では結んでいません。検査の側の被覆は、別の道具が別の単位で見ています(クリアに要る手は clearpath・T35、台本の着地は !expect・T37)。
なので、正確には「要求と実装の二項の、前後両方の追跡表」です。要求の被覆率(作った / 台帳の行)と孤児の数は出せますが、検査の被覆率は出せません。
もう一つ、要求の出どころ(Gotel と Finkelstein の言う pre-RS)は、台帳の外の企画のファイルにあります。台帳は企画から機械で起こすので、企画と台帳のずれは --merge で足した分しか追えません。
弱点と、採らなかった案 — requirements traceability matrix#
- 台帳が見るのは id の実在だけです。作った手が企画どおりに作られたか(場所・相手・前提)は見ません。それは企画から起こし直した作品と比べる差分テスト(T44)の仕事です
- 「統合した」は統合先の id を書くので、統合先の意図が 1 つで何本の手を受けているかは、台帳を読まないと分かりません
- 雑談の意図を後ろ向きの検査から外しているので、雑談の印を付けた本筋の意図は孤児になっても見えません
- 霧走町のように台帳を持たない作品では、分母はクリアに要る手(clearpath)と、作り手の頭の中にしかありません
採らなかった案:
- 検査の列を足して三項にする。検査の側は台本・凍結した問い・期待の行と単位が違い、1 行に収まらない。収めようとすると、表を手で保守することになり、すぐ古くなる(一般の解説の弱点の 1 つ目)
- 台帳を作品データから逆に起こす。作ってから書く表は、分母ではなく「作ったものの一覧」になる。作る前に置くから分母になる
- 霧走町に台帳を後から作る。作り終えた作品に後から付けた台帳は、上と同じ理由で分母にならない
検索語#
requirements traceability matrix
Gotel Finkelstein requirements traceability problem 1994
forward and backward traceability orphan requirements
requirements coverage traceability
『霧走町 流氷に眠る』の第一章は、ブラウザで無料で遊べます。プロフィールのリンクから。