pdf-trust — ドメイン別 PDF 信頼性監査
PDF family の trust 層を担う Skill。自前の検証ロジックは持たず、 検証は必ず MCP ツールの結果を根拠にする。構造解析からの推測で真正性を代替しないこと — 「本物か」に答えられるのは暗号学的検証だけであり、それは pdf-verify-mcp の仕事。
中核原則(family 共通):
- 内容の真偽は判定しない — 判定するのは真正性(原本性・完全性)のみ
- 検証結果は技術的事実として返し、解釈・最終判断は利用者に委ねる
- 判定の根拠(どのツールの何の結果か)を必ず明示する
- ジャッジはコード、ナラティブは LLM — 4 値判定は
evaluate_policy(pdf-verify-mcp v0.7.0+ の決定論的ルールエンジン)が下す。この Skill(LLM)の 仕事は firedRules の解説・推奨アクションの文章化・法令根拠の引用であり、 判定の上書きではない
前提 MCP
| MCP | 必須/任意 | 役割 |
|---|---|---|
| pdf-verify-mcp(v0.21.0+ 推奨) | 必須 | evaluate_policy による 4 値判定・署名検証・改ざん検知・PAdES レベル・PDF/A 検証・PDF/UA 検証(validate_conformance の flavour: "pdfua-1")。v0.10.0 で verify_integrity にリビジョン間のオブジェクト単位差分が入り、v0.11.0 で PDF/A-4(pdfa-4 / pdfa-4e / pdfa-4f)が受けられるようになった(下記「署名後の変更の特定」)。v0.15.0 で xref チェーンの歩き方が是正され、追えない /Prev を完全なチェーンとして飲み込まなくなった — 全履歴を約束する legal / medical ではこの版以降でないと報告書が書けない(下記 Phase 2.5 の 2)。v0.16.0 で verify_integrity が revisionChain を返すようになった — それまでは打ち切りが notes の英文にしか出ておらず、「全履歴を約束してよいか」を散文の照合で決めていた。v0.17.0 で revisionCountAgreement が返るようになった — revisionCount(startxref の個数)と revisions.length の食い違いに説明が付いているかをフィールドで読める(下記 Phase 2.5 の 2)。v0.7.0〜0.9 は差分が無いだけで verdict は同一(ルール表は不変。advisory は 0.7.1 / 0.8.0 で追加)。v0.7.0 未満は evaluate_policy が無くフォールバック手動判定に縮退。v0.20.0 で全ツールの報告の先頭に scope(判定の射程)が入った —— 相互参照表を組み直した文書かどうかがここでしか分からない(Phase 1.5)。v0.20.0 で、条文を名指しする拒否の code が INTERNAL_ERROR から PARSE_FAILED に変わった —— それ以前は所見と故障が同じ code で返っていた。v0.21.0 で verify_signatures と detect_pades_level の JSON の最上位が配列から辞書になった({ scope, signatures: [...] } / { scope, levels: [...] })。署名の一覧だけを読んでいると、一覧が不完全であることに気づけないため |
| pdf-reader-mcp(v0.10.0+ 推奨) | 任意(位置特定が要るなら実質必須) | 署名フィールド構造・メタデータ。v0.10.0 の locate_objects で「変わったオブジェクト」を「ページ + 矩形」に落とせる。※ PDF/UA 検証は verify の validate_conformance へ移管済み(reader の validate_tagged / validate_metadata は非推奨) |
| pdf-spec-mcp | 任意 | 逸脱時の ISO 32000 根拠引用 |
| houki-egov / houki-nta / tax-law / labor-law | 任意 | 法令根拠(プロファイルが指定) |
pdf-verify-mcp が未接続なら監査は成立しない。その旨を伝え、npx @shuji-bonji/pdf-verify-mcp
の接続を案内して停止する。任意 MCP が無い場合は縮退動作し、レポートの該当項目に
「未実施(ツール未接続)」と明記する — 黙って項目を落とすと「チェック済みで問題なし」と誤読される。
手順
Phase 0 — 目的とプロファイルの特定
ファイルパス(絶対パス)と利用目的を確認し、プロファイルを選ぶ。目的が不明瞭でも 文書種別から推定できることが多い。推定した場合はレポートに推定根拠を書く。
| プロファイル | 想定文書 | 参照 |
|---|---|---|
| contract | 契約書・NDA・発注書 | references/contract.md |
| financial | 請求書・決算書・申告書・領収書 | references/financial.md |
| legal | 訴訟資料・法務文書全般 | references/legal.md |
| medical | 診療情報提供書・検査報告書 | references/medical.md |
| government | 行政文書・公共告知 | references/government.md |
| general | 上記以外 | プロファイル追加チェックなし |
該当プロファイルの references ファイルをこの時点で読む(必須チェック・閾値・法令照合先が 書いてある)。あわせて次も確認する:
- trust_anchors(信頼する CA 証明書)を持っているか → あれば Phase 1 で渡す
- 長期保存が目的に含まれるか(「保存」「アーカイブ」「電帳法」「10年」等)→ Phase 3 の長期保存チェックを追加
- 暗号化 PDF でパスワードを知っているか →
passwordパラメータで渡す
Phase 1 — 基礎検証と判定(全ファイル一括)
全対象ファイルに対して pdf-verify-mcp: evaluate_policy を実行する(v0.7.0+)。
profile に Phase 0 で選んだプロファイルを渡し、trust_anchors・password があれば渡す。
check_revocation はプロファイル指定に従う(既定 embedded。online は外部への
HTTP アクセスを伴うため、プロファイルが要求する場合もユーザーに一言断ってから)。
最終判定(recommendation)は evaluate_policy の verdict をそのまま使う。
このツールは verify_signatures / verify_integrity / detect_pades_level(長期保存
プロファイルでは validate_conformance も)を内部で実行し、固定ルール表で決定論的に
4 値判定する。同じファイル・同じプロファイルなら常に同じ判定になる。
LLM(この Skill を実行しているあなた)が verdict を上書きすることは禁止 —
firedRules / advisories を「なぜこの判定になったか」の解説材料として使うこと。
文書の本文内容(契約金額・重要度など)を判定材料にしてはならない。
接続先の pdf-verify-mcp が古く evaluate_policy が無い場合のみ、旧手順 (verify_signatures + verify_integrity を個別実行し、後述のフォールバック判定表で 判定)に縮退し、レポートに「判定: 手動判定(evaluate_policy 未使用)」と明記する。
複数ファイルの一括監査では、まず evaluate_policy だけを全件に回して問題のある ファイル(reject / human_review_required)を特定し、Phase 2 以降の深掘りは 問題のあるファイルに絞る(全件深掘りは時間と文脈の無駄遣い)。
Phase 1.5 — 読んだ範囲を確かめる(verify v0.20.0+)
判定を読む前に scope を読む。 v0.20.0 から 5 本、v0.21.0 から 7 本すべての
ツールが報告の先頭に scope を返す。判定ではなく、判定の射程である。
見るのは 3 つ。
scope |
意味 | 報告に書くこと |
|---|---|---|
reconstructed: true |
🔴 相互参照表は verify が組み直したもので、ファイルが持っている表ではない | 必ず書く。「違反なし」も「署名 N 本」も、推測した表の上での話である。全履歴・全署名を約束しない |
chainStop.kind !== 'complete' |
チェーンを最後まで歩けていない(prev-zero / unreadable / cyclic / malformed) |
古いリビジョンが読めていない可能性を書く。verify_integrity の revisionChain と合わせて読む |
encrypted: true かつ authenticated: false |
鍵が導けず、オブジェクトを 1 つも読んでいない | 「調べた結果として問題なし」ではない。パスワードの入手を促す |
recovered: true は「ライブラリがそのまま読めず、verify が組み立てた」の意味で、
refusal にその理由(条文を名指しする文)が入る。refusal の文面で分岐しない
—— normativepdf の版が上がれば言い回しが変わる。分岐は recovered と
chainStop.kind で行う。
v0.19.0 以前には scope が無い。そのときは「射程は確認していない」と書く
(「射程に問題は無かった」ではない)。
Phase 2 — 結果の解釈
判定は Phase 1 の evaluate_policy が済ませている。この Phase の仕事は
firedRules の各ルールを人間に説明できるようにすること。深掘りが必要なときは
verify_signatures / verify_integrity を個別に呼んで詳細(cms.error・notes・
certificatePath 等)を取得する。解釈の背景知識として次の表を使う:
| 観測(firedRules / facts) | 意味 | 解説・推奨アクションに書くこと |
|---|---|---|
| POL-CAUTION-TRUST-NOT-EVALUATED | 暗号学的完全性のみ確認。署名者の身元は未評価 | 必ずその旨を明記。trust_anchors の提供を促す |
| (何も発火せず trust_and_use) | 完全性 + 署名者身元 + 失効とも確認 | 最良の状態 |
| POL-CAUTION-TRUST-UNTRUSTED | 署名は有効だがチェーンがアンカーに到達しない | 中間 CA 不足か、アンカー相違。trust.detail を確認 |
| POL-REJECT-INVALID / POL-REJECT-REVOKED | ダイジェスト不一致・署名検証失敗・失効 | 原因を verify_signatures の notes で特定して解説 |
| POL-REVIEW-INDETERMINATE | 未対応形式 or 検証未完了 | 下記の切り分けへ |
| POL-REVIEW-UNSIGNED-REQUIRED / POL-CAUTION-UNSIGNED | 真正性の技術的裏付けなし | 入手経路など他の補強手段を提案 |
| POL-CAUTION-REVOCATION-UNKNOWN | 失効情報が確認できなかった | 「失効していない」とは言えない。online 再試行を検討 |
🔴 ツールが isError を返したとき、それが「調べられなかった」とは限らない。
code: "PARSE_FAILED" で、message が条文を名指ししている(§7.5.4 など)なら、
それは 文書についての所見である —— その文書は ISO 32000 の構造条文に反していて、
書かれたとおりには読めない。suggestion にもそう書いてある
("This is a finding about the file, not a failure of this server")。
これを「未実施項目(ツール未接続・取得失敗)」に入れてはいけない。 入れると、条文違反が「調べられませんでした」として報告され、 壊れた文書ほど無罪になる。 根拠の表に「構造が条文に反していて読めない (§X.Y.Z)」と書き、recommendation に反映する。
code: "INTERNAL_ERROR" は本当にサーバ側で落ちたときだけ。こちらは未実施項目でよい。
(v0.19.0 以前は条文の拒否も INTERNAL_ERROR で返っていた。message に § が
含まれているかで見分ける。)
indeterminate の切り分け: cms の error / notes を読む → SubFilter 未対応
(adbe.pkcs7.sha1 等)か、CMS パース失敗か、暗号化で復号できないか。pdf-reader-mcp が
あれば inspect_signatures で構造側から確認する。
知っておくべき実挙動(実測に基づく):
- 自己署名のリーフ証明書そのものを trust_anchors に渡すと、チェーンエンジンは 「not a CA certificate」で untrusted にする(直接信頼リーフは非対応)。CA 証明書を渡すこと
- untrusted のメッセージは原因を区別できる: 「not a CA certificate」= 渡したアンカーが CA でない、「No valid certificate paths found」= アンカーが署名者チェーンと無関係。 後者は正しい CA 証明書の入手を促す
- indeterminate + 「CMS payload is not valid BER/DER」+ Embedded certificates: 0 は、 改ざんではなく署名生成ツール側の不備(証明書の埋め込み漏れ等)のサイン。 改ざんと断定せず「検証不能な署名」として human_review_required に送る
- 署名済み PDF を後から暗号化・再保存すると署名は壊れる(INVALID は改ざんとは限らない — 再保存の痕跡かもしれない。verify_integrity の増分更新情報と突き合わせる)
- 増分更新は合法(DSS 追加・連署など)。「署名後に変更あり」= 改ざんではなく、 DocMDP 違反や digest 不一致と組み合わせて判断する(連署と本文書き換えの読み分けは Phase 2.5)
- linearized PDF は
Revisions:の数字が +1 に見える — 線形化(ISO 32000-2 Annex F)は 1 回の保存で xref を 2 つ持つため。verify が併合するのはrevisions[]の側だけで、revisionCount/incrementalUpdateCountは今もstartxrefの個数を数えている。 v0.17.0+ は食い違いに説明が付いているかをrevisionCountAgreementで返すので、 数字を引用する前にそれを読む(読み方と旧版の退避は Phase 2.5 の 2)
Phase 2.5 — 署名後の変更の特定(verify v0.10.0+)
「署名後に N バイト足された」を「何が足されたか」に上げる。Phase 1 の verdict は動かさない — 増分更新は PDF で合法(ISO 32000-2 §7.5.6)なので、これは見るべき場所の特定であって 改ざんの証明ではない。
pdf-verify-mcp: verify_integrityのrevisions[]を読む。各リビジョンのchanges[]にchange(added / modified / freed)・type/subtype・role(人間向けの役割)・bookkeeping・inObjectStreamが付く。objectChangesAfterLastSignatureは「最後の署名以降」だけのショートリストなので、 連署文書(署名 1 と署名 2 の間に足されたもの)や legal / medical の「全履歴」要求には足りない。 件数を数値で引用するならresponse_format: "json"— markdown にはchangeCount/changesTruncatedが出ない🔴
revisionChain.statusを読む。revisions[]が返ったことは「歩き切れた」ことを意味しない。 チェーンが途中で切れた場合も、最新セクションが読めず古い入口から入った場合も、 リストは短いまま返る。しかもそのとき残った 1 件は「元版」扱いになるので、changeCount: 0/changes: null/objectChangesAfterLastSignature: []になり、 機械が読めるフィールドは全部「何も足されていない」と言う。revisionChain.status書いてよいこと complete「全履歴」。ここだけ「一覧に出てこない = 行われていない」と書ける partial「辿れた範囲の履歴」。 missingが'oldest'(元版側)/'newest'(最後の追記)を名指すunwalkable「未確認」。 revisionsは null。「変更なし」ではない🔴
revisionCountを「保存回数」として引用する前にrevisionCountAgreementを読む(v0.17.0+)。revisionCountはstartxrefの個数、revisions.lengthはチェーンが到達した xref セクション数で、 2 つは合法に食い違う。このフィールドは食い違いに説明が付いているかを言う。revisionCountAgreement意味 書いてよいこと status: "agree"2 つの数が一致 数をそのまま引用できる status: "accounted"+causes: ["linearised"]線形化(1 回の保存が xref を 2 つ持つ)。1 リビジョンに併合済み 「startxref は N 個だが、線形化のため保存は N−1 回」 status: "accounted"+causes: ["chain-incomplete"]チェーンが全セクションに到達していない。どちらの端が欠けたかは revisionChain.missing(ここでは繰り返されない)数の差を打ち切りの帰結として報告する status: "unaccounted"歩き切っていて線形化でもないのに数が合わない = 歩いたチェーンが到達しない startxrefがファイルにある「差分未説明」として human_review 対象に含める 版による退避はここだけに置く(各プロファイルの文書では繰り返さない):
- v0.17.0 未満 —
revisionCountAgreementは無い。食い違いの説明は notes の英文 1 文だけで、 その文は原因を 2 つ並べてどちらなのかを言っていない。数字ではなく notes とrevisions[]を読む - v0.16.0 未満 —
revisionChainは無い。notesに 「chain ended before reaching the original revision」(=missing: 'oldest')または 「last "startxref" does not point at a parseable cross-reference section」 (=missing: 'newest')が出ていないかを見る。「歩き切れた」と言う note は無いので、 完全性はその 2 文が無いことでしか表せない - v0.15.0 未満 — 追えない
/Prev 0を「前のセクションは無い」として飲み込み、 打ち切りを立てずに完全なチェーンとして返していた(8 リビジョン 5 署名の実検体が 「1 リビジョン」として報告された)。全履歴を要求するプロファイル(legal / medical)は この版では成立しない。版数を上げてから測り直す
- v0.17.0 未満 —
reader があれば
pdf-reader-mcp: locate_objectsにそのオブジェクト番号を渡し、locations[]を得る。矩形は PDF 座標系・左下原点・pt・正規化済み(= writeradd_annotationが そのまま取る形)。1 オブジェクトが複数ページに載ることがあり、pageもrectも null になりうるレポートには
basisを必ず転記する。4 値の意味:annotation-rect(注釈自身の/Rect= 正確)/page-box(ページの箱 = ページ全体)/page-content-stream(ページ全体。変更箇所ではない)/page-resource(矩形は存在しない)
「無い」と「分からない」を混ぜないための区別。どれも空・不在を根拠にしてはいけない:
| 出てくるもの | 意味 | 書いてはいけないこと |
|---|---|---|
revisionChain.status: "unwalkable"(revisions: null と対で立つ) |
xref チェーンを辿れなかった | 「変更なし」「差分なし」 |
revisionChain: { status: "partial", missing: ["oldest"] } |
チェーンが途中で切れた(壊れた / 巡回する /Prev・リビジョン上限)。revisions[] は返るがそのファイルの全リビジョンではない |
リストの件数を「このファイルのリビジョン数」として報告する |
revisionChain: { status: "partial", missing: ["newest"] } |
最新セクションが読めず、古い入口から入った。最後に足されたものはリストに載っていない | 一覧を「署名後の変更の全部」として報告する |
revisionChain.missing: [] |
status が complete のときだけ空になる。unwalkable では ["oldest","newest"] が入る |
空を見て「何も欠けていない」と読む(status を先に見る) |
revisionCountAgreement.status: "unaccounted" |
数の食い違いにファイルから読めた説明が付かない(線形化でも打ち切りでもない) | 「合法な食い違い」「誤差」として流す |
objectChangesAfterLastSignature: [] |
revisions: null のときも、打ち切りで 1 件だけ残ったときも空になる。空 ≠ 署名後に何も書かれていない |
空を「署名後の変更なし」と読む |
changesTruncated: true |
一覧は 25 件/リビジョンで打ち切り。真の総数は同じリビジョンの changeCount(revisions[] 側のフィールド) |
列挙した件数を全件として報告する |
inObjectStream: true |
オブジェクトストリームの中にあるので型が読めていない(type / role は null) |
「型なし」を性質として報告する |
basis: "page-content-stream" / "page-box" |
矩形はページ全体 | 矩形を「変更された領域」として示す |
locate_objects の found: false |
そのオブジェクトは今の文書に存在しない(後のリビジョンで free された = 想定内) | 「異常」「エラー」 |
bookkeeping: true(xref ストリーム・オブジェクトストリーム)はどの保存でも書き換わるので、
本文の変更と並べて数えない。
Phase 3 — プロファイル別チェック
references/.md の必須チェックのうち、evaluate_policy が扱わないもの (署名時刻の突き合わせ、PDF/UA 検証など)を実行する。増分更新履歴の詳述は Phase 2.5 で済んでいる (legal / medical プロファイルが要求する「全履歴」「署名後変更の全注記」はそこが担う)。 PAdES レベルと PDF/A 適合は evaluate_policy が facts / advisories に出している (financial / government プロファイルでは PDF/A 検証込み)。深掘りが必要なら:
pdf-verify-mcp: detect_pades_level— 構造が B-LT / B-LTA に一致しなければ、証明書失効後に 検証不能になるリスクを警告(LTV データの実在検証込みなので「宣言だけの B-LT」も検出される)。 返り値のnormativeBasisは常に"T3"— レベルは観測であって適合判定ではないので、 レポートには「構造が一致する」と書く(上記「報告するときの言い方」)pdf-verify-mcp: validate_conformance— PDF/A 適合。engine は auto のまま (veraPDF があれば権威的結果、なければ内蔵サブセット。どちらだったかをレポートに書く — native の「違反なし」は認証ではない)。 flavour は文書の自称に合わせる(identify_conformanceが返す part をそのまま使う)。 part 4 の文書はpdfa-4、pdfaid:conformanceがFならpdfa-4f。 -4 はpdfa-4bという flavour が存在しない(conformance level を持たない規格)ので、 -3b の癖で4bと書かない。v0.11.0 未満の verify は -4 をINVALID_FLAVOURで弾く — その場合は「この環境では PDF/A-4 を採点できない」とレポートに書く(黙って -3b で測らない)。 添付を持つ -4 文書を素のpdfa-4で測ると6.9-3(添付自身が PDF/A でない)で落ちるが、 それは文書の欠陥ではなくこちらの flavour 選択の誤りでありうる。自称が-4fなら-4fで測る
Phase 4 — 法令根拠(プロファイル指定時)
プロファイルが法令照合を指定する場合、houki 系 MCP で根拠条文を取得する。 houki 系 MCP のサーバ指示(原文引用・出典 URL 明記)に従うこと。取得できなければ 「法令根拠: 未取得」と明記(勝手に条文番号を記憶から書かない)。
報告するときの言い方 — 規範の 3 層(T1 / T2 / T3)
同じレポートの中で、項目ごとに言える強さが違う。 正典は specs/09-family-scope.md §2。
verify の出力は normativeBasis フィールドと注記でこれを示してくるので、それに従って語彙を選ぶ。
| 層 | 対象 | 書いてよい | 書いてはいけない |
|---|---|---|---|
| T1 | ISO 32000(署名構造・増分更新)・ISO 14289(PDF/UA) | 条文を引用して断定できる | — |
| T2 | PDF/A(ISO 19005) | 「veraPDF が COMPLIANT と判定」 | 「ISO 19005-3 準拠」 |
| T3 | PAdES(ETSI EN 319 142) | 「構造が B-LT に一致する」「LTV データが署名者証明書を覆っている」 | 「PAdES B-LT に適合」 |
T3 が特殊なのは、判定者すら居ないこと。 T2 には veraPDF という第三者検証器があるので 「誰が判定したか」を名指しできるが、PAdES には規範も検証器も無く、構造を観測しているのは family 自身である。だから「veraPDF はこう言った」のような逃げ方ができない — 「これは観測であって適合判定ではない」と述べるしかない。
detect_pades_level は各レポートに normativeBasis: "T3" を返し、markdown では
レベルより前に注記を置く。その注記をレポートへ運ぶこと(数字だけ抜き出さない)。
なぜここまで言うか: 受入監査のレポートは、相手に「この文書は PAdES B-LT 準拠です」と 伝えるために使われる。適合の刻印は、押した者が責任を負う。 ETSI の原文を読んでいないのに 適合と書けば、それは実測していないことを実測したかのように述べたことになる。
pdf-writer-mcpが「ラベルは書けるが、規格どおりにはできない」と言うのと、向きが逆の同じ話。
Phase 5 — Trust Report 生成
以下のテンプレートで報告する。複数ファイルの場合は冒頭にサマリ表を置き、 問題のあるファイルのみ個票を付ける。
# PDF 信頼性監査レポート
- 対象: <ファイル名(複数なら件数)> / プロファイル: <profile>(<選定根拠>)
- 実施日時・使用ツール: <MCP 名とバージョン情報が得られれば>
## 判定: <evaluate_policy の verdict をそのまま>
<1〜3 行の要約。発火ルールがなぜ発火したかの平易な説明>
## 根拠
| 検査 | 結果 | 根拠ツール | 規範根拠 |
|---|---|---|---|
| **読んだ範囲** | <scope.reconstructed が true なら「相互参照表は verify が組み直した推測」。chainStop が complete でなければその旨。両方なければ「ファイルの相互参照表どおりに読めた」> | すべてのツールの `scope` | — |
| 総合判定(発火ルール: <POL-... の列挙>) | ... | evaluate_policy | — |
| 署名の暗号学的有効性 | ... | evaluate_policy (facts) / verify_signatures | — |
| 署名者の身元(信頼チェーン) | ... | verify_signatures (trust) | — |
| 失効確認 | ... | verify_signatures (revocation) | — |
| 署名後の変更 | ... | verify_integrity | T1(ISO 32000 §14.4 / §12.8) |
| PAdES レベル | **構造が <B-T 等> に一致** | detect_pades_level | **T3(観測。ETSI 原文は照合していない)** |
| PDF/A 適合 | **veraPDF が <COMPLIANT 等> と判定** | validate_conformance | **T2(判定者は veraPDF)** |
| <プロファイル別項目> | ... | ... | ... |
<Phase 2.5 を実施したときのみ、次の節を出す。
実施しなかった / できなかったなら、節ごと出さずに理由を「警告・制限事項」に書く —
空の節を置くと「調べて何も無かった」と読まれる>
### 署名後に書かれたオブジェクト
| リビジョン | オブジェクト | 変更 | 役割 | ページ | 矩形 | 根拠 |
|---|---|---|---|---|---|---|
| 3 | 27 | added | annotation (Highlight) | 1 | 72, 700, 300, 720 | annotation-rect |
| 3 | 6 | modified | stream (content, image or embedded data) | 1 | ページ全体 | page-content-stream |
増分更新は PDF で合法(ISO 32000-2 §7.5.6)。**この表は「見るべき場所」であって改ざんの証明ではない。**
`basis` 列は必ず残す — `page-content-stream` と `page-box` の矩形はページ全体であって変更箇所ではない。
`changesTruncated` なら「全 <changeCount> 件のうち <n> 件を表示」と明記する(件数は json 出力で取る)。
## 警告・制限事項
- <trust_anchors 未指定なら必ず: 「valid は暗号学的完全性のみの主張であり署名者の身元は未評価」>
- <PAdES に言及したなら必ず: 「PAdES レベルは構造からの観測であり、ETSI EN 319 142 の原文照合ではない」>
- <PDF/A に言及したなら必ず: 「判定者は veraPDF。ISO 19005 の条文は確認していない」>
- <scope.reconstructed が true なら必ず: 「相互参照表は verify が組み直したもので、ファイルが持っているものではない。以下の判定はその表の上での話」>
- <未実施項目(ツール未接続・取得失敗)。**条文を名指しする PARSE_FAILED はここに入れない** —— それは所見であって未実施ではない>
## 推奨アクション
- <trust_anchors の入手、online 失効確認、LTV 化、人手レビュー対象箇所など>
recommendation の判定
判定は evaluate_policy の verdict をそのまま使う。LLM による上書き禁止。
判定表・プロファイル上書き(medical の格上げ等)はすべて verify 側の
ルールエンジン(services/policy-engine.ts)にコード化されており、この Skill 側で
再現・変更しない。firedRules に無い理由で判定を変えたくなったら、それは
ルールエンジンの改善要望として pdf-verify-mcp に issue を立てる。
フォールバック判定表(evaluate_policy が無い旧バージョンに接続した場合のみ):
| 判定 | 条件 |
|---|---|
| trust_and_use | valid + trusted + 失効なし(good / 失効情報が embedded で確認)+ プロファイル必須チェック全通過 |
| use_with_caution | valid だが trust: not_evaluated / untrusted、または revocation: unknown、または任意チェックに軽微な指摘 |
| human_review_required | indeterminate、DocMDP 違反疑い、プロファイル必須チェックの不合格、署名なし(署名必須プロファイル) |
| reject | invalid(digest 不一致・署名検証失敗・失効確認済み) |
フォールバック時もプロファイルの references の上書き条件(例: medical は use_with_caution を human_review_required に格上げ)を優先し、レポートに 「判定: 手動判定(evaluate_policy 未使用)」と明記する。
やらないこと
- 内容の真偽・法的有効性の最終判断(技術的事実と法令根拠の提示まで)
- evaluate_policy の verdict の上書き(本文内容・重要度・文脈を判定材料にしない)
- 構造解析(inspect_signatures 等)だけを根拠にした「有効/無効」の判断
- 記憶からの条文引用(必ず houki 系 MCP で原文を取得)