name: datalog-debug-skill description: freee API から同期したデータ、または任意の What-if データを DataLog (オンメモリ監査エンジン) に投入し、クエリ・監査・可視化で分析するためのスキル。会計税務・労務・社内統制の整合性監査、仮想シナリオ検証、グラフ探索を行う際に使う。

DataLog Skill

freee 会計・人事労務データを「事実 (Facts) と規則 (Rules) の集合」として取り込み、クエリ・監査・インタラクティブなグラフで分析するスキル。本番 freee データへの書き込みは発生せず (オンメモリ独立環境)、多様な入り口 (API 同期 / 自由 transact / LLM 試作) を同じグラフモデルに吸収する。

初期設定 (必須・非自動)

DataLog は MCP サーバー起動時に空の状態で立ち上がる。freee API データは 明示的に datalog_sync_* を呼ばない限り 1 件も投入されない (自動同期は無い)。クエリや監査を走らせる前に、対象ドメインに応じて以下を順に実行すること。

  1. freee_auth_status で OAuth トークンが有効か確認 (期限切れなら freee_authenticate)
  2. freee_set_current_company で対象事業所を選択
  3. datalog_reset でクリーンな環境を確保 (再 sync 時は必須)
  4. 対象データを sync (会計監査なら deals/partners/account_items/tax_codes、社労士監査なら employees/work_record_summaries)
  5. datalog_visualize で取り込み結果を確認

注意点:

  • datalog_sync_*差分同期ではなく全件取得。再実行で重複は発生しないが、削除されたエンティティを反映するには datalog_reset を挟む必要がある
  • datalog_sync_deals / datalog_sync_account_items は内部で概念ノード (取引/明細/勘定科目 等) を最初の呼び出しでプリセットする。datalog_sync_partners は単独では概念ノードを作らないので、deals か account_items の sync を先に走らせること (順序逆だと "Nothing found for entity id ('node/name' '取引先')" でエラー)
  • datalog_sync_work_record_summariesdatalog_sync_employees の後でのみ動作 (emp ノードが無いと早期 return)
  • datalog_query / datalog_query_run を sync 前に走らせると空結果が返るだけで失敗扱いにはならない。「データが無い」と「クエリが間違っている」の区別がつきにくいので、必ず初期設定 (上記 1-4) を済ませてから検証する

ツール早見表

ツール 役割
datalog_schema 現在のスキーマ情報 (固定 + 登録済み + 動的統計 + 推奨クエリ) を一括で返す
datalog_query 任意の datalog クエリを実行
datalog_reset DataLog 環境を初期化
datalog_transact 任意 datom を投入
datalog_sync_* freee API からデータ同期 (account_items / partners / tax_codes / deals / user_matchers / employees / work_record_summaries)
datalog_visualize HTML 可視化 (自動更新)
datalog_query_list / datalog_query_get / datalog_query_create / datalog_query_update / datalog_query_delete / datalog_query_run 監査クエリ CRUD + 実行 (datalog-store/query.*.json)
datalog_idb_list / datalog_idb_get / datalog_idb_create / datalog_idb_update / datalog_idb_delete IDB (Interpretation Rules) CRUD (datalog-store/idb.*.json)。事前バリア (= 違反 datom 投入ブロック) は IDB rule の violationQuery + errorMessage で実現する (= 旧 prerule API の役割を統合済)
datalog_testrecord_list / datalog_testrecord_get TestRecord (独立 test 行) の一覧 + 1 件取得。一覧は集計 (byRunStatus / bySyncStatus / byPyramidLevel / byKindRunStatus TP/TN/FP/FN / passRate) も返す
datalog_testrecord_propose LLM が新規 test を提案、または既存 test の改修案を出す (= syncStatus='llm_proposal_pending')。承認は user が datalog_testrecord_approve で行う
datalog_testrecord_approve user が LLM 提案を承認 (= userVersion ← llmRecommendation)。AI agent からは呼ばない (audit trail 維持)
datalog_testrecord_user_edit user が userVersion を直接編集 (= syncStatus='user_diverged')
datalog_testrecord_request_change user → LLM 改修指示 (= syncStatus='user_change_requested')。次の LLM ターンが userChangeRequest を pickup して datalog_testrecord_propose で改修案を返す想定
datalog_testrecord_run userVersion を真として走行 → runStatus / actualHits / lastRunAt を persist。syncStatus='llm_proposal_pending' は skip
datalog_testrecord_delete 削除

v1 (datalog_testrecord_*) と v2 (datalog_testcase_* / datalog_testrow_*) が並走 (#177)

  • v1 datalog_testrecord_* (上表): 旧来の「独立 test 行」モデル。1 行 = datoms + expectedRows + dual content (llmRecommendation / userVersion + syncStatus)。_propose / _approve / _user_edit / _request_change / _run / _list / _get / _delete
  • v2 datalog_testcase_* + datalog_testrow_* (#177): 親子 2 層モデル。datalog_testcase_* = TestCase header (= title / dataSource / kind / tags / statuteDescription) の CRUD + datalog_testcase_run で配下 row 一括走行。datalog_testrow_* = TestCase 配下の TestRecordV2 row (= nodeKey / expectedKind / businessExpectation / freeeReqBody 等) の CRUD。
  • 使い分け: 新規 test 設計は v2 (= TestCase 1 件 + その配下に複数 row、dataSource / businessExpectation ordinal / freee 投入 record を持てる)。v1 は移行前の既存 test 用に共存し、別 issue で順次 v2 へ移行する。audit-test-design-skill / freee-api-test-record-skill が前提とするのは v2 の TestCase + row モデル。

基本ワークフロー

A. 実データ監査 (会計・税務)

freee_set_current_company → datalog_reset → datalog_sync_account_items
→ datalog_sync_partners → datalog_sync_tax_codes → datalog_sync_deals
→ datalog_sync_user_matchers (任意: 自動仕訳ロジック監査用)
→ datalog_visualize

datalog_sync_user_matchers は freee の自動登録ルール (UserMatcher) を取り込む。明細の自動仕訳ロジック (条件 / 適用 act / 適用先 account_item) を DataLog で照合できるようになり、「ルール適用結果と実 account_item の乖離」「無効化されたまま放置されたルール」等の監査が可能。

B. 人事労務データ (社労士観点)

freee_set_current_company → datalog_sync_employees
→ datalog_sync_work_record_summaries year=YYYY month=M → datalog_visualize

C. What-if 仮想データ

datalog_transact {...} → datalog_query → datalog_visualize

D. スキーマ発見 (LLM 向け)

datalog_schema (初回のみで全体像取得)
→ 興味のある概念名を特定
→ datalog_query (返された recommendedQueries を参考に)
→ 必要なら datalog_query_create で監査化 / datalog_idb_create で IDB 化

詳細リファレンス

よくある操作

運用鉄則 (厳守)

  1. local (datalog-store/<base>.local.<breaking>.json) は絶対に手編集しない
    • 必ず MCP 経由: datalog_query_create/update/delete, datalog_idb_create/update/delete
    • 理由: Zod 検証、conn.onModified 連動 (HTML 自動更新)、テスト互換性を壊さないため
  2. common (datalog-store/<base>.<breaking>.json) のみ手編集可
    • git commit の diff で team レビューできるので、手編集の挙動変更は検出可能
  3. 監査クエリは IDB ドメイン述語を最大限活用
    • [?e "is_a" ?c] [?c "node/name" "..."] のような型検査の繰り返しは 対応する IDB (例 is_detail_of, is_partner_of, is_payment_instance_of) に畳む
    • 監査の :where:in $ % を書けば runner が IDB ルール集合を自動注入する
  4. 足りない IDB は先に作る → その後監査クエリを書く
    • 順序逆は技術負債になる (監査ごとに長い where が重複)
  5. 新規 IDB / query は test 駆動
    • tests (datoms + expectedRows、IDB は verifyQuery も) を同梱して datalog_idb_create / datalog_query_create
    • datalog_idb_test / datalog_query_testpassing を確認 → status: 'active' に昇格
    • tests 空 (= test_status: 'untested') のままでも登録は可能だが UI に「未検証」バッジが出る
    • scope 別の必須要件 (Issue #62 Phase 2d で enforce):
      • scope='user': test 0 件可
      • scope='organization' (= org-admin caller のみ作成可): test 1 件以上必須
      • scope='common' (= app-admin caller のみ作成可): test 1 件以上必須
    • TestRecord (独立 test 行) は dual content:
      • 各 test は llmRecommendation (= AI 提案版) と userVersion (= 利用者確定版) の 2 揃いを持ち、syncStatus で関係を 4 値で表現する
      • AI agent flow: datalog_testrecord_propose で提案 → user が UI または _approvein_sync
      • User flow: GUI 編集 = user_diverged / 改修指示 = user_change_requested → 次の AI ターンで再 propose
      • 走行は常に userVersion を真とする (= datalog_testrecord_run)。syncStatus='llm_proposal_pending' は未承認なので skip
      • 旧来の tests: a.json() 埋め込みは legacy として共存 (= Phase 4 完了後別 issue で削除予定)
  6. description は 3 系統で書く (#396、 audit query のみ statuteDescription 必須)
    • userDescription: ドメインユーザー向け平易日本語 (集合論レベル / プログラミング用語禁止)
    • engineerDescription: エンジニア / AI 向け技術疑似コード (旧 pseudocode の役割)
    • statuteDescription: 税理士 / 会計士 向け 法文風 (= 条文風) Markdown 詳細。 audit query (= datalog_query_create / _update) では 必ず 埋める (= 詳細書式は §「statuteDescription (法文風 Markdown)」)。 IDB rule では 不要。 既存 query を update する 際に 空のまま 上書き しない (= 既存値を 必ず 引き継ぐか、 改訂版を 出す)。
  7. status 4 値ライフサイクル (= rule / query の 稼働状況)
    • active (本番) / not_active (停止) / updating (試作中、create 既定) / outdate (breaking ver up 後の未対応)
    • (実装上は int ordinal: not_active=0 / outdate=10 / deprecated=20 / updating=30 / active=40)
  8. runStatus 7 値 (= test / TestRecord の 走行・段階)
    • test の 走行結果 + 設計段階 を 1 enum (int ordinal、 末端 = 健全) で 表す。 datalog_testrecord_run / datalog_testcase_run が persist し、byRunStatus 集計でも使う:

      ordinal name label 意味
      0 untested 未走行 まだ 走らせて いない
      5 query_pending クエリ未完成 親 audit query が 未完成 (= 走行 不可)
      7 test_design_pending テスト設計中 header / 期待 だけ で datoms 未充足 (= 走行 不可)
      10 error 走行エラー 走行 中 に 例外
      20 failing 失敗 走行 結果 が 期待 と 不一致
      30 passing 成功 unit 走行 OK (= datom_only の terminal green)
      40 e2e_passing E2E 成功 freee 実投入 + 実走行 まで 通った 最上位
    • runStatus ≠ rule の status (= 別 enum)。 runStatus は test 行 の 走行/段階、 status は rule/query の 稼働状況。

breaking version とファイル名

datalog-store/ 配下の common / local 両方にバージョンタグ (<major>.<minor>、ゼロ詰め無し) が埋め込まれる:

common: <base>.<breaking>.json         例: query.0.0.json
local : <base>.local.<breaking>.json   例: query.0.0.local.json

<breaking>package.json.version の major.minor 部分に追従する。

  • patch bump (0.0.1 → 0.0.2): 後方互換、データファイル変更不要
  • minor bump (0.0.x → 0.1.0): breaking、common と local 両方を新タグへ書き直す必要がある (datalog-migration-skill を参照)

旧 breaking 版の local ファイルは並列に残せる ( datalog_migration_checkcurrent=false で識別)。書き直し完了後に git rm で削除可。MCP CRUD ツールは現行 breaking 版にのみ書き込む。

IDB ファースト few-shot (LLM 向け)

incident #1 で観察された誤りパターンへの対策。クエリを書く前に必ず datalog_idb_list で既存 IDB を確認し、活用可能なドメイン述語があれば畳むこと。

観察された誤りパターン

  1. (is_detail_of ?det ?deal) を使わず [?det "is_belong_to" ?deal] [?det "is_a" ?c_det] [?c_det "node/name" "明細"] を毎回展開
  2. :in $ % を書かずに IDB 述語 (rule 形式) を呼ぼうとする → DataScript が Missing rules var '%' in :in で compile error (空返りではない)
  3. datalog_query (ad-hoc) で :in $ % を書きながら inputs を空にしてしまう (audit runner と挙動が違う)
  4. 既存 IDB を datalog_idb_list で事前確認しないまま手書きクエリを書く

good / bad ペア

例 A — 明細→取引のリレーション取得

bad (アトミック述語で展開、5 述語、IDB 更新時に追従漏れ):

[:find ?detail_id
 :where [?det "is_belong_to" ?deal]
        [?det "is_a" ?c_det] [?c_det "node/name" "明細"]
        [?deal "is_a" ?c_deal] [?c_deal "node/name" "取引"]
        [?det "node/id" ?detail_id]]

good (is_detail_of IDB に畳む、:in $ % 必須):

[:find ?detail_id
 :in $ %
 :where (is_detail_of ?det ?deal)
        [?det "node/id" ?detail_id]]

例 B — :in $ % 忘れ

bad (:in $ % 無しで rule 形式を呼ぶ → Missing rules var '%' in :in エラーで失敗):

[:find ?det ?deal :where (is_detail_of ?det ?deal)]

good:

[:find ?det ?deal :in $ % :where (is_detail_of ?det ?deal)]

例 C — 複数 IDB 述語の組み合わせ

[:find ?deal_id ?partner_name
 :in $ %
 :where (is_partner_of ?partner ?deal)
        (is_detail_of ?det ?deal)
        [?deal "node/id" ?deal_id]
        [?partner "node/label" ?partner_name]]

:in $ % 経路の差 (重要)

経路 inputs に渡すもの 備考
datalog_query_run (audit runner 経由) 不要 runner が datalog-store/idb.*.json 全 IDB を自動注入 (buildIdbRulesEdn() を実行時に注入)
datalog_query (ad-hoc 直接実行) inputs[0] に IDB ルール EDN 文字列 runner と別経路。[[(rule-name ?args) <body>]] 形式の string

ad-hoc datalog_query で is_detail_of を呼ぶ場合の inputs 具体例:

[
  "[[(is_detail_of ?detail ?deal) [?detail \"is_belong_to\" ?deal] [?detail \"is_a\" ?c_det] [?c_det \"node/name\" \"明細\"] [?deal \"is_a\" ?c_deal] [?c_deal \"node/name\" \"取引\"]]]"
]

(IDB の query 形式 [:find ?a ?b :where ...][(rule-name ?a ?b) ...] に変換し、複数ルールを [] でくるんだ EDN を 1 つの string として渡す。src/logic/idb/runtime.tsidbRuleToEdn / buildIdbRulesEdn 参照)

試作段階では :in $ % 無しのアトミック述語クエリで動作確認 → 安定したら datalog_idb_create + datalog_query_create で監査化、が推奨ルート。

Pre-flight チェックリスト

クエリを書く前に:

  1. datalog_idb_list で既存 IDB を確認
  2. 活用できる IDB が無ければ datalog_idb_create で先に作る
  3. IDB 述語を呼ぶときは :find 直後の :in$ % を必ず含める
  4. ad-hoc datalog_query 利用時のみ inputs にルール EDN 明示

注意事項

  • セッション内で state は保持される。クリーンな状態が必要なら datalog_reset
  • 完全リセット (local.json 含む) は datalog_reset fullReset=true (破壊的)
  • 可視化 HTML は /tmp/datalog_graph_latest.html に常時上書き
  • sync は差分同期ではない。取り直しは datalog_reset → sync を再実行
  • datalog_sync_* は freee API 認証前提。事前に freee_auth_status で確認

開発時の reconnect 最小化

原則: normal user 経路 (= MCP tool 連続呼び出し + 必要に応じた手編集) では reconnect 一切不要。reconnect が要るのは「コード変更で bin を入れ替える」開発作業時のみ。

reconnect 要否マトリクス:

変更内容 reconnect 理由
datalog_*_create/update/delete 等の MCP CRUD 不要 notifyChanged()refreshIdbRules() で in-memory cache 自動更新
datalog-store/*.json の手編集 不要 (local) / 要 (common 反映) listQueries/listIdbRules は file 毎回再読込。ただし IDB rule edn は CRUD 経由でのみ refresh されるので、手編集後は最初の CRUD or datalog_reset 等で発火する
bun run build:ui のみ (UI bundle 差分) 不要 loadBundle() は visualize 毎回 disk 読込 → 次の datalog_visualize で新 bundle 反映
bun run build 全体 (bin 再生成) MCP host が新 bin を読み直すため
新 MCP tool 追加 / Zod schema 変更 bin 再生成必須 + tool 一覧は connect 時取得

機能完了の前に必ず: 想定 user 操作 (例: create → test → list / reset → re-sync → visualize) を vitest で e2e shape のテスト にし、reconnect 無しで pass することを確認する。

statuteDescription (法文風 Markdown — audit query 必須)

audit query (= datalog_query_create / _update) で 必ず 埋める 3 つ目の description。 税理士 / 会計士 が 税法・会計基準 と 同じ 語彙・文体 で 要件を 読めるよう、 条文風 + 箇条書き の Markdown を 持つ。

書式規約 (= LLM が守る)

  • 各要件 を 箇条書き (-) で 分解
  • 入れ子で 「ただし」「除く」「ないし」「に限る」 を 表現
  • 数値要件 は > < = を 使う (= 「以上」 ではなく 記号 を 優先)
  • 主語省略禁止 (= 「明細が」「取引が」 等 を 毎回 明示)
  • 必須属性 は 太字 (= **取引先** のように)
  • 引用条文 (= 「消費税法 第30条第7項」 等) は 正確性が 確認できる場合のみ 末尾に 出典明記。 怪しいなら 省略 (= hallucination 防止)
  • 見出し (#) 不要 (= 条文 は 箇条書き 主体)
  • 2000 chars 以下 を 目安 (= 超えると validator が too-long warning)
  • raw HTML タグ 禁止 (= validator が sanitize-required warning)

3 系統 description の 役割分担

field 主読者 文体 長さ目安
userDescription 一般ユーザ 1〜3 行 平易な日本語
statuteDescription 税理士 / 会計士 条文風 Markdown、 箇条書き + 留保条件 中 (= 5〜30 行)
engineerDescription エンジニア / AI / LLM datalog 句の意図、 実装上の罠 短〜中

Few-shot 例

例 1 — deal_type_tax_purchase_mismatch (= 収入取引 に 課税仕入 明細、 税区分矛盾)

**要件**: 取引 (deal) の 種類 (収支タイプ) と、 取引内に 含まれる 明細 (deal_detail) の 税区分 が 整合 すること

**判定条件**:
- 取引 (deal) が **収入** に 分類されていること
- かつ、 取引内に **税区分 が 「課税仕入」** の 明細 (deal_detail) が 1 件以上 存在すること

**該当する場合は要修正**: 収入取引に 仕入 明細 が 混在しており、 消費税集計の 性質 (= 売上 vs 仕入) が 不整合。 仕訳 が 誤って 入力されている 可能性が 高く、 消費税申告 に 影響する。

(参考: 消費税法 第30条 仕入税額控除 / 同 第28条 売上 — 性質 区分 の 厳格性)

例 2 — taxable_purchase_no_partner (= 課税仕入 なのに 取引先 未紐付け)

**要件**: 課税仕入 の 取引 について、 **取引先** の 記録 が ある こと

**判定条件**:
- 取引 (deal) が **以下のいずれか** を 満たす こと:
  - 明細 (deal_detail) のうち、 税区分 が 「課税仕入」 であるもの が 1 件以上 存在する
- かつ、 上記取引 について **次のいずれも 満たさない**:
  - 取引 (deal) 自体 に **取引先 (partner)** が 紐付いている
  - 課税仕入 の 明細 (deal_detail) 自身 に **取引先 (partner)** が 紐付いている

**該当する場合は要調査**: 取引帳簿 の 真実性要件 (= 取引先名 の 記録義務) を 満たさない 可能性 が ある。

(参考: 消費税法 第30条第7項 仕入税額控除 の 帳簿要件)

例 3 — overtime_36agreement (= 36 協定超過)

**要件**: 法定外労働時間 が 月 ≤ 45 時間 で あること (= 36 協定 の 限度時間)

**判定条件**:
- 月次勤怠 (monthly_work_record) が **法定外労働時間** を 記録 している こと
- かつ、 月次勤怠 の 法定外労働時間 が **45 時間 を 超えている** こと

**該当する場合は要修正**: 36 協定 (= 労働基準法 第36条) で 定める 限度時間 を 超過 しており、 違反 の 可能性 が ある。

(参考: 労働基準法 第36条 / 同 施行規則 第16条)

強制ルール

  • audit query を 新規作成 (datalog_query_create) する 際 は statuteDescription を 必ず 埋める。 caller が 提供してない なら LLM が userDescription + engineerDescription から draft 生成 し、 条文風に 推敲して 埋める。
  • 既存 audit query を update (datalog_query_update) する 際 は、 既存 statuteDescription必ず 引き継ぐ (= 空のまま 上書き 禁止)。 内容変更 を 伴う update なら、 改訂版 を 同時に 生成して 埋める。
  • IDB rule (= datalog_idb_create / _update) では statuteDescription不要
  • 条文番号 / 法令名 は hallucination リスクが 高い ため、 確証 ある 場合のみ 明示。 怪しいなら 「(参考: 法令名 — 要 確認)」 と 留保 を 付ける か、 出典 を 省略。

関連スキル