Context

BizLP GAS Accounting に Backlog(課題管理ツール)との連携機能を新規実装する。 スプレッドシート上の不整合タスク(96_backlog_tasks)を Backlog API 経由で課題登録し、ステータスを双方向同期できるようにする。

既知の Spec/Code 不整合を一括登録する registerKnownIssues() も提供し、手動でのタスク起票コストを削減する。

変更対象ファイル

#ファイル変更内容
1000_infra/001_env.jsBacklog用スクリプトプロパティのアクセサ4件追加
2000_infra/002_constants.jsID_PREFIX_MAP に TASK_ エントリ追加
3100_config/101_sys_config.jsDDLスキーマ, config行, MST_DICT, SYS_DROP, メニュー, setVali 追加
4800_ops/804_backlog.js新規作成 - Backlog API連携モジュール
5900_test/901_test_runner.jsT9: Backlog連携テスト追加

実装ステップ

Step 1: 000_infra/001_env.js — Backlog プロパティアクセサ追加

setReceiptFolderId の後(L64付近)に4つのメソッドを追加:

backlogSpace: function () {
  return _getProps().getProperty('BACKLOG_SPACE') || '';
},
backlogApiKey: function () {
  return _getProps().getProperty('BACKLOG_API_KEY') || '';
},
backlogProjectId: function () {
  return _getProps().getProperty('BACKLOG_PROJECT_ID') || '';
},
backlogIssueTypeId: function () {
  return _getProps().getProperty('BACKLOG_ISSUE_TYPE_ID') || '';
},

パターン: 既存の geminiApiKey() / receiptFolderId() と同一。

Step 2: 000_infra/002_constants.js — ID_PREFIX_MAP 追加

ID_PREFIX_MAP 配列の末尾(15_mst_dictionary の後)に追加:

{ pattern: '96_backlog_tasks', prefix: 'TASK_', digit: 4, isDate: true }

isDate: true → ID形式は TASK_YYYYMMDD_NNNN

Step 3: 100_config/101_sys_config.js — DDL・メニュー・バリデーション

7箇所の変更が必要。

3a. config行の自動登録 (setupAllSchemas 内, SYS_TEST 付近)

if (!existKeys.includes('BKL_TASK')) confSheet.appendRow(['BKL_TASK', '', '96_backlog_tasks', 'Backlog課題管理']);

3b. schemas オブジェクトに追加 (WRK_RCPT の後)

'BKL_TASK': {
  headers: ["有効フラグ","タスクID","Backlog課題キー","ステータス","優先度","カテゴリ","対象ファイル","不整合ID","タイトル","詳細","関連仕様書","関連コード","担当者","期限","Backlog同期日時","備考"],
  color: "#795548"
},

3c. MST_DICT 自動挿入 (newEntries 配列の末尾)

['Backlogステータス', [['BKL_OPEN','未対応'], ['BKL_PROG','処理中'], ['BKL_DONE','処理済み'], ['BKL_CLOS','完了']]],
['Backlog優先度',     [['BKL_HI','高'], ['BKL_MID','中'], ['BKL_LO','低']]],
['Backlogカテゴリ',   [['BKL_SPEC','spec修正'], ['BKL_CODE','code修正'], ['BKL_REV','要検討']]],

3d. SYS_DROP に3列追加

dropHeaders 配列の末尾("UI有効フラグ" の後)に追加:

"UIBacklogステータス", "UIBacklog優先度", "UIBacklogカテゴリ"

formulas 配列の末尾(={TRUE;FALSE} の後)に3つのFILTER式追加:

`=IFERROR(FILTER('${dictName}'!D2:D, '${dictName}'!B2:B="Backlogステータス", ('${dictName}'!A2:A=TRUE)+('${dictName}'!A2:A="TRUE")),"")`, // AJ列
`=IFERROR(FILTER('${dictName}'!D2:D, '${dictName}'!B2:B="Backlog優先度", ('${dictName}'!A2:A=TRUE)+('${dictName}'!A2:A="TRUE")),"")`, // AK列
`=IFERROR(FILTER('${dictName}'!D2:D, '${dictName}'!B2:B="Backlogカテゴリ", ('${dictName}'!A2:A=TRUE)+('${dictName}'!A2:A="TRUE")),"")`, // AL列

3e. setVali 追加 (WRK_RCPT の後)

setVali('BKL_TASK', 4, 'AJ', '96_backlog_tasks');  // ステータス → UIBacklogステータス
setVali('BKL_TASK', 5, 'AK', '96_backlog_tasks');  // 優先度 → UIBacklog優先度
setVali('BKL_TASK', 6, 'AL', '96_backlog_tasks');  // カテゴリ → UIBacklogカテゴリ

flagTabs 配列に 'BKL_TASK' を追加(有効フラグの TRUE/FALSE バリデーション)。

3f. スキーマUI設定 — inputCols / autoCols (WRK_RCPT ブロックの後)

if (key === 'BKL_TASK') {
  sheet.getRange("N2:N").setNumberFormat('yyyy-mm-dd');       // 期限
  sheet.getRange("O2:O").setNumberFormat('yyyy-mm-dd hh:mm'); // Backlog同期日時
  inputCols = [1,4,5,6,7,8,9,10,11,12,13,14,16]; // 入力列
  autoCols = [2,3,15]; // 自動列: タスクID, Backlog課題キー, 同期日時
}

3g. メニュー追加 — ⚙️ メンテナンス に3件

既存の .addItem('🔎 ...', 'checkInvStlConsistency').addToUi() の間に:

.addSeparator()
.addItem('📋 Backlog課題を登録', 'syncTasksToBacklog')
.addItem('📋 Backlogステータス同期', 'pullBacklogStatus')
.addItem('📋 不整合タスク自動生成', 'registerKnownIssues')

Step 4: 800_ops/804_backlog.js — 新規作成

4a. backlogFetch_(method, path, payload) — API共通ヘルパー

  • Env.backlogSpace() / Env.backlogApiKey() で認証情報取得
  • 未設定時は SpreadsheetApp.getUi().alert() でセットアップ手順案内 → return null
  • URL: https://${space}/api/v2/${path}?apiKey=${key} (space はフルドメイン)
  • POST時: contentType: 'application/json', payload: JSON.stringify(payload)
  • GET時: method: 'get'
  • muteHttpExceptions: true
  • 503リトライ: 3回、Utilities.sleep(5000) 間隔 (502_receipt_reader.js のパターンを踏襲)
  • 200以外はエラーログ出力 + throw
  • 返却: JSON.parse(response.getContentText())

4b. syncTasksToBacklog()

  1. getWebSpreadsheet_()Utils.getSheetByKey('BKL_TASK', '96_backlog_tasks')
  2. ヘッダー行からカラムインデックスをビルド (ヘッダー名ベースの indexOf)
  3. 有効フラグ=TRUE かつ Backlog課題キー空の行をフィルタ
  4. 各行について:
    • priorityId マッピング: {'高':2, '中':3, '低':4} (Backlog API仕様)
    • description テンプレート組み立て(## 不整合内容 / ## 修正内容 / ## 関連ファイル)
    • backlogFetch_('post', 'issues', { projectId, summary, description, issueTypeId, priorityId, dueDate })
    • 返却 issueKey をシートに書き戻し(Backlog課題キー列)
    • Backlog同期日時に new Date() を書き戻し
    • Utilities.sleep(1000) レート制限
  5. toast で結果サマリー表示

4c. pullBacklogStatus()

  1. 96タブから Backlog課題キーが空でない行を取得
  2. 各行: backlogFetch_('get', 'issues/' + issueKey) でステータス取得
  3. Backlog JP locale の status.name がそのまま 未対応/処理中/処理済み/完了 に対応
  4. シートのステータス列を更新、Backlog同期日時を更新
  5. Utilities.sleep(1000) レート制限
  6. toast で結果表示

4d. registerKnownIssues()

  1. 96タブの既存データを読み込み、不整合ID列で Set を構築(重複チェック用)
  2. 3件の既知の不整合を定義:
    • #6: noCash cashPlug補正未実装, 06_datamart_ingest.js, 高, spec修正
    • #7: Finance ORD開始年月のDate→YYYY-MM変換, 04_bat_rpa.js:1557, 中, code修正
    • #11: STL決済口座ハードコード定数化, 08_subledger_engine.js:101, 低, code修正
  3. 不整合IDが既存 Set に含まれていなければ追加
  4. タスクID生成: TASK_YYYYMMDD_NNNN (列Bスキャンで最大連番+1)
  5. シート末尾に書き込み (列Bで最終行判定 → Utils.getTrueLastRow(sheet, 2))
  6. toast で結果表示

Step 5: 900_test/901_test_runner.js — T9テスト追加

runAllTests() に T9 呼び出しを追加:

T9-01: registerKnownIssues 実行後、96タブに3件存在
T9-02: 不整合ID #6, #7, #11 が存在
T9-03: 重複実行しても行数が増えない(冪等性)
T9-04: 有効フラグが全てTRUE
T9-05: タスクIDがTASK_形式

再利用パターン

この実装で踏襲する既存パターンの一覧。今後の同種開発で参照できる。

DDLスキーマ追加手順 (101_sys_config.js)

  1. setupAllSchemas() 内の config行自動登録セクションに confSheet.appendRow 追加
  2. schemas オブジェクトにヘッダー配列+色を追加
  3. 必要に応じて newEntries (MST_DICT) にプルダウン値追加
  4. SYS_DROPdropHeaders + formulas に列追加
  5. setVali() でターゲットシートの列にバリデーション設定
  6. inputCols / autoCols で入力・自動列の背景色設定

外部API連携パターン (502_receipt_reader.js 準拠)

  1. Env モジュールにスクリプトプロパティのアクセサ追加
  2. UrlFetchApp.fetch() + muteHttpExceptions: true
  3. 503リトライループ (最大3回, 5秒間隔)
  4. レスポンスの getResponseCode() + JSON.parse(getContentText())
  5. 未設定時は ui.alert() で案内して return

メニュー追加手順 (101_sys_config.js onOpen)

  1. 既存メニューの .addToUi() の前に .addSeparator() + .addItem() を追加
  2. 関数名はグローバルスコープに公開(GAS の制約)

検証手順

  1. npm run push:dev で開発環境にデプロイ
  2. GASエディタで setupAllSchemas 実行 → 96_backlog_tasks タブが作成されること
  3. メニュー「⚙️ メンテナンス」に Backlog 関連3項目が表示されること
  4. 「📋 不整合タスク自動生成」実行 → 3件投入、再実行で増えないこと
  5. テスト: runAllTests で T9 が PASS すること
  6. (APIキー設定済みの場合) 「📋 Backlog課題を登録」で課題作成 + キー書き戻し確認
  7. (APIキー設定済みの場合) 「📋 Backlogステータス同期」でステータス取得確認

セットアップ要件

GAS の「プロジェクトの設定」→「スクリプトプロパティ」に以下を設定:

プロパティ説明
BACKLOG_SPACEBacklog スペースのフルドメインmycompany.backlog.com
BACKLOG_API_KEYBacklog API キー(ユーザー設定画面から取得)
BACKLOG_PROJECT_ID対象プロジェクトの数値ID12345
BACKLOG_ISSUE_TYPE_ID使用する課題種別の数値ID67890

実装プロンプト(Claude Code 用)

あなたはGAS会計システム(bizlp-gas-accounting)のシニア開発者です。
案件 MAS-198「Backlog API連携」を本仕様書「## 実装ステップ」Step 1-5 に従って実装してください。

## Phase 1: 実行前タスク(Read で実物を確認。行番号・関数名は推測しない)

1. `000_infra/001_env.js` — 既存の `geminiApiKey()` / `receiptFolderId()` / `setReceiptFolderId()` の実装パターン(`_getProps()` 経由・デフォルト空文字)を確認。Backlog 用アクセサ4件は同一パターンで追加。
2. `000_infra/002_constants.js` — `ID_PREFIX_MAP` 配列の既存エントリ(特に `15_mst_dictionary` のエントリ)と、`isDate: true` 形式の ID 採番パターンを確認。
3. `100_config/101_sys_config.js` — 以下を特定:
   - `schemas` オブジェクト内の `WRK_RCPT` エントリ(新エントリ `BKL_TASK` はこの直後に追加)
   - `setupAllSchemas()` 内の config行自動登録セクションで `SYS_TEST` が登録される箇所
   - `newEntries`(MST_DICT 追加用)配列の末尾
   - `SYS_DROP` 生成セクションの `dropHeaders` / `formulas` 定義
   - `setVali(...)` 呼び出し群の位置(`WRK_RCPT` 付近)
   - `flagTabs` 配列の位置
   - `inputCols` / `autoCols` を設定する分岐(`WRK_RCPT` ブロック)
   - `onOpen()` のメニュー定義(`⚙️ メンテナンス` カテゴリ、`.addToUi()` の直前)
4. `500_import/502_receipt_reader.js` — 外部 API 連携の既存パターン(`UrlFetchApp.fetch()` + `muteHttpExceptions: true` + 503 リトライ 3 回・5 秒間隔)を確認し、`backlogFetch_` ヘルパーで踏襲。
5. `000_infra/004_utils.js` — `Utils.getSheetByKey` / `Utils.getTrueLastRow` / `Utils.auditLog` のシグネチャを確認。列 B の末尾行スキャンで ID 採番する。
6. `900_test/901_test_runner.js` — 既存テスト T1-T8 の構造(関数名・assert パターン)を確認し、T9 を同形式で追加。

## Phase 2: 修正対象ファイル

- `000_infra/001_env.js` — Backlog プロパティアクセサ 4 件追加(Step 1)
- `000_infra/002_constants.js` — `ID_PREFIX_MAP` に `TASK_` エントリ追加(Step 2)
- `100_config/101_sys_config.js` — DDL・MST_DICT・SYS_DROP・setVali・メニュー追加(Step 3a-3g)
- `800_ops/804_backlog.js`(新規) — API ヘルパー・同期関数 3 件(Step 4a-4d)
- `900_test/901_test_runner.js` — T9 追加(Step 5)

## Phase 3: 実装内容

本仕様書「## 実装ステップ」Step 1 ~ Step 5 のコードブロックをそのまま適用する。重要ポイントのみ再掲:

### Step 4 (800_ops/804_backlog.js 新規作成) の注意点

- `backlogFetch_` は空の `BACKLOG_SPACE` / `BACKLOG_API_KEY` 時に `SpreadsheetApp.getUi().alert()` でセットアップ手順案内 → return null。Utils.logError に投げない(ユーザー側の初期設定未完了は例外ではないため)。
- URL: `https://${space}/api/v2/${path}?apiKey=${key}`。POST 時は `apiKey` を URL クエリに残しつつ、body に JSON で他パラメータ。
- `syncTasksToBacklog`: 書き込み順序は **Backlog 課題作成 → キー書き戻し → 同期日時書き戻し**。途中で失敗した場合、`Backlog課題キー` が空のままになるため、再実行で重複登録されるリスク。対策: API 成功後に**トランザクション的**にシートへ書き戻し(失敗時は `Utils.logError` + スキップ、次行へ)。
- `pullBacklogStatus`: Backlog の `status.name` が日本語 (`未対応` / `処理中` / `処理済み` / `完了`) で返るため、MST_DICT の `Backlogステータス` 表示値とそのまま一致。マッピング不要。
- `registerKnownIssues`: 3 件の既知不整合は仕様書「Step 4d」の ID(`#6` / `#7` / `#11`)で固定。既存データに同じ不整合 ID があればスキップ(冪等性)。

### ID 採番パターン(TASK_YYYYMMDD_NNNN)

- 列 B(タスクID)を下から走査して当日分の最大 `NNNN` を取得
- `NNNN` は 0001 ~ 9999 の 4 桁ゼロパディング
- `ID_PREFIX_MAP` の `isDate: true` フラグで日次リセット

## 制約

- 列番号ハードコード禁止。すべて `indexOf('タスクID')` 等のヘッダー名ベースで取得。
- 列 B を末尾行判定に使用(列 A はチェックボックスのため使用不可)。
- MCP `add_rows` 非使用(先頭に挿入されるため)。`Utils.getTrueLastRow(sheet, 2)` + `Range.setValues` で末尾書き込み。
- Backlog API のレート制限回避: 書き込み系は `Utilities.sleep(1000)` を各リクエスト間に挿入。
- セキュリティ: `BACKLOG_API_KEY` をコード・ログに出力しない。`backlogFetch_` 内でもエラーログはパスとステータスコードのみ記録。

## エッジケース

| 条件 | 動作 |
|------|------|
| `BACKLOG_SPACE` / `BACKLOG_API_KEY` 未設定 | `ui.alert()` でセットアップ案内 → 処理中断 |
| Backlog API が 503 | 3 回リトライ(5 秒間隔)。それでも失敗なら `Utils.logError` + throw |
| Backlog 課題作成後にキー書き戻し失敗 | `Utils.logError` + 次行へ。再実行時は `Backlog課題キー` 空の行が対象となり、API で二重登録リスクあり → 運用で警告 |
| `registerKnownIssues` の重複実行 | 不整合 ID Set で冪等性担保(0 件追加) |
| 96_backlog_tasks シート未作成 | `setupAllSchemas()` 未実行。Step 3a で config 行が登録されているため、再度 `setupAllSchemas` 実行で作成される |
| Backlog プロジェクトが削除済 | API が 404 → `Utils.logError` + 該当行スキップ |

## 実データ検証(実装直前に実施)

- Backlog API のテストスペース/プロジェクト/課題種別の ID を取得し、スクリプトプロパティに設定
- `priorityId` マッピング (`{'高':2, '中':3, '低':4}`) が対象 Backlog スペースで実際に有効か確認(カスタム優先度がある場合は要調整)
- 日本語 locale 以外のスペースでは `status.name` が英語になる可能性あり。テストスペースの locale を確認

## 動作確認

1. `npm run push:dev` → デプロイ
2. GAS スクリプトプロパティに `BACKLOG_SPACE` / `BACKLOG_API_KEY` / `BACKLOG_PROJECT_ID` / `BACKLOG_ISSUE_TYPE_ID` 設定
3. GAS エディタで `setupAllSchemas` 実行 → `96_backlog_tasks` タブが作成され、ヘッダー・MST_DICT・プルダウンが正しく設定されること
4. メニュー「⚙️ メンテナンス」に Backlog 関連 3 項目が表示されること
5. 「📋 不整合タスク自動生成」実行 → 3 件投入、再実行で 0 件追加(冪等)
6. 「📋 Backlog課題を登録」実行 → Backlog 側に課題が作成され、シート側に `Backlog課題キー` が書き戻されること
7. Backlog 側で課題のステータスを変更 → 「📋 Backlogステータス同期」で反映されること
8. T9 テスト全項目 PASS

### 拡張思考の使用状況

| フェーズ | 拡張思考 | 備考 |
|---------|---------|------|
| Phase 1(調査) | あり | 101_sys_config の 7 箇所の挿入位置特定・既存パターンの踏襲確認 |
| Phase 2(実装) | 部分的 | 各 Step の挿入位置を再確認する局面のみ。コード自体は仕様書に明記済み |

推奨実行モデル

工程推奨モデル理由
Phase 1 調査(101_sys_config の 7 箇所特定)Claude Opus 4.7複数セクションの位置関係・既存パターンの理解が必要
Step 1-3(Env・Constants・sys_config の編集)Claude Sonnet 4.6定型的な DDL 拡張だが挿入位置の正確性が必要
Step 4(804_backlog.js 新規作成)Claude Sonnet 4.6502_receipt_reader パターンを踏襲しつつ API 仕様を反映
Step 5(テスト追加)Claude Haiku 4.5既存 T1-T8 パターンの踏襲

変更履歴

日付変更内容
2026-04-13 23:55初版作成
2026-04-20実装プロンプト + 推奨実行モデル セクション追加