carta-reporting の内部向け補助スキル。Claude Code(AIが直接コードを操作できる機能)とのやり取り全般を制御します。具体的には、データ構造の確認、ユーザーが設定を変更する段階、データ変換の設定(絞り込み、並べ替え、計算式、集計など)、結果の確認、carta-reporting-excel への処理引き継ぎまでを担当します。 プレビューファイルが読み込まれた後に、carta-reporting(Claude Code 経由)から呼び出されます。ユーザーが直接実行するスキルではありません。
Internal subskill for carta-reporting. Drives the full Claude Code interaction: schema preview, customization checkpoint, transform config (filtering, sorting, formulas, aggregations), output preview, and hand-off to carta-reporting-excel. Invoked by carta-reporting (Claude Code path) after the preview file lands. Not for direct user invocation.
carta-reporting ステップ 4d(Claude Code / MARKDOWN パス)から呼び出されます。このセッションで既に解決した値を使用します:データファイルパス、corporation_id、user_report_pk、および _report_processor_path(親スキルが一度解決したスクリプトパス — find を再実行する代わりにこれを再利用します)。
変換を適用せずにデータファイルに対して report_processor.py を実行し、列名と型を抽出します。キャッシュされた _report_processor_path を再利用し、空の場合のみ find にフォールバックします:
UV_PYTHON_DOWNLOADS=never uv run "${_report_processor_path:-$(find ~ -name "report_processor.py" -path "*/carta-reporting/scripts/*" 2>/dev/null | head -1)}" <<'EOF'
{
"local_file": "<preview or full report file path>"
}
EOF
簡潔な結果サマリー(例:「保有者台帳レポートです — バックグラウンドでフルデータを読み込み中です」)で始まり、その後で列一覧を提示します。
列一覧の形式 — カテゴリ別にまとめた番号付きリスト。28列レポートの例:
**全28列:**
識別情報
1 — Stakeholder Name(テキスト) 2 — Grant ID(テキスト) 3 — Equity Plan(テキスト)
付与詳細
4 — Grant Date(日付) 5 — Grant Type(テキスト) 6 — Exercise Price(金額)
7 — Expiration Date(日付)
株式数
8 — Shares Issued(整数) 9 — Shares Vested(整数)
10 — Shares Unvested(整数) 11 — Shares Cancelled(整数)
...
グループ化のヒューリスティック(順に適用;一致しないものは「その他」):
dateinteger、列名に Share、Quantity、Count、Units を含むmoneypercentage列、フィルター、並び替え、形式をカバーする 1 つの「ユーザー質問」です。元のプロンプトで既に指定されていた内容に基づいて、コンテンツを適応させます。
プロンプトで詳細が指定されていた場合:
リクエストに基づいて以下を適用します:
- 列: Stakeholder Name、Grant Date、Shares Issued、Vested %
- フィルター: Vested % > 50%
- 並び替え: Grant Date 降順
これらの設定で続行 に返信して進めるか、何か調整してください。 また以下を追加することもできます:計算列(全体の %、累計、比率、差分)、または集計(合計行、グループごとの集計)。
プロンプトが曖昧だった場合:
どのようにカスタマイズしたいですか?全列、フィルターなし に返信してデータセット全体をそのまま取得するか、以下の何かを指定してください:
- 列 — どの列を含めるか
- フィルター — 例:「Vested % > 50」「Grant Date が 2024-01-01 以降」「Name に Smith を含む」
- 並び替え — 例:「Grant Date 最新順」「Shares Issued 降順」
- 計算式 — 全体の %、累計、比率(A ÷ B)、差分(行間の変化)
- 集計 — 合計行またはグループごとの集計
設定を構築する前に、ユーザーが言及したすべての列名およびフィルターターゲットをスキーマプレビューの実際の列名と照合します。ユーザーの用語はレポートの列名と異なることがよくあります(例:「Department」→「Cost Center」、「Employee」→「Stakeholder Name」、「Vest %」→「Vested %」)。
マッチングルール(順に適用):
ターム化できない場合、チェックポイントで候補を表示します:
⚠ 「Department」 はこのレポートに見つかりません。最も近いテキスト列:Cost Center、Equity Plan、Grant Type。どれをお考えですか、またはこのフィルターをスキップしますか?
最大 3 つの候補を、編集距離 / 単語の重なり順に表示します。一致しないターム(フィルターターゲットだった場合)をユーザーが解決するまで、設定に追加しないでください。
このチェックを以下に適用してください:明示的な列選択、フィルター列名、並び替え列名、計算式の取得元列。
常に主要識別子列(Stakeholder Name、Grant ID、または同等)とプロンプトで明示的に名付けられたすべての列を事前選択します。チェックポイントで事前選択を説明します:
事前選択列: 1 — Stakeholder Name、2 — Grant ID、6 — Exercise Price 全て で 28 列すべてを表示、番号を指定して追加(例:+8, 12, 13)、キーワードで検索(例:vesting)、または事前選択を yes で確認してください。
ユーザーがキーワードで返信した場合、マッチングする列のみを表示する「ユーザー質問」をもう一度呼び出し、確認するか追加の列を番号で選ぶよう求めます。これは 2 番目のチェックポイント質問が許可される唯一のケースです。
すべてのフィルタリングは report_processor.py で処理されます — Claude のメモリでフィルターを適用しないでください。
ユーザーリクエストをフィルターオブジェクトに変換します:
| ユーザーリクエスト | フィルターオブジェクト |
|---|---|
| 「vested % > 50」 | {"column": "Vested %", "op": ">", "value": 0.5} |
| 「grant date 2024-01-01 以降」 | {"column": "Grant Date", "op": ">", "value": "2024-01-01"} |
| 「name に Smith を含む」 | {"column": "Stakeholder Name", "op": "contains", "value": "Smith"} |
| 「ownership 5% 以上」 | {"column": "Fully Diluted %", "op": ">", "value": 0.05} |
サポートされる演算子:> < >= <= = != contains
列型が percentage の場合、パーセンテージ値を小数として渡します(例:50% → 0.5)。
フィルターがシートのすべての行を削除した場合、スクリプトは 0 行を返します — そのシートをスキップし、サマリーで注記します。
すべての列選択は report_processor.py で処理されます。目的の表示順で列名を渡します。スクリプトその順序を保持します。
ユーザーが明示的に要求しなかった場合でも、常に主要識別子列(Stakeholder Name、Grant ID、または同等)を含めます。
ユーザーリクエストを並び替えオブジェクトに変換します:
| ユーザーリクエスト | 並び替えオブジェクト |
|---|---|
| 「最新順」 | {"column": "Grant Date", "direction": "desc"} |
| 「最大順」 | {"column": "Shares Issued", "direction": "desc"} |
| 「アルファベット順」 | {"column": "Stakeholder Name", "direction": "asc"} |
複数キー:優先度順に複数のオブジェクトを渡します(最初 = 主ソートキー)。
計算列の計算は report_processor.py で処理されます。計算式は列選択の後に適用されます — 出力に含まれる列のみを参照できます。
計算式は、レポートデータに既に存在する値に対する 機械的な変換 です — スプレッドシートが資産規則について何も知らずにできる算術です。4 つのサポートされる演算子:
| 演算子 | 説明 | 必須フィールド |
|---|---|---|
pct_of_total |
各行が列の合計の何 %(このレポート内) | column |
running_sum |
累積合計(現在の並び替え順) | column |
ratio |
分子 ÷ 分母 | numerator、denominator |
delta |
行間の差分(現在の並び替え順) | column |
pct_of_total は存在する行の合計で除算します。これはレポートがこれの場合のみ会社全体です。_scope_<corporation_id> が is_limited_admin: true を報告する場合(carta-reporting のステップ 1b)、列を「% of {Share Class} Total」と名付けます — または複数のクラスの場合「% of In-Scope Total」— それを会社の所有権として提示することはありません。
ユーザーが資本構造、資産権、または所有権計算の理解を必要とする値を求めた場合、それを既に含む Carta レポートに直接彼らを指示します:
その計算は生データから安全に計算できない資産規則を関連しています。Carta の [レポート名] レポートには既にそれがあります — それを代わりに取得しますか?
例:
[
{"name": "% of Total Shares", "op": "pct_of_total", "column": "Shares Issued"},
{"name": "Cumulative Shares", "op": "running_sum", "column": "Shares Issued"}
]
集計は report_processor.py で処理されます。
サマリー行 — 底部に 1 つの合計行を追加;最初の列は「Total」と表示:
{"type": "summary", "columns": {"Shares Issued": "sum", "Vested %": "avg"}}
グループ化 — キー列で行を折りたたみ、ユニークな値ごとに 1 行:
{"type": "group_by", "group_by": "Stakeholder Name",
"columns": {"Shares Issued": "sum", "Grant Count": "count"}}
サポートされる演算子:sum avg min max count
carta-reporting の Script Reference を参照(全フィールド、複数シート、マージ、出力形式)。
ファントム資産ラベル: このセッションで _phantom_label_<corporation_id> が解決された場合(carta-reporting のステップ 1a)、この企業に対する report_processor.py のすべての呼び出し(スキーマプレビュー呼び出しと出力プレビュー / フルレポート呼び出しの両方)に "label_overrides": {"CBU": "<label>"} を含めます。企業キーの変数を使用します(例:_phantom_label_12345)複数企業フローを正しく処理するため。
実行後に常に stats を確認します:
missing_columns が空でない → data[sheet].columns から利用可能な列名をリストし、ユーザーに何を意図したのか質問してから修正された名前で再実行しますskipped_formulas が空でない → どの計算式を実行できなかったかをユーザーに伝えます(通常、取得元列が選択に含まれていません)filtered_row_count = 0 → 一致する行がない;フィルターをリラックスするか as_of_date を変更することを提案しますdisplayed_row_count < filtered_row_count → プレビューがアクティブ;フルデータを取得するには preview なしで再実行しますカスタマイズチェックポイント後、/tmp/carta_report_<user_report_pk>.json が存在するかどうかを確認します。準備ができている場合、これを local_file として使用します。そうでない場合、最大 5 回さらに 5 秒ごとにポーリングします。
スクリプトを "preview": 5 で実行します:
UV_PYTHON_DOWNLOADS=never uv run "${_report_processor_path:-$(find ~ -name "report_processor.py" -path "*/carta-reporting/scripts/*" 2>/dev/null | head
<!-- carta:plugin-version --> <carta-plugin>carta-cap-table:6.93.3</carta-plugin>
Called from carta-reporting step 4d (Claude Code / MARKDOWN path). Use values resolved earlier in this session: data file path, corporation_id, user_report_pk, and _report_processor_path (the script path the parent skill resolved once — reuse it instead of re-running find).
Run report_processor.py on the data file with no transforms to extract column names and types. Reuse the cached _report_processor_path; only fall back to find if it is empty:
UV_PYTHON_DOWNLOADS=never uv run "${_report_processor_path:-$(find ~ -name "report_processor.py" -path "*/carta-reporting/scripts/*" 2>/dev/null | head -1)}" <<'EOF'
{
"local_file": "<preview or full report file path>"
}
EOF
Lead with a brief result summary (e.g. "Here's your Securities Ledger report — loading full data in the background."), then present the column inventory.
Column inventory format — numbered list, grouped by category. Example for a 28-column report:
**All 28 columns:**
Identity
1 — Stakeholder Name (text) 2 — Grant ID (text) 3 — Equity Plan (text)
Grant details
4 — Grant Date (date) 5 — Grant Type (text) 6 — Exercise Price (money)
7 — Expiration Date (date)
Share counts
8 — Shares Issued (integer) 9 — Shares Vested (integer)
10 — Shares Unvested (integer) 11 — Shares Cancelled (integer)
...
Grouping heuristics (apply in order; unmatched → "Other"):
dateinteger, name contains Share, Quantity, Count, UnitsmoneypercentageOne AskUserQuestion covering columns, filters, sort, and format. Content adapts based on what the original prompt already specified.
When the prompt specified details:
Here's what I'll apply based on your request:
- Columns: Stakeholder Name, Grant Date, Shares Issued, Vested %
- Filter: Vested % > 50%
- Sort: Grant Date descending
Reply Continue with these settings to proceed, or adjust anything. You can also add: formula columns (% of total, running sum, ratio, delta), or aggregations (totals row, group-by).
When the prompt was vague:
What would you like to customize? Reply all columns, no filters to get the full dataset as-is, or specify any of:
- Columns — which to include
- Filters — e.g. "Vested % > 50", "Grant Date after 2024-01-01", "Name contains Smith"
- Sort — e.g. "Grant Date newest first", "Shares Issued descending"
- Formulas — % of total, running sum, ratio (A ÷ B), delta (row-over-row change)
- Aggregations — totals row or group-by rollup
Before building the config, check every column name and filter target the user mentioned against the actual column names from the schema preview. The user's terminology often differs from the report's column names (e.g. "Department" → "Cost Center", "Employee" → "Stakeholder Name", "Vest %" → "Vested %").
Matching rules (apply in order):
When a term can't be matched, surface it in the checkpoint with candidates:
⚠ "Department" wasn't found in this report. Closest text columns: Cost Center, Equity Plan, Grant Type. Which did you mean, or should I skip this filter?
Show at most 3 candidates, ranked by edit distance / word overlap. If the unmatched term was a filter target, do not add it to the config until the user resolves it.
Apply this check to: explicit column selections, filter column names, sort column names, and formula source columns.
Always pre-select key identifier columns (Stakeholder Name, Grant ID, or equivalent) plus any columns explicitly named in the prompt. State what's pre-selected in the checkpoint:
Columns pre-selected: 1 — Stakeholder Name, 2 — Grant ID, 6 — Exercise Price Reply all for all 28 columns, add by number (e.g. +8, 12, 13), enter a keyword to filter (e.g. vesting), or confirm the pre-selection with yes.
If the user replies with a keyword, call AskUserQuestion once more showing only matching columns and ask them to confirm or add more by number. This is the only case where a second checkpoint question is allowed.
All filtering is handled by report_processor.py — do not apply filters in Claude's memory.
Translate user requests to filter objects:
| User request | Filter object |
|---|---|
| "vested % > 50" | {"column": "Vested %", "op": ">", "value": 0.5} |
| "grant date after 2024-01-01" | {"column": "Grant Date", "op": ">", "value": "2024-01-01"} |
| "name contains Smith" | {"column": "Stakeholder Name", "op": "contains", "value": "Smith"} |
| "ownership above 5%" | {"column": "Fully Diluted %", "op": ">", "value": 0.05} |
Supported ops: > < >= <= = != contains
Pass percentage values as decimals when the column type is percentage (e.g. 50% → 0.5).
When a filter removes all rows from a sheet, the script returns 0 rows — skip that sheet and note it in the summary.
All column selection is handled by report_processor.py. Pass column names in the desired display order; the script preserves that order.
Always include key identifier columns (Stakeholder Name, Grant ID, or equivalent) even if the user didn't explicitly request them.
Translate user requests to sort objects:
| User request | Sort object |
|---|---|
| "newest first" | {"column": "Grant Date", "direction": "desc"} |
| "largest first" | {"column": "Shares Issued", "direction": "desc"} |
| "alphabetical" | {"column": "Stakeholder Name", "direction": "asc"} |
Multi-key: pass multiple objects in priority order (first = primary sort key).
Formula computation is handled by report_processor.py. Formulas are applied after column selection — they can only reference columns that are included in the output.
Formulas are for mechanical transforms on values already present in the report data — arithmetic that any spreadsheet could do without knowing anything about equity rules. The four supported ops:
| Op | Description | Required fields |
|---|---|---|
pct_of_total |
Each row as % of the column's total in this report | column |
running_sum |
Cumulative total down the column (current sort order) | column |
ratio |
numerator ÷ denominator | numerator, denominator |
delta |
Row-over-row difference (current sort order) | column |
pct_of_total divides by the sum of the rows present, which is the whole company only when the
report is. If _scope_<corporation_id> reports is_limited_admin: true (step 1b of
carta-reporting), name the column "% of {Share Class} Total" — or "% of In-Scope Total" for more
than one class — and never present it as ownership of the company.
If a user asks for a value that requires understanding cap structure, equity rights, or ownership math, direct them to the Carta report that already contains it:
"That calculation involves equity rules that I can't safely compute from the raw data. Carta's [Report Name] report already has it — want me to pull that one instead?"
Example:
[
{"name": "% of Total Shares", "op": "pct_of_total", "column": "Shares Issued"},
{"name": "Cumulative Shares", "op": "running_sum", "column": "Shares Issued"}
]
Aggregation is handled by report_processor.py.
Summary row — appends one totals row at the bottom; first column reads "Total":
{"type": "summary", "columns": {"Shares Issued": "sum", "Vested %": "avg"}}
Group-by — collapses rows by a key column, one row per unique value:
{"type": "group_by", "group_by": "Stakeholder Name",
"columns": {"Shares Issued": "sum", "Grant Count": "count"}}
Supported ops: sum avg min max count
See Script Reference in carta-reporting for the full API (all fields, multi-sheet, merge, output format).
Phantom equity label: If _phantom_label_<corporation_id> was resolved in this session (Step 1a of carta-reporting), include "label_overrides": {"CBU": "<label>"} in every report_processor.py invocation for this corporation — both the schema preview call and the output preview/full-report call. Use the corporation-keyed variable (e.g. _phantom_label_12345) to correctly handle multi-corporation flows.
Always check stats after running:
missing_columns non-empty → list available column names from data[sheet].columns and ask the user which they meant, then re-run with the corrected nameskipped_formulas non-empty → tell the user which formulas couldn't run (usually the source column wasn't included in the selection)filtered_row_count = 0 → no rows matched; offer to relax the filter or change as_of_datedisplayed_row_count < filtered_row_count → preview is active; re-run without preview for full resultsAfter the Customization Checkpoint, check if /tmp/carta_report_<user_report_pk>.json exists. If ready, use it as local_file. If not, poll every 5 s up to 5 more attempts.
Run the script with "preview": 5:
UV_PYTHON_DOWNLOADS=never uv run "${_report_processor_path:-$(find ~ -name "report_processor.py" -path "*/carta-reporting/scripts/*" 2>/dev/null | head -1)}" <<'EOF'
{
"local_file": "<path>",
"columns": [...],
"filters": [...],
"sort": [...],
"preview": 5
}
EOF
Show first 5 rows of each processed sheet as a markdown table. Above each table write: N of M rows matched · K columns.
Ask:
Does this look right?
- Generate full report — all rows as a markdown table
- Excel — download as .xlsx
- Describe any change to filters, columns, or sorting — I'll update just that field and re-run the preview without restarting the whole checkpoint.
If the user requests a change, update only the affected config field and re-run the preview — do not restart the customization checkpoint.
If the user chooses Excel, invoke Skill(carta-cap-table:carta-reporting-excel).
$1,234.56.12.34% (script stores as decimal; multiply × 100 for display).MMM D, YYYY (e.g. May 3, 2026) — the Carta brand standard.原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。