Skip to content

[構造監査] 新CCプロトコルを安全・低コストに追加できるよう基盤をリファクタリングする #28

@thawk105

Description

@thawk105

進捗サマリ (2026-05-15 時点)

この issue は構造監査の正典トラッキングイシュー。P0〜P10 は個別のサブ issue / PR に分割して進行している。

重複イシューについて: #29 は本 issue (#28) の誤作成された重複であり CLOSED 済み。トラッキングは #28 が正典

凡例: ✅ 完了 / 🔀 分割して子イシュー進行中 / ⬜ 未着手

P 概要 元 issue 状態 子イシュー / 分割先
P0 common/ を静的ライブラリ化 #30 ✅ 完了 — (PR #39)
P1 TxExecutor コントラクトを C++20 concept 化 #31 ✅ 完了 — (PR #40)
P2 per-protocol result.cc + ワーカーボイラープレート廃止 #33 (CLOSED) 🔀 分割・進行中 (#101 完了) #101 per-protocol result.cc 撲滅 (✅ PR #108・CLOSED) / #102 common/runner.hh テンプレート化 (OPEN・着手可能)
P3 宣言的 ccbench_add_protocol() CMake 関数 #32 ✅ 完了 — (PR #41, #42)
P4 コンパイル時オプションの集約 (cmake/Options.cmake) #37#60 ✅ 完了 — (PR #73)
P5 cc_format/ をジェネレータに置き換え #34 (CLOSED) ✅ 完了 生成スクリプト案は廃止。cc_format/ 削除は P7 #83 に統合されマージ済み。新プロトコル追加手順は概念ベースで docs 化済み
P6 テストインフラを機能させる + 分離レベルチェッカー #35 (CLOSED) 🔀 分割・進行中 #91 テストインフラ配線 (OPEN) / #92 include/ ユニットテスト (OPEN) / #93 分離レベルチェッカー (OPEN)
P7 デッドコード一掃 #36 (CLOSED) 完了 #83#87 すべて 2026-05-15 に PR #103#107 として master マージ済み (詳細は下記)
P8 サードパーティの FetchContent 化 #37#64 (CLOSED) 🔀 分割・進行中 (主要部分完了) #97 FetchContent 基盤 + masstree/googletest (✅ PR #109・CLOSED) / #98 mimalloc (✅ PR #109・CLOSED、性能検証は user 指示でスキップ) / #99 bootstrap/CI/docs 仕上げ (OPEN・残スコープは ubuntu.deps + docs/build_{ja,en}.md のみ、CI / .gitmodules / bootstrap_*.sh#109 に取り込まれ済み) / #100 logger.h/spdlog docs (✅ #105/#86 で superseded・CLOSED)
P9 構造化ベンチ出力 #38 (CLOSED) 🔀 分割・進行中 #94 JSON 出力 (OPEN) / #95 run_matrix (OPEN) / #96 ccdata docs (OPEN)
P10 衛生 (.gitattributes / clang-format CI / Doxygen) #37#61/#62/#63 ✅ 完了 #61 (PR #72) / #62 (PR #70) / #63 (PR #76) すべて CLOSED。一括 reformat (#74) も完了

P7 完了の内訳 (2026-05-15)

#36 を 5 つの子イシューに分割し、すべて PR #103#107 として master にマージ済み・CLOSED:

子 issue 内容 PR
#83 cc_format/ 削除 + 新プロトコル追加手順を docs に概念ベースで記載 #103
#84 cc/occ/ の撤去 #104
#85 instruction/microbench/ リネーム + オプトイン CMake 化 #107
#86 未追跡の third_party/spdlog 残骸・未使用 include/logger.h 除去 #105
#87 残骸ビルドシステム (common/Makefileinclude/Makefilebuild_tools/bootstrap_tbb.sh) 削除 #106

補足:


以下は元の構造監査の本文。リファクタリングの根拠として保存している。P7 完了 (#83#87 / PR #103#107) により実態が変わった箇所には ✅ 注記を入れた。各提案ブロックの末尾に現状ステータスを注記している。

動機

CCBenchの使命は「共通基盤の上でCCプロトコルを比較する」ことだ。ベンチ部分は機能しているが、VLDB 2020オリジナルリリース+jnmt/vldb-paper マージ以降の累積的なドリフトにより、新しいCCプロトコルを追加することが不必要に困難になっている。また、どこにも分離レベル/直列化可能性チェックが存在しないため、バグのある新プロトコルを追加しても必ずしも検出されない。このイシューは証拠ベースの構造的な監査であり、「新CCプロトコルを安全・低コストに追加できる」目標に向けた優先順位付きリファクタリング提案である。各項目は具体的なファイル・行数を引用しているため検証可能だ。

発見された問題

1. ビルドの重複はccacheが隠しているだけ

各プロトコルの CMakeLists.txt は、ワークロードエントリポイントだけが異なる同一ソースリストを file(GLOB) ブロックで1〜4個のワークロードバイナリに対して宣言している:

file(GLOB YCSB_SILO_SOURCES  "../common/result.cc" "../common/util.cc" "result.cc" "ycsb_silo.cc"  "transaction.cc" "util.cc")
file(GLOB TPCC_SILO_SOURCES  "../common/result.cc" "../common/util.cc" "result.cc" "tpcc_silo.cc"  "transaction.cc" "util.cc")
file(GLOB BOMB_SILO_SOURCES  "../common/result.cc" "../common/util.cc" "result.cc" "bomb_silo.cc"  "transaction.cc" "util.cc")
file(GLOB SBOMB_SILO_SOURCES "../common/result.cc" "../common/util.cc" "result.cc" "sbomb_silo.cc" "transaction.cc" "util.cc")

grep -c 'file(GLOB.*_SOURCES)' <protocol>/CMakeLists.txt の合計は10プロトコルで34ブロック。つまり common/*.cc<protocol>/transaction.cc が現在の〜28バイナリそれぞれに対して個別にコンパイルされている。CIワークフロー自身のコメントもこの点を正直に述べている:

ccache deduplicates compilations across the 28 binaries (each protocol's CMakeLists.txt currently compiles common/*.cc and its own .cc files separately for every target).

すなわちccacheは構造的問題をマスクしているだけで、add_library(ccbench_common STATIC ...) で一度で解決できる。

その他のCMake衛生問題:

  • add_definitions(...) (3.12以降非推奨、ディレクトリスコープ): プロトコルCMakeファイル全体で182箇所。置き換えは target_compile_definitions(... PRIVATE ...) でターゲットスコープが標準パターン。
  • file(GLOB) によるソース列挙(CMakeドキュメントが明示するアンチパターン: 変更でreconfigureが走らない)。
  • masstree / mimalloc が raw パス(${THIRD_PARTY_DIR}/masstree/libkohler_masstree_json.a)でリンクされており、imported target(add_library(ccbench::masstree STATIC IMPORTED))化されていない。
  • silo/, ss2pl/, cicada/, mocc/, ermia/ の CMakeLists.txt が CRLF 改行(他はすべてLF)。

2. 「共通」コードが実際には共通化されていない

各プロトコルに11行の result.cc が存在し、違いはグローバル変数名だけ:

// silo/result.cc
alignas(CACHE_LINE_SIZE) std::vector<Result> SiloResult;
void initResult() { SiloResult.resize(TotalThreadNum); }

同じ11行が cicada/result.cc(CicadaResult)、mocc/result.cc(MoccResult)、ermia/, tictoc/, oze/, ss2pl/, mvto/, si/, d2pl/ にも存在する。識別子だけが異なる10コピー。

util.cc(80〜330行)は差異が大きいが、ワークロードエントリポイントの <workload>_<protocol>.cc(ワーカー+メイン+スレッド生成+バリア+結果集計)も、TxExecutor コンストラクタ引数とper-protocolの *Result グローバル名が違うだけでプロトコル間でほぼ同一だ。

3. TxExecutorコントラクトがコードではなくCLAUDE.mdに存在する

CLAUDE.md には各プロトコルの TxExecutor が公開すべきAPIが記載されている:

Status read(...)
Status write(...)
Status insert(...)
Status delete_record(...)
Status scan(...)
Status scan(..., int64_t limit)
bool commit()
void abort()

しかしそれを強制するものが何もない: abstractベースも、C++20 conceptも、static_assertもない。強制されたコントラクトがあれば検出できたはずのバグがこのリポジトリの最近の履歴で実際に発生している:

  • ss2pl/d2pl/mvto/si が当初 scan(..., int64_t limit) オーバーロードを実装しておらず、TPC-Cの get_order_id や OrderSecondary スキャンで必要だった。
  • tx.readStatus::WARN_NOT_FOUND を返すと *body が未変更のまま、tx.status_ == aborted だけを確認する呼び出し元が以前のreadが指していた値を参照解除する。これにより HeapObject::cast_to<Order> アサーションが発生し、根本原因特定に多段階のbisectionが必要だった。

CLAUDE.md の「実装上の注意事項」は conceptがコンパイル拒否できるはずのフットガンで埋まっている。

4. ツリー内のデッドコード

✅ この節の項目は P7 (#83#87 / PR #103#107) ですべて解消済み。 cc_format/occ/instruction/include/logger.hcommon/Makefileinclude/Makefilebootstrap_tbb.shthird_party/spdlog 残骸はツリーから削除済み。instruction/microbench/ にリネームしオプトイン CMake 化。tpcc_silo コメント行も除去済み。以下は監査当時の記録。

項目 状況 P7 後
occ/ ツリー内に存在するが #add_subdirectory(occ) なし。Makefileを使用、CMakeでない。 #84 / PR #104 で撤去
#add_subdirectory(tpcc_silo) トップレベルCMakeLists.txtのコメント行(ディレクトリ自体は既に削除済み)。 ✅ 除去済み
bootstrap_tbb.sh third_party/tbb を参照するがサブモジュールに存在しない。CLAUDE.mdは「Skip it」と指示。 #87 / PR #106 で削除
third_party/spdlog(サブモジュール) 唯一の利用箇所は include/logger.hsetup_spdlog() を定義)だが、#include "logger.h" はツリー内の .cc / .hh ファイルに0件。spdlog は推移的にデッドである。 #86 / PR #105 で残骸・logger.h ともに除去
instruction/ 独自のMakefileを持つマイクロベンチディレクトリ、トップレベルCMakeに未接続。 #85 / PR #107microbench/ にリネーム + オプトイン CMake 化
common/Makefile, include/Makefile CMake移行前の残骸。現在のビルドに一致しない INST_SRCS1+=... フラグメントを含む。 #87 / PR #106 で削除
cc_format/ 新プロトコルの「テンプレート」—Makefileベースで、CMakeメインラインと並行世界。項目6参照。 #83 / PR #103 で削除
ss2pl/test/CMakeLists.txt make_db_test.cpp があるが、どこからも add_subdirectory(test) されていない孤立テスト。 ⬜ P6 #91 で配線予定 (現 cc/ss2pl/test/)

5. テストインフラが機能していない

  • トップレベルCMakeLists.txtに enable_testing() なし。
  • .github/workflows/build.yml にctestステップなし。CIはコンパイルのみ検証。
  • googletestは bootstrap_googletest.sh でサブモジュールとしてビルドされるが、唯一の利用箇所は上記の孤立テスト。
  • include/ の共通コンポーネント(atomic_wrapper, rwlock, zipf, masstree_wrapper)のユニットテストがゼロ。
  • 「Snapshot Isolation」や「直列化可能性」を主張する新プロトコルを追加しても、その主張を検証するものが一切ない—ベンチはどちらにせよ数値を出し続ける。

6. 新プロトコル追加の「公式」手順が劣悪

cc_format/README.md が文書化されたテンプレートだ。抜粋:

result.cc L12-14 : 適切な名前で変数を宣言する。
sv_format.cc L48-66 : ログ永続化を有効にする場合は適切に編集する。
         L106-133 : 単一トランザクションのワークフローを定義する。
util.cc L105-117 : レコードメンバの初期値を適切に設定する。

行番号ベースの編集指示。内部リファクタリングのたびに無効化される。さらに cc_format/ 自体がMakefileベースで、メインCMakeビルドから切り離されており、MVバージョンや決定論的スタイルはスコープ外だ。

cc_format/ は P7 #83 / PR #103 で削除済み。 行番号ベースのレシピは廃止し、新プロトコル追加手順は docs に概念ベースで記載した。

7. プロトコル×ワークロードのカバレッジが非宣言的

README.md / docs/protocols.md / CLAUDE.md のサポートマトリクスは、各プロトコルのCMakeLists.txtで add_executable(<workload>_<protocol>.exe ...) 行を読み、対応する <workload>_<protocol>.cc ファイルの存在と照合することでしか復元できない。3つの情報源(ドキュメント、CMake、.ccファイル)を人手で同期している。

改善提案(「新CCプロトコルを安全・低コストに追加」のROI順)

P0 — common/ を真の静的ライブラリにする

add_library(ccbench_common STATIC common/util.cc common/result.cc ...)
target_link_libraries(ccbench_common PUBLIC
    Threads::Threads Boost::filesystem gflags::gflags glog::glog
    ccbench::masstree ccbench::mimalloc)
target_include_directories(ccbench_common PUBLIC include)

各プロトコルは target_link_libraries(<binary> PRIVATE ccbench_common) に。masstree / mimalloc は add_library(ccbench::masstree STATIC IMPORTED) に。「common を28回コンパイル」問題が消え、ccacheがコンパイル重複排除の要でなくなる。

→ ✅ 完了 (#30 / PR #39)。 トップレベル CMakeLists.txtadd_library(ccbench_common STATIC ...)ccbench::masstree / ccbench::mimallocIMPORTED GLOBAL ターゲットが入った。各 cc/<protocol>/CMakeLists.txtccbench_common ccbench::masstree ccbench::mimalloc をリンクする。

P1 — TxExecutorコントラクトをC++20 conceptとして成文化する

template <class T>
concept TxExecutorLike = requires(T t, Storage s, std::string_view k, TupleBody** b) {
    { t.read(s, k, b) }                                                              -> std::same_as<Status>;
    { t.write(s, k, std::declval<TupleBody&&>()) }                                   -> std::same_as<Status>;
    { t.insert(s, k, std::declval<TupleBody&&>()) }                                  -> std::same_as<Status>;
    { t.delete_record(s, k) }                                                        -> std::same_as<Status>;
    { t.scan(s, k, false, k, false, std::declval<std::vector<TupleBody*>&>()) }      -> std::same_as<Status>;
    { t.scan(s, k, false, k, false, std::declval<std::vector<TupleBody*>&>(), int64_t{}) } -> std::same_as<Status>;
    { t.commit() }                                                                   -> std::same_as<bool>;
    { t.abort() }                                                                    -> std::same_as<void>;
};

ワークロードテンプレートは <TxExecutorLike Tx, ...> を受け取る。limit オーバーロードの実装漏れはTPC-Cデリバリーでのランタイム SEGVではなく、名前付き診断メッセージのビルドエラーになる。CLAUDE.mdのフットガンリストが縮小する。

→ ✅ 完了 (#31 / PR #40)。 include/tx_executor_concept.hhTxExecutorLike concept が入り、CMAKE_CXX_STANDARD 20 / cxx_std_20 に統一。全 10 プロトコルの cc/<protocol>/include/transaction.hhstatic_assert(TxExecutorLike<TxExecutor>); が入った。あわせて tx.writetx.update の rename も実施。

P2 — per-protocol result.cc とワーカーボイラープレートを廃止する

単一の Result グローバルを common/ に置く。per-protocolの命名(SiloResult, CicadaResult, ...)を廃止。共通ランナーテンプレートがスレッド生成・バリア・時間ループ・集計を担う:

// silo/ycsb_silo.cc が〜100行から以下に縮小:
#include "common/runner.hh"
#include "silo/silo_tx.hh"
int main(int argc, char** argv) {
    return ccbench::run<silo::TxExecutor, ccbench::YcsbWorkload>(argc, argv);
}

プロトコル固有のセットアップ(SiloのWAL、Cicadaの pre_reserve_version 等)は、ランナーが取り込むper-protocolポリシー/トレイトになる。

→ 🔀 分割・進行中 (#33 CLOSED → #101 / #102)。 #101「per-protocol result.cc を撲滅し initResult を共通化」は PR #108 で完了・CLOSED (2026-05-15)。10 プロトコルの cc/<protocol>/result.cc を撲滅し、common/result.cc の単一 CCBenchResults + initResult(std::size_t) に集約済み。残るは #102「ワークロードエントリポイント main() を common/runner.hh テンプレートに集約」(OPEN・着手可能)。共通ランナーテンプレートはまだ未導入。

P3 — 宣言的な ccbench_add_protocol(...) CMake関数

<protocol>/CMakeLists.txt が以下に縮小:

ccbench_add_protocol(silo
    SOURCES silo.cc transaction.cc
    WORKLOADS ycsb tpcc bomb sbomb
    OPTIONS WAL=0 BACK_OFF=1
)

この関数が add_executable 呼び出し、ccbench_common のリンク、オプションの適用を生成する。README/CLAUDE.mdのサポートマトリクスはこれらの宣言から生成されるようになり、人手での同期が不要になる。d2plの「BombのみサポートWork」が暗黙でなく一言で事実となる。

→ ✅ 完了 (#32 / PR #41, #42)。 cmake/ProtocolHelpers.cmakeccbench_add_protocol() を実装。全プロトコルを cc/ 配下に移動し、各 cc/<protocol>/CMakeLists.txtccbench_add_protocol(...) 1 呼び出しに縮小。occ/ も PR #42 でいったん cc/occ に移動し、その後 P7 #84 / PR #104 で撤去。

P4 — コンパイル時オプションの集約

# cmake/Options.cmake
set(CCBENCH_KEY_SIZE 8 CACHE STRING "key size in bytes")
set(CCBENCH_BACK_OFF 1 CACHE BOOL "exponential backoff on abort")
# ...
function(ccbench_apply_options target)
    target_compile_definitions(${target} PRIVATE
        KEY_SIZE=${CCBENCH_KEY_SIZE}
        BACK_OFF=${CCBENCH_BACK_OFF} ...)
endfunction()

182箇所の add_definitions(...) と各プロトコルが持つ〜20個の if/else ブロックを置き換える。

→ ✅ 完了 (#37 から分割 → #60 / PR #73)。 cmake/Options.cmake を新設し、ビルド時 tunable を CACHE エントリ化。ccbench_add_protocol()target_compile_definitions でターゲットスコープに適用する。プロジェクト本体の CMakeLists.txt / *.cmake から add_definitions(...) は消えた (最後の残りだった ss2pl/test も PR #73 で対応)。

P5 — cc_format/ をジェネレータに置き換える

cc_format/ を削除。scripts/new_protocol.py NAME=foo STYLE=sv を追加し、以下を生成:

  • foo/CMakeLists.txt — 5行程度、ccbench_add_protocol(foo ...) を呼び出すのみ。
  • foo/transaction.ccTxExecutorLike conceptを満たす最小スケルトン。プロトコル固有ロジックに // TODO
  • foo/include/ ヘッダー群。

docs/contributing.md に行番号なしの構造的な手順を記載する。

→ ✅ 完了 (#34 CLOSED)。 検討の結果、生成スクリプト (scripts/new_protocol.py) 案は廃止。cc_format/ の削除は P7 #83 / PR #103 に統合されマージ済み。新プロトコル追加手順は行番号ベースのレシピをやめ、docs/ に概念ベースで記載した。

P6 — テストを実際に動かす

トップレベルCMakeLists.txtに enable_testing()add_subdirectory<protocol>/test/ と新規 test/ を接続。CI: cmake --build build --target test(または ctest --output-on-failure)を追加。

トップレベル test/:

  • include/ の共通コンポーネントのユニットテスト(zipf, atomic_wrapper, rwlock)。
  • 分離レベルチェッカー: 各プロトコルに対して Adya/Berenson の既知のアノマリースケジュール(G0/G1/G2、write skew、read skew...)を実行し、結果がプロトコルの主張する分離レベルと一致することを検証する。SIを主張するプロトコルはwrite skewを許容すべき、直列化可能を主張するプロトコルはwrite skewを拒否すべき。これが「バグのある新プロトコルが追加されてもベンチが数値を出し続ける」問題への構造的な修正だ。

→ 🔀 分割・進行中 (#35 CLOSED → #91 / #92 / #93)。 #91「テストインフラ配線 (enable_testing + CI ctest + 孤立テスト接続)」・#92「include/ 共通コンポーネントのユニットテスト」・#93「分離レベル正当性チェッカー (アノマリーテストスイート)」の 3 子イシューに分割、すべて OPEN。現状トップレベル CMakeLists.txtenable_testing() なし、CI (build.yml) に ctest ステップなし、cc/ss2pl/test/CMakeLists.txt に孤立した add_test が 1 件あるのみ。

P7 — デッドコードを一掃する

各項目を判断する:

  • occ/, instruction/, cc_format/: トップレベルCMakeに統合、legacy/ に移動、または削除。
  • third_party/spdlog サブモジュール+include/logger.h を削除(未使用)。
  • bootstrap_tbb.sh, common/Makefile, include/Makefile を削除。
  • #add_subdirectory(tpcc_silo) の古いコメント行を削除。

→ ✅ 完了 (#36 CLOSED)。 #36 を 5 子イシュー #83#87 に分割し、すべて 2026-05-15 に PR #103#107 として master マージ済み・CLOSED。

P8 — サードパーティのFetchContent化

include(FetchContent)
FetchContent_Declare(mimalloc
    GIT_REPOSITORY ...
    GIT_TAG ...)
FetchContent_MakeAvailable(mimalloc)
add_library(ccbench::mimalloc ALIAS mimalloc-static)

3つの bootstrap_*.sh スクリプトを廃止する。cmake -B build のみがセットアップ手順になる。

→ 🔀 分割・進行中 (主要部分完了)。#37 から分割 → #64 CLOSED → #97 / #98 / #99 / #100 #64 をさらに 4 子イシューに分割:

P9 — 構造化されたベンチ出力

--output-format=json --output=run.json フラグを common/ に追加し、{config, env (cpu/freq), per-thread stats, aggregate} を出力する。scripts/run_matrix.py がマトリクスYAMLを受け取り、結果ディレクトリを生成する。ccdata リポジトリの役割が「収集結果のストア」として明確になる。

→ 🔀 分割・進行中 (#38 CLOSED → #94 / #95 / #96)。 #94「ベンチバイナリに構造化 (JSON) 出力を追加」・#95「マトリクス実行スクリプト (run_matrix) と実行定義 YAML」・#96「ccdata リポジトリの役割と結果保存規約を docs 化」の 3 子イシューに分割、すべて OPEN。--output-format=json 等のフラグ・scripts/run_matrix.py ともに未実装。

P10 — 衛生

.gitattributes: * text=auto eol=lf と問題のあるCMakeLists.txtのdos2unix。CI に clang-format --dry-run --Werror ジョブを追加(.clang-format はリポジトリに既存)。Doxygenの find_package を生成するか廃止するか決める。

→ ✅ 完了 (#37 から分割 → #61, #62, #63、すべて CLOSED)。 .gitattributes* text=auto eol=lf 追加 (#61 / PR #72)、dead な find_package(Doxygen) 削除 (#62 / PR #70)、clang-format dry-run CI ジョブ追加 (#63 / PR #76)。.clang-format をリポジトリ実態に合わせてリファイン (PR #75)、.clang-format 準拠の一括 reformat (#74) も完了。
あわせてビルド堅牢化として段階的 -Werror 化 (#43) を実施。複数の警告カテゴリを -Werror= 昇格し、#43 は CLOSED・完了

推奨実装順序

「新CCプロトコルを安全・低コストに追加」という目標に向けて:

「新プロトコルが正しいことを保証」という目標に向けて:

P7〜P10は衛生作業として構造的変更と並行して実施するのが理想(二度手間を避けるため)。 → P4 / P7 / P10 完了、P8 #97 / #98 完了 (PR #109)、残るは #99 の docs + apt 仕上げのみ。

推奨PoC

1プロトコル(silo/ 推奨)に P0 + P1 + P3 を実装する。期待される成果: silo/CMakeLists.txt の〜100行が10〜20行に縮小、silo/{result.cc,*workload*_silo.cc} が大幅に縮小、TxExecutorLike conceptがコンパイル時に missing-overload 系のバグを検出。これが成立すれば、同じ形を残り9プロトコルに展開する。

→ ✅ 実施済み。 PoC は成立し、P0/P1/P3 は silo だけでなく全 10 プロトコルへ展開完了。各 cc/<protocol>/CMakeLists.txtccbench_add_protocol(...) 1 呼び出しに縮小済み。残課題は P2 (#101 完了・#102 残)、P6 (#91#93)、P8 (#97 / #98 完了・#99 docs 仕上げ残)、P9 (#94#96)。

サブイシューに分割するのが有用であれば喜んで対応する。 → 実施済み。P0〜P10 を #30#38 に、CMake 衛生の #37 をさらに #60#64 に分割。さらに残 OPEN だった #33 (P2)・#34 (P5)・#35 (P6)・#36 (P7)・#64 (P8)・#38 (P9) を 2 次分割し、#83#102 の子イシュー群を起こした。関連して #43#27 派生の #88#90 も派生。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions