✓=レビュー済 ○=未レビュー ⚠=Suspect(複数同時表示あり。IDクリックで詳細へ)
| グループ | REQ | ARCH | SPEC | TST | IMPL | ADR |
|---|---|---|---|---|---|---|
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC004 ✓ ## インターフェース ```python class KeyGen: @staticmethod def _default(args: tu... | TST004 ✓ ## 目的 キャッシュキーはキャッシュの同一性判定の基盤であり、ハッシュが不安定だとキャッシュミスの頻発(性能劣化)や誤ヒット(データ不整合)を引き起こす。P... | IMPL004 ✓ ## 実装概要 `KeyGen` クラスの静的メソッド群がキャッシュキー生成を担う。 `_default(args, kwargs)` が主要なエントリポイン... | ADR002 ✓ # Stable Hashing for Function Arguments ## Context and Problem Statement / コンテキ... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC005 ✓ ## インターフェース ```python @singledispatch def canonicalize(obj: Any) -> Any: ... ``... | TST005 ✓ ## 目的 `canonicalize()` は引数の型ごとに正規化ルールを適用し、論理的に同値な入力が同じキャッシュキーを生成することを保証する。正規化の不... | IMPL005 ✓ ## 実装概要 `canonicalize()` は `functools.singledispatch` で実装された再帰的正規化関数。 型ごとにディスパッ... | ADR002 ✓ # Stable Hashing for Function Arguments ## Context and Problem Statement / コンテキ... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC006 ✓ ## インターフェース ```python class Strategy(Enum): DEFAULT = auto() # 再帰的正規化 ... | TST006 ✓ ## 目的 `KeyGenPolicy` は引数ごとのキー生成戦略をカスタマイズする機構であり、ログレベルやデバッグフラグのような非決定的引数をキーから除外し... | IMPL006 ✓ ## 実装概要 `Strategy` enum(DEFAULT, IGNORE, FILE_CONTENT, PATH_STAT の4種)と `KeyGenP... | ADR002 ✓ # Stable Hashing for Function Arguments ## Context and Problem Statement / コンテキ... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC004 ✓ ## インターフェース ```python class KeyGen: @staticmethod def _default(args: tu... | TST004 ✓ ## 目的 キャッシュキーはキャッシュの同一性判定の基盤であり、ハッシュが不安定だとキャッシュミスの頻発(性能劣化)や誤ヒット(データ不整合)を引き起こす。P... | IMPL004 ✓ ## 実装概要 `KeyGen` クラスの静的メソッド群がキャッシュキー生成を担う。 `_default(args, kwargs)` が主要なエントリポイン... | ADR011 ✓ # Canonical Input Serialization with Msgpack and SHA-256 ## Context and Problem... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC004 ✓ ## インターフェース ```python class KeyGen: @staticmethod def _default(args: tu... | TST004 ✓ ## 目的 キャッシュキーはキャッシュの同一性判定の基盤であり、ハッシュが不安定だとキャッシュミスの頻発(性能劣化)や誤ヒット(データ不整合)を引き起こす。P... | IMPL004 ✓ ## 実装概要 `KeyGen` クラスの静的メソッド群がキャッシュキー生成を担う。 `_default(args, kwargs)` が主要なエントリポイン... | ADR022 ✓ # Refactoring Complex Modules with Single Dispatch ## Context and Problem State... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC005 ✓ ## インターフェース ```python @singledispatch def canonicalize(obj: Any) -> Any: ... ``... | TST005 ✓ ## 目的 `canonicalize()` は引数の型ごとに正規化ルールを適用し、論理的に同値な入力が同じキャッシュキーを生成することを保証する。正規化の不... | IMPL005 ✓ ## 実装概要 `canonicalize()` は `functools.singledispatch` で実装された再帰的正規化関数。 型ごとにディスパッ... | ADR011 ✓ # Canonical Input Serialization with Msgpack and SHA-256 ## Context and Problem... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC005 ✓ ## インターフェース ```python @singledispatch def canonicalize(obj: Any) -> Any: ... ``... | TST005 ✓ ## 目的 `canonicalize()` は引数の型ごとに正規化ルールを適用し、論理的に同値な入力が同じキャッシュキーを生成することを保証する。正規化の不... | IMPL005 ✓ ## 実装概要 `canonicalize()` は `functools.singledispatch` で実装された再帰的正規化関数。 型ごとにディスパッ... | ADR022 ✓ # Refactoring Complex Modules with Single Dispatch ## Context and Problem State... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC006 ✓ ## インターフェース ```python class Strategy(Enum): DEFAULT = auto() # 再帰的正規化 ... | TST006 ✓ ## 目的 `KeyGenPolicy` は引数ごとのキー生成戦略をカスタマイズする機構であり、ログレベルやデバッグフラグのような非決定的引数をキーから除外し... | IMPL006 ✓ ## 実装概要 `Strategy` enum(DEFAULT, IGNORE, FILE_CONTENT, PATH_STAT の4種)と `KeyGenP... | ADR011 ✓ # Canonical Input Serialization with Msgpack and SHA-256 ## Context and Problem... |
| KEY | REQ002 ✓ 関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。 | ARCH002 ✓ ## コンポーネント構成 ```mermaid graph TD A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy... | SPEC006 ✓ ## インターフェース ```python class Strategy(Enum): DEFAULT = auto() # 再帰的正規化 ... | TST006 ✓ ## 目的 `KeyGenPolicy` は引数ごとのキー生成戦略をカスタマイズする機構であり、ログレベルやデバッグフラグのような非決定的引数をキーから除外し... | IMPL006 ✓ ## 実装概要 `Strategy` enum(DEFAULT, IGNORE, FILE_CONTENT, PATH_STAT の4種)と `KeyGenP... | ADR022 ✓ # Refactoring Complex Modules with Single Dispatch ## Context and Problem State... |
| リンク方向 | カバー数 | カバー率 | 未カバー |
|---|---|---|---|
| ARCH → REQ | 1 / 1 | 100.0% | — |
| SPEC → ARCH | 1 / 1 | 100.0% | — |
| TST → SPEC | 3 / 3 | 100.0% | — |
| IMPL → SPEC | 3 / 3 | 100.0% | — |
| ADR → REQ | 1 / 1 | 100.0% | — |
関数の引数から安定かつ一意なキャッシュキーを生成できること。引数の型・構造に応じた正規化を行い、辞書のキー順序やコレクション型の違いを正しく区別できること。
親: —
子: ADR002, ADR011, ADR022, ARCH002
graph TD
A[core.Spot] -->|キャッシュキー生成要求| B[KeyGenPolicy]
B -->|バインド| C[bound_keygen]
C -->|引数別戦略| D[Strategy]
C -->|正規化| E[canonicalize]
C -->|ハッシュ計算| F[KeyGen.hash_items]
E --> F
| コンポーネント | 責務 | インターフェース |
|---|---|---|
KeyGenPolicy |
関数の引数に対するハッシュ化戦略(無視、ファイル内容等)の宣言的定義と保持。 | bind(func) |
Strategy |
引数ごとの処理方法(DEFAULT, IGNORE, FILE_CONTENT, PATH_STAT)の定義。 | - |
canonicalize |
Pythonオブジェクトを再帰的に正規化し、Msgpackで安定してシリアライズ可能な状態に変換。 | @singledispatch canonicalize(obj) |
KeyGen |
正規化されたリストからSHA-256ハッシュを生成。レガシーデフォルトのハッシュ生成も担う。 | hash_items(items), from_file_content(path) |
sequenceDiagram
participant Spot as core.Spot
participant KP as KeyGenPolicy
participant Sig as inspect.signature
participant Can as canonicalize
participant KG as KeyGen
Spot->>KP: bind(func)
KP->>Sig: signature(func)
Sig-->>KP: bound_keygen 生成
KP-->>Spot: bound_keygen
Spot->>Spot: bound_keygen(*args, **kwargs)
loop 各引数
alt Strategy.IGNORE
Spot->>Spot: スキップ
else Strategy.FILE_CONTENT
Spot->>KG: from_file_content(path)
else Strategy.PATH_STAT
Spot->>KG: from_path_stat(path)
else Strategy.DEFAULT
Spot->>Can: canonicalize(val)
Can-->>Spot: 正規化済みデータ
end
end
Spot->>KG: hash_items(items_to_hash)
KG-->>Spot: SHA-256 ハッシュ文字列 (キャッシュキー)
| 技術領域 | 選定 | 理由 |
|---|---|---|
| 引数のバインド | inspect.signature |
argsとkwargsを定義順に正確にマッピングし、デフォルト値も適用するため |
| 正規化 | functools.singledispatch |
型ごとの正規化ロジックを拡張可能かつクリーンに実装するため |
| シリアライズ | msgpack |
高速でバイナリセーフであり、一貫したバイト列表現を得るため |
| ハッシュ関数 | SHA-256 | 衝突確率が極めて低く、暗号学的に安全かつ広くサポートされているため |
KeyGenPolicy を定義可能にし、柔軟なキャッシュ戦略をサポートする。親: REQ002
class KeyGen:
@staticmethod
def _default(args: tuple, kwargs: dict) -> str: ...
@staticmethod
def hash_items(items: list) -> str: ...
_default)args と kwargs を canonicalize() により再帰的に正規化する[args, kwargs] を MessagePack でバイナリにシリアライズするhash_items)KeyGenPolicy で使用される。| パラメータ | 型 | 説明 |
|---|---|---|
args |
tuple |
関数の位置引数 |
kwargs |
dict |
関数のキーワード引数 |
items |
list |
バインド済み引数の正規化値リスト |
RecursionError 発生時は、安全のため str((args, kwargs)) のハッシュをフォールバックとして使用するstr() ベースのハッシュを返す[(), {}] の構造として一定のハッシュ値を生成するstr() はオブジェクトによっては実行ごとに変わる可能性があるため、明示的な正規化が推奨される親: ARCH002
@singledispatch
def canonicalize(obj: Any) -> Any: ...
singledispatch を使用し、型に応じた最適な正規化形式へ再帰的に変換する。
("__bool__", value) のタプルに変換(1 と True の衝突を防止)[[k1, v1], [k2, v2], ...] のリストに変換"__list__") を付与し、要素を再帰的に正規化。集合型はソートを行う("__enum__", module, qualname, value) に変換__dict__ または __slots__ から属性を取得し、型名と共に正規化| オブジェクト型 | 正規化後の形式 | 備考 |
|---|---|---|
numpy.ndarray |
("__numpy__", shape, dtype, bytes) |
高速かつ正確なハッシュを保証 |
OrderedDict |
("__ordered_dict__", items) |
挿入順序を保持したまま正規化 |
Pydantic Model |
("__pydantic_v2__", schema) |
スキーマ情報をベースに判定 |
str(obj) にフォールバックするRecursionError をキャッチし、その階層で str() による正規化に切り替える_safe_sort_key により、Python 3 で比較不可能な型同士(例: int と str)が辞書のキーに含まれていても安定したソート順を保証する親: ARCH002
class Strategy(Enum):
DEFAULT = auto() # 再帰的正規化
IGNORE = auto() # キー計算から除外
FILE_CONTENT = auto() # ファイルの内容をハッシュ
PATH_STAT = auto() # パス+サイズ+更新時刻をハッシュ
class KeyGenPolicy:
def bind(self, func: Callable) -> Callable[..., str]: ...
bind(func) 時に inspect.signature を解析し、引数名とデフォルト値を把握するStrategy を適用し、正規化された値のリストを作成するKeyGen.hash_items() で一つのハッシュにまとめる| 戦略 | 内容 | 用途 |
|---|---|---|
IGNORE |
キャッシュキーに含めない | verbose や logger 等、結果に影響しない引数 |
FILE_CONTENT |
ファイルを全読込してハッシュ化 | 入力ファイルの厳密な変更検知 |
PATH_STAT |
os.stat 情報を使用 |
大容量ファイルの高速な変更検知 |
FILE_CONTENT / PATH_STAT 指定時にファイルがない場合、"MISSING_{path}" という固定文字列をハッシュ対象とする"ERROR_{path}" を返すTypeError を送出する(実行前バリデーション)親: ARCH002
キャッシュキーはキャッシュの同一性判定の基盤であり、ハッシュが不安定だとキャッシュミスの頻発(性能劣化)や誤ヒット(データ不整合)を引き起こす。Python バージョンやプラットフォームに依存しないキー生成の安定性を保証する。
func_identifier:input_id[:version] 入力に対して、複数回の呼び出しで常に同じ SHA-256 ハッシュが生成されることmodule.qualname 形式の func_identifier を含む正しい構造であることreferences: tests/unit/test_cachekey.py
親: SPEC004
子: —
canonicalize() は引数の型ごとに正規化ルールを適用し、論理的に同値な入力が同じキャッシュキーを生成することを保証する。正規化の不備は「同じ入力なのにキャッシュミス」または「異なる入力なのに誤ヒット」という致命的なバグに直結する。
{"a": 1, "b": 2} と {"b": 2, "a": 1} が同一キーを生成すること[1, 2] (list) と (1, 2) (tuple) が異なるキーを生成すること(型タグ付き正規化)型名.メンバー名 で正規化されることshape + dtype + tobytes による正規化で、同値配列が同一キーを生成することTrue と 1 が異なるキーを生成すること(Python では True == 1 だが区別が必要)references: tests/unit/test_cachekey.py
親: SPEC005
子: —
KeyGenPolicy は引数ごとのキー生成戦略をカスタマイズする機構であり、ログレベルやデバッグフラグのような非決定的引数をキーから除外したり、ファイルパスの代わりにファイル内容でキーを生成するユースケースを支える。戦略の誤適用はキャッシュの正確性を根本から損なう。
KeyGenPolicy.bind(func) が関数シグネチャ(位置引数・キーワード引数・デフォルト値)を正しく解釈すること@spot.mark(keygen=KeyGen.ignore("verbose")) のようにデコレータ経由で戦略が正しく適用されることreferences: tests/unit/test_cachekey.py
親: SPEC006
子: —
KeyGen クラスの静的メソッド群がキャッシュキー生成を担う。
_default(args, kwargs) が主要なエントリポイントで、引数を
canonicalize() で正規化 → msgpack でシリアライズ → SHA-256 でハッシュする。
最終キーは func_identifier:input_id[:version] 形式の文字列を SHA-256 した値。
MD5 より衝突耐性が高く、Python 標準ライブラリの hashlib で利用可能。
キャッシュキーとして64文字の hex 文字列は十分にコンパクトで、
DB のインデックスとしても効率的。
正規化結果を直接 str() でハッシュする方式と比較し、
msgpack はバイナリ表現が安定しており、Python バージョン間での
repr() の差異に影響されない。
from_file_content() はファイルを 65KB チャンクで読み取り、
拡張子をハッシュに含める。大きなファイルでもメモリ効率が良い。
from_path_stat() は mtime + size のみでハッシュし、
ファイル内容の読み取りを避ける高速版。ファイル不在時は
"MISSING_{filepath}" を返し、不在自体をキーに反映する。
hash_items() は汎用リストハッシュ。内部でも _default でも使われるreferences: src/beautyspot/cachekey.py
親: SPEC004
子: —
canonicalize() は functools.singledispatch で実装された再帰的正規化関数。
型ごとにディスパッチハンドラを登録し、ネストされたデータ構造を再帰的に正規化する。
各ハンドラは型タグ付きのタプルを返し、異なるコレクション型が同じ内容でも
区別されるようにする。
if/elif チェーンと比較して、新しい型のサポート追加が局所的で、 既存コードへの影響がない。numpy や Pydantic のようなオプショナル依存の 型ハンドラも、条件付きで登録できる。
[1, 2](list)と (1, 2)(tuple)を区別するため、
正規化結果に ("__list__", ...), ("__tuple__", ...) のように
型タグを含める。これにより、構造的に同一でも型が異なるデータが
異なるキャッシュキーを生成する。
Python では True == 1 かつ isinstance(True, int) だが、
キャッシュキーとしては区別が必要。singledispatch は MRO 順でマッチするため、
bool を int より先にチェックするインライン処理を base handler に配置した。
__dict__ と __slots__ の両方を MRO 走査で収集する。
__dict__ スロット自体はスキップするOrderedDict は順序が意味を持つため、キーのソートを行わない(dict とは異なる)defaultdict は default_factory を無視し、内容のみで正規化するEnum は 型名.メンバー名 + value で正規化し、モジュール・qualname も含めるstr() にフォールバックし、警告を出力する(非決定的)str() にフォールバックするが、安定性は保証されないreferences: src/beautyspot/cachekey.py
親: SPEC005
子: —
Strategy enum(DEFAULT, IGNORE, FILE_CONTENT, PATH_STAT の4種)と
KeyGenPolicy クラスで構成。KeyGenPolicy は引数名→戦略のマッピングを保持し、
bind(func) で inspect.signature() を使って関数シグネチャにバインドされた
キー生成関数を返す。ファクトリメソッド KeyGen.ignore(), KeyGen.map(),
KeyGen.file_content(), KeyGen.path_stat() で宣言的にポリシーを構築できる。
デコレーション時に inspect.signature(func) を呼び、
位置引数・キーワード引数・デフォルト値を解決する。
これにより、呼び出し時にはシグネチャ解析のオーバーヘッドがなくなる。
引数ごとの戦略を enum で宣言する方式を採用。
関数を渡す方式(keygen=lambda ...)と比較して、
シリアライズ可能で、デバッグ時の可読性が高い。
bind() 内で RecursionError を catch し、警告を出力してフォールバックするFILE_CONTENT 戦略は KeyGen.from_file_content() に委譲し、65KB チャンク読み取りを行うPATH_STAT 戦略は KeyGen.from_path_stat() に委譲し、mtime + size でハッシュするIGNORE 戦略の引数はキー計算から完全に除外される(値に関係なくキャッシュヒット)references: src/beautyspot/cachekey.py
親: SPEC006
子: —
beautyspot のコア機能である「関数のメモ化(キャッシュ)」において、関数の引数 (args, kwargs) から一意なキャッシュキーを生成する必要があります。
初期実装 (v0.1.0) では、json.dumps が失敗した場合のフォールバックとして str((args, kwargs)) のハッシュ値を使用していました。
しかし、Pythonのデフォルトの __str__ / __repr__ 実装は、オブジェクトのメモリアドレス(例: <MyObject at 0x10a...>)を含むことが多くあります。
これにより以下の問題が発生していました:
外部ライブラリ(joblib 等)を使えば解決しますが、beautyspot は軽量な「黒子」ライブラリを目指しており、依存関係を増やしたくありません。
pydantic モデルや dataclass 等、一般的なPythonオブジェクトを透過的に扱えること。str() によるフォールバック)。joblib 等)の導入。json を拡張した、独自の安定化シリアライザの実装。Chosen option: Option 3.
標準ライブラリの json モジュールを使用し、独自の default シリアライザ (_stable_serialize_default) を実装することで、依存関係なしで 堅牢なハッシュ生成を実現します。
具体的には以下の戦略を採用します:
sorted(list(obj)) でリスト化し、順序を保証します。__dict__ または __slots__ を参照し、オブジェクトの「中身の値」をシリアライズ対象とします。これにより、メモリアドレスへの依存を排除します。hex) に変換します。str() を使用します(この場合のみ不安定になるリスクを許容します)。pydantic モデルや dataclass も設定なしでキャッシュ可能になる。numpy, joblib 等)を増やさずに済む。str() 変換よりも、シリアライズ処理のオーバーヘッド(CPUコスト)がわずかに増加する。親: REQ002
子: —
beautyspot はこれまで、キャッシュキーの生成に json.dumps(sort_keys=True) と MD5 を使用していました。
しかし、以下の課題が顕在化していました:
tolist() や str())による巨大なオーバーヘッドとメモリ消費が発生する。str() に依存したフォールバックでは、巨大なNumpy配列が省略表示(...)された際にハッシュが衝突し、誤ったキャッシュヒットを引き起こす危険性がある。MD5 は現代のセキュリティ基準では非推奨とされており、コンプライアンス上の懸念がある。msgpack エコシステムで統一したい。json.dumps(sort_keys=True) と MD5 を継続利用する。msgpack を用いた独自正規化ロジックと SHA-256 を採用する。Chosen option: Option 2.
キャッシュキー生成ロジックを以下のように刷新します:
canonicalize(obj) を実装する。[[k, v], ...]」に変換し、順序を固定する。numpy をインポートせず、Duck Typing(shape, dtype, tobytes 属性の確認)により検知し、生のバイト列を含むタプルに変換する。これにより完全な一意性を保証する。msgpack でシリアライズする。SHA-256 に変更する。pickle を使用しないため、安全性(RCEフリー)が維持される。親: REQ002
子: —
quality_report により、src/beautyspot/cachekey.py の canonicalize 関数が非常に高い循環的複雑度(ランク D)を持っていることが判明しました。この関数は、ハッシュ化のためのオブジェクト正規化において、dict, list, set, numpy, type など多岐にわたる型をチェックするために長い if-elif-else チェーンを使用していました。
この構造は開放閉鎖の原則 (Open-Closed Principle) に違反しており、新しい型のサポートを追加するたびにコア関数を修正する必要があるため、退行(デグレード)のリスクを高めていました。
if-elif チェーンを維持する。functools.singledispatch を使用してリファクタリングする。Chosen option: Option 2.
canonicalize 関数を functools.singledispatch を用いて刷新します。
str)、およびダックタイピングによるチェック(Numpy 配列や __dict__ を持つオブジェクト等)を処理する。dict, list, tuple, set, frozenset, type 等の特定のロジックは、装飾された独立した関数に移動する。singledispatch は厳密な型に基づいているため、Numpy のようなダックタイピングによるチェックは依然としてデフォルトハンドラや基底層で行う必要がある。親: REQ002
子: —