@takagaki/cortex-decisions-viewer 0.4.74 → 0.4.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/build.js CHANGED
@@ -249,17 +249,8 @@ async function readFleetSources(repoRoot) {
249
249
  applicable: typeof p.applicable === "boolean" ? p.applicable : undefined,
250
250
  }))
251
251
  : undefined;
252
- // 能力→ツールの宣言マップ(例: { チャット: "slack", 開発: "none" })。"none" は未使用のグレー行表示に使う
253
- let tools;
254
- if (obj.tools != null && typeof obj.tools === "object" && !Array.isArray(obj.tools)) {
255
- tools = {};
256
- for (const [k, v] of Object.entries(obj.tools)) {
257
- if (typeof v === "string")
258
- tools[k] = v;
259
- }
260
- if (!Object.keys(tools).length)
261
- tools = undefined;
262
- }
252
+ // **宣言(tools)は読まない(#125 ⑤)。** engine が出力から落としたので、
253
+ // 残っているのは再生成前の古いスナップショットだけ。読まずに捨てる。
263
254
  // 能力ごとの導出済みの状態(#125①が載せ、#125②で画面の正本になった)。
264
255
  //
265
256
  // **列挙し忘れは静かに消える。** このマッピングは素通しではなく明示の許可リストで、
@@ -313,7 +304,7 @@ async function readFleetSources(repoRoot) {
313
304
  const secretSources = obj.secretSources && typeof obj.secretSources === "object"
314
305
  ? obj.secretSources
315
306
  : undefined;
316
- return { externalSources, internalSources, pipelines, tools, capabilities, fleetGeneratedAt, secretSources, checks, githubOwners, backlogTarget };
307
+ return { externalSources, internalSources, pipelines, capabilities, fleetGeneratedAt, secretSources, checks, githubOwners, backlogTarget };
317
308
  }
318
309
  /** パスがディレクトリとして存在するか */
319
310
  async function isDirectory(dirAbs) {
@@ -1193,7 +1184,6 @@ async function build(opts) {
1193
1184
  secretSources: fleet.secretSources,
1194
1185
  internalSources: fleet.internalSources,
1195
1186
  pipelines: fleet.pipelines,
1196
- tools: fleet.tools,
1197
1187
  capabilities: fleet.capabilities,
1198
1188
  checks: fleet.checks,
1199
1189
  githubOwners: fleet.githubOwners,
package/dist/render.js CHANGED
@@ -132,15 +132,19 @@ const CM_CAPS = [
132
132
  { cap: "チャット", icon: "💬", pipe: null },
133
133
  { cap: "開発", icon: "🐙", pipe: null },
134
134
  ];
135
- /** ツールid→表示名(接続マップのノードラベル用) */
135
+ /**
136
+ * ツールid→表示名(接続マップのノードラベル用)。
137
+ *
138
+ * **キーは ADAPTER_CATALOG の値だけ。** かつては jira / teams / box / local / none も
139
+ * 持っていたが、これらは宣言(Home.md の `tools`)にしか現れない値だった。宣言を撤去した
140
+ * いま、ここに来るのはカタログのアダプタだけ(`capabilityAdapter` がカタログ外を弾く)。
141
+ * **未対応ツールの表示名を復活させないこと**——名前を出した瞬間「対応している」と
142
+ * 読まれるのが #124 の症状そのもので、状態を出す場所すら無いまま名前だけが並ぶ。
143
+ */
136
144
  const CM_TOOL_LABEL = {
137
145
  backlog: "Backlog",
138
- jira: "Jira",
139
146
  "google-meet": "Google Meet",
140
- teams: "Teams",
141
147
  "google-drive": "Google Drive",
142
- box: "Box",
143
- local: "ローカル",
144
148
  figma: "Figma",
145
149
  slack: "Slack",
146
150
  github: "GitHub",
@@ -178,8 +182,8 @@ function sourceNeedsAttention(s) {
178
182
  * **クロージャを持たない純関数に保つこと**——外の変数を掴むと直列化した先で壊れる。
179
183
  *
180
184
  * 返す値:
181
- * "off" … 未使用(tools が none / enabled:false / 使っている証拠が無い)
182
- * "idle" … 宣言はあるが一度も繋がっていない(画面表記は「未接続」)
185
+ * "off" … 未使用(enabled:false / 使っている証拠が無い)
186
+ * "idle" … 設定はあるが一度も繋がっていない(画面表記は「未接続」)
183
187
  * "warn" … 問題あり
184
188
  * "ok" … 健全
185
189
  *
@@ -189,7 +193,7 @@ function sourceNeedsAttention(s) {
189
193
  * APIキー未投入で sync-backlog が失敗し、課題管理が警告色で線付きになった)。
190
194
  *
191
195
  * @param cap 能力名
192
- * @param ctx 生のフリートデータ { tools, internals, externals, pipes }。
196
+ * @param ctx 生のフリートデータ { capabilities, internals, externals, pipes }。
193
197
  * **解決済みの値を渡させない。** 振り分けや regex を呼び出し側に持たせると、
194
198
  * 呼び出し側ごとに写経が生まれ、片方が書き忘れると判定が静かに変わる。
195
199
  */
@@ -311,7 +315,6 @@ function capabilityState(cap, ctx) {
311
315
  // (読み手は未使用を信じて押さないので、行が消えるより悪い)
312
316
  const liveExts = ctx.externals.filter(function (s) { return externalCapability(s.type) === cap; });
313
317
  const isLive = cap === "チャット" || cap === "開発" || cap === CAP_OTHER;
314
- const toolVal = ctx.tools[cap];
315
318
  const internal = ctx.internals.filter(function (s) { return s.kind === cap; })[0];
316
319
  const pipeRe = PIPE[cap];
317
320
  // ---- #125②: 導出(capabilities)があるならそれが状態の正本 ----
@@ -346,12 +349,16 @@ function capabilityState(cap, ctx) {
346
349
  // 答えが割れることはない。**この2つを分けるなら、写像とここの両方を直すこと。**
347
350
  return "idle";
348
351
  }
349
- // ---- ここから下は capabilities を持たない古い fleet-status.json のための劣化表示 ----
350
- // 使用中か: tools宣言がnone以外 / internalSourceが存在(enabled:false除く) / ライブ参照の実体がある
351
- const used = (toolVal != null && toolVal !== "none") ||
352
- (internal != null && internal.enabled !== false) ||
352
+ // ---- ここから下は capabilities を持たない fleet-status.json のための防御 ----
353
+ //
354
+ // **宣言(tools)はもう見ない(#125 ⑤)。** 艦隊の fleet-status.json は全案件が
355
+ // capabilities を持つので、通常ここへは来ない。残してあるのは、生成前・取得失敗・
356
+ // 途中で壊れたデータで**画面が落ちない**ようにするためだけ——実体(同期ソースの行・
357
+ // ライブ参照)だけで判定し、無ければ「まだ繋がっていない」側へ倒す。
358
+ // **ここに宣言を読む道を足さないこと。** 足した瞬間、実態と食い違える経路が復活する。
359
+ const used = (internal != null && internal.enabled !== false) ||
353
360
  (isLive && liveExts.length > 0);
354
- const off = toolVal === "none" || (internal != null && internal.enabled === false) || !used;
361
+ const off = (internal != null && internal.enabled === false) || !used;
355
362
  // 問題あり: ライブ参照はgate≠okが1件以上、同期ソースは対応パイプラインの直近runがfailure
356
363
  const warn = !off &&
357
364
  (isLive
@@ -395,21 +402,38 @@ function capabilityState(cap, ctx) {
395
402
  : !syncedForReal);
396
403
  return off ? "off" : idle ? "idle" : warn ? "warn" : "ok";
397
404
  }
405
+ /**
406
+ * `repoBaseUrl`(https://github.com/owner/repo)からリポジトリ名だけを取り出す。
407
+ *
408
+ * **owner は出さない。** 画面は1案件ぶんしか映さないので、どの組織かは常に同じ——
409
+ * 出しても名前が長くなるだけで、読み手の判断は変わらない。
410
+ * 取れない形(URLが無い・GitHub 以外・末尾が空)では空文字を返し、呼び出し側が落とす。
411
+ *
412
+ * **github.com のURLだけを受ける。** 隣に GitHub のロゴを置くので、別ホストの名前を
413
+ * 通すとロゴが嘘になる。
414
+ */
415
+ function repoNameOf(repoBaseUrl) {
416
+ if (!repoBaseUrl)
417
+ return "";
418
+ const m = /^https?:\/\/github\.com\/(.+)$/.exec(String(repoBaseUrl));
419
+ if (!m)
420
+ return "";
421
+ // **末尾スラッシュを先に落とす。** 後にすると `repo.git/` の `.git` が残る
422
+ const slug = m[1].replace(/\/+$/, "").replace(/\.git$/, "");
423
+ const name = slug.split("/").pop() || "";
424
+ // owner だけの URL(github.com/owner)を名前と取り違えない
425
+ return slug.includes("/") ? name : "";
426
+ }
398
427
  function renderConnectionMap(site) {
399
428
  const internals = site.internalSources ?? [];
400
429
  const externals = site.externalSources ?? [];
401
430
  const pipes = site.pipelines ?? [];
402
- const tools = site.tools ?? {};
403
431
  const caps = site.capabilities ?? {};
404
432
  // **fleet のデータが1つでも取れているか、だけで判定する(#125②)。**
405
433
  // 素の Decisions ビュー(fleet-status.json が無い構成)ではマップを出さない現行の意図は
406
- // そのまま。`tools` を条件から外さないのは、capabilities を持たない古いスナップショットで
407
- // マップが丸ごと消えるのを防ぐため(移行中は両方が現物として存在する)。
408
- if (!internals.length &&
409
- !externals.length &&
410
- !pipes.length &&
411
- !Object.keys(tools).length &&
412
- !Object.keys(caps).length) {
434
+ // そのまま。宣言(`tools`)は条件から外した(⑤)——艦隊の fleet-status.json
435
+ // すべて capabilities を持つので、宣言はもうマップの有無を左右しない。
436
+ if (!internals.length && !externals.length && !pipes.length && !Object.keys(caps).length) {
413
437
  return null;
414
438
  }
415
439
  const hasCaps = Object.keys(caps).length > 0;
@@ -421,13 +445,12 @@ function renderConnectionMap(site) {
421
445
  const REPO_X = W / 2;
422
446
  CM_CAPS.forEach((c, i) => {
423
447
  const cx = 80 + i * 160;
424
- const toolVal = tools[c.cap];
425
448
  const internal = internals.find((s) => s.kind === c.cap);
426
449
  const isLive = c.cap === "チャット" || c.cap === "開発";
427
450
  const liveExts = c.cap === "チャット" ? slackExts : c.cap === "開発" ? ghExts : [];
428
451
  // **判定は capabilityState に一本化している。** 画面側(クライアントJS)も同じ関数を
429
452
  // 呼ぶので、ここで写経すると「同じ画面でマップが⚠️・直下が『接続OK』」が復活する。
430
- const st = capabilityState(c.cap, { tools, capabilities: caps, internals, externals, pipes });
453
+ const st = capabilityState(c.cap, { capabilities: caps, internals, externals, pipes });
431
454
  const off = st === "off";
432
455
  const idle = st === "idle";
433
456
  const warn = st === "warn";
@@ -437,22 +460,19 @@ function renderConnectionMap(site) {
437
460
  // ツールは「その能力をいま何で実現しているか」という差し替え可能なアダプタで、
438
461
  // 主語ではない。ツール名を主にすると、Teams のように複数の能力に使われるものが
439
462
  // 同じ名前で並び(会議とチャットに Teams が2つ)、何が繋がっているのか読めなくなる。
440
- // "none"(未使用の宣言)はツール名ではないので出さない。そのまま表示すると
441
- // 「ツール: none」という読めない行になる(実際にそう出ていた)
442
463
  //
443
- // **導出があるならカタログのアダプタを常に出す(#125②)。** 未接続でも「何で繋ぐのか」が
444
- // 分からないと入口にならない。カタログ外の値(teams 等)は capabilityAdapter が弾く。
464
+ // **カタログのアダプタを常に出す(#125②)。** 未接続でも「何で繋ぐのか」が
465
+ // 分からないと入口にならない。カタログ外の値は capabilityAdapter が弾く。
466
+ // capabilities を持たないデータでは、実体(同期ソースの行・ライブ参照)から拾う(⑤)。
445
467
  const rawTool = hasCaps
446
468
  ? capabilityAdapter(c.cap, caps)
447
- : toolVal && toolVal !== "none"
448
- ? toolVal
449
- : internal && internal.tool && internal.tool !== "none"
450
- ? internal.tool
451
- : isLive && liveExts.length
452
- ? c.cap === "チャット"
453
- ? "slack"
454
- : "github"
455
- : "";
469
+ : internal && internal.tool && internal.tool !== "none"
470
+ ? internal.tool
471
+ : isLive && liveExts.length
472
+ ? c.cap === "チャット"
473
+ ? "slack"
474
+ : "github"
475
+ : "";
456
476
  const toolName = rawTool ? CM_TOOL_LABEL[rawTool] ?? rawTool : "";
457
477
  const label = `${c.icon} ${c.cap}`;
458
478
  // 2行目=ツール名(小さく)/3行目=状態(未使用・自動連携なし・ライブ参照・最終同期)
@@ -508,10 +528,26 @@ function renderConnectionMap(site) {
508
528
  links.push(`<path class="cm-link ${state}" d="M ${cx} 78 C ${cx} 152, ${REPO_X} 162, ${REPO_X} 238"></path>`);
509
529
  }
510
530
  });
511
- const repoTitle = site.title.length > 24 ? site.title.slice(0, 23) + "…" : site.title;
531
+ // **中央ノードは GitHub のリポジトリ名そのもの(オーナー指示・2026-08-12)。**
532
+ // 以前は「📦 コンテキストリポジトリ(案件の表示名)」だったが、役割名は図の文脈から自明で、
533
+ // 表示名は上下のノードとも重複していた。実際に開く先はリポジトリなので、その名前で呼ぶ。
534
+ // 名前は repoBaseUrl から導出する(新しいデータは足さない)。取れない構成では
535
+ // 案件の表示名へ落とす——名前の無い箱にしない。
536
+ const repoName = repoNameOf(site.repoBaseUrl) || site.title;
537
+ const repoLabel = repoName.length > 24 ? repoName.slice(0, 23) + "…" : repoName;
538
+ // アイコンと名前を**まとめて中央へ置く**。text-anchor=middle だけだとアイコンの分だけ
539
+ // 全体が右に寄るので、合計幅を見積もってから左端を決める。日本語が混じる案件名へ
540
+ // フォールバックした場合に備え、全角は倍幅で数える(14px/700 の実測に合わせた概算)
541
+ const ICON = 18;
542
+ const GAP = 8;
543
+ const textW = [...repoLabel].reduce((w, ch) => w + (ch.charCodeAt(0) < 128 ? 8.2 : 14), 0);
544
+ const startX = REPO_X - (ICON + GAP + textW) / 2;
512
545
  const repo = `<g class="cm-repo">` +
513
546
  `<rect x="${REPO_X - 170}" y="240" width="340" height="52" rx="12"></rect>` +
514
- `<text class="cm-repo-label" x="${REPO_X}" y="272" text-anchor="middle">📦 コンテキストリポジトリ(${escapeHtml(repoTitle)})</text>` +
547
+ // ロゴはビルド時に埋め込む(ヘッダ右上・開発ソースのカードと同じ GITHUB_ICON)。外部通信は起きない。
548
+ // 入れ子の svg を transform で置き、色は cm-repo-logo の color を継ぐ(fill="currentColor")
549
+ `<g class="cm-repo-logo" transform="translate(${startX.toFixed(1)}, ${266 - ICON / 2 - 1})">${GITHUB_ICON}</g>` +
550
+ `<text class="cm-repo-label" x="${(startX + ICON + GAP).toFixed(1)}" y="272">${escapeHtml(repoLabel)}</text>` +
515
551
  `</g>`;
516
552
  return (`<svg class="cm-svg" viewBox="0 0 ${W} 312" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="データソースとの接続マップ">` +
517
553
  links.join("") +
@@ -554,15 +590,14 @@ function buildTutorialData(site, codeUrl, repoSlug) {
554
590
  },
555
591
  ];
556
592
  // ここから先は「書き込み・外部連携」の体験。案件の構成に無い能力のチャレンジは出さない(表示条件つき)
557
- const tools = site.tools ?? {};
558
593
  const caps = site.capabilities ?? {};
559
594
  const internals = site.internalSources ?? [];
560
595
  const externals = site.externalSources ?? [];
561
596
  /**
562
- * その能力が配線済みか。**導出(capabilities)があるならそれだけで決める(#125②)。**
597
+ * その能力が配線済みか。**導出(capabilities)だけで決める(#125②)。**
563
598
  * 宣言でチャレンジを出していた頃は、Backlog を宣言しているだけで鍵も入っていない案件に
564
599
  * 「記票してみよう」が出ていた——押すと必ず失敗する体験を最初の5分に置くことになる。
565
- * 導出が無い古いデータでは null を返し、呼び出し側が従来の判定へ落ちる。
600
+ * 導出が無いデータでは null を返し、呼び出し側が実体(行の有無)だけで判断する(⑤)。
566
601
  */
567
602
  const capOn = (cap) => {
568
603
  const s = capabilityRawState(cap, caps);
@@ -578,8 +613,7 @@ function buildTutorialData(site, codeUrl, repoSlug) {
578
613
  }
579
614
  // Backlogに記票する体験(課題管理にBacklogを使う案件のみ出す)
580
615
  const usesBacklog = capOn("課題管理") ??
581
- (tools["課題管理"] === "backlog" ||
582
- internals.some((s) => s.kind === "課題管理" && s.tool === "backlog" && s.enabled !== false));
616
+ internals.some((s) => s.kind === "課題管理" && s.tool === "backlog" && s.enabled !== false);
583
617
  if (usesBacklog) {
584
618
  specs.push({
585
619
  title: "Backlogに記票してみよう",
@@ -588,9 +622,7 @@ function buildTutorialData(site, codeUrl, repoSlug) {
588
622
  });
589
623
  }
590
624
  // 画面インベントリを読む体験(デザインを同期している案件のみ出す)
591
- const usesDesign = capOn("デザイン") ??
592
- ((tools["デザイン"] != null && tools["デザイン"] !== "none") ||
593
- internals.some((s) => s.kind === "デザイン" && s.enabled !== false));
625
+ const usesDesign = capOn("デザイン") ?? internals.some((s) => s.kind === "デザイン" && s.enabled !== false);
594
626
  if (usesDesign) {
595
627
  specs.push({
596
628
  title: "デザインを読んでみよう",
@@ -599,7 +631,7 @@ function buildTutorialData(site, codeUrl, repoSlug) {
599
631
  });
600
632
  }
601
633
  // 完了をSlackに知らせる体験(チャットにSlackを宣言している案件のみ出す。最終チャレンジ)
602
- const usesSlack = capOn("チャット") ?? (tools["チャット"] === "slack" || externals.some((s) => s.type === "slack"));
634
+ const usesSlack = capOn("チャット") ?? externals.some((s) => s.type === "slack");
603
635
  if (usesSlack) {
604
636
  specs.push({
605
637
  title: "Slackに完了を知らせよう",
@@ -1132,6 +1164,8 @@ a.ds-name:hover { text-decoration: underline; }
1132
1164
  @media (prefers-reduced-motion: reduce) { .cm-link.cm-ok { animation: none; } }
1133
1165
  .cm-repo rect { fill: var(--accent-weak); stroke: var(--accent); stroke-width: 1.5; }
1134
1166
  .cm-repo-label { font-size: 14px; font-weight: 700; fill: #1e3a8a; }
1167
+ /* GitHubロゴは fill="currentColor" なので、色は color で渡す(ラベルと同色に揃える) */
1168
+ .cm-repo-logo { color: #1e3a8a; }
1135
1169
  /* 未使用(非活性)行: アダプターとして存在するが、この案件では設定されていないもの */
1136
1170
  .ds-card.ds-inactive, .ds-pipe.ds-inactive { background: #f8f9fa; border-style: dashed; box-shadow: none; }
1137
1171
  .ds-inactive .ds-icon { filter: grayscale(1); opacity: .55; }
@@ -1463,11 +1497,16 @@ ${capabilityState.toString()}
1463
1497
  }
1464
1498
  // Cortex自体を操作するアクションと、接続・自動化の情報の受け皿(取り込み・接続マップ・データソース)。
1465
1499
  // 投入フォーム設定 or fleet-status由来の接続情報のどちらかがあれば出す。
1466
- // **fleet のデータが取れているか、だけで決める(#125②)。** 能力の宣言(tools)は廃止したので
1467
- // capabilities を条件に足す。**常に真にはしない**——fleet-status.json を持たない素の
1468
- // Decisions ビューでは操作タブそのものを出さない現行の意図を保つ
1500
+ // **fleet のデータが取れているか、だけで決める(#125②)。** 能力の宣言(tools)は撤去した(⑤)。
1501
+ // **常に真にはしない**——fleet-status.json を持たない素の
1502
+ // Decisions ビューでは操作タブそのものを出さない現行の意図を保つ。
1503
+ //
1504
+ // **鍵(secretKinds)も候補に数える。** 下の節(buildDataSources)は鍵だけの案件でも
1505
+ // 出す作りだが、タブが立たなければその判定に辿り着けない。宣言を撤去するまでは
1506
+ // 空の宣言オブジェクトが truthy でここを通していたため、鍵だけの案件が通っていたのは
1507
+ // **偶然**だった(テストもその偶然に乗っていた)。候補として明示する
1469
1508
  var HAS_FLEET = !!((SITE.internalSources || []).length || (SITE.externalSources || []).length ||
1470
- (SITE.pipelines || []).length || SITE.capabilities || SITE.tools);
1509
+ (SITE.pipelines || []).length || SITE.capabilities || (SITE.secretKinds || []).length);
1471
1510
  if (SITE.intake || HAS_FLEET) {
1472
1511
  TABS.push({ key: "ops", label: "操作" });
1473
1512
  }
@@ -2018,23 +2057,18 @@ ${capabilityState.toString()}
2018
2057
  * 誤った封鎖を生んでいた(実際に Slack を渡されているのに追加できない案件があった)。
2019
2058
  * カタログはこの製品が実装しているアダプタの定義なので、押して必ず失敗することはない。
2020
2059
  *
2021
- * **capabilities を持たない古いデータでは、従来どおり宣言で見る。** 移行前の
2022
- * スナップショットには宣言しか無く、そこで全面開放すると別ツール宣言の案件に
2023
- * 必ず400になるボタンが復活する。
2060
+ * **宣言によるゲートは撤去した(⑤)。** capabilities を持たないデータでも
2061
+ * カタログだけで判断する——判定の道は1本にする。
2024
2062
  */
2025
2063
  function capabilityUsable(cap, tool) {
2026
- var caps = fleetVal("capabilities") || {};
2027
- if (Object.keys(caps).length) return (ADAPTER_CATALOG[cap] || []).indexOf(tool) >= 0;
2028
- var v = (fleetVal("tools") || {})[cap];
2029
- return !v || v === "none" || v === tool;
2064
+ return (ADAPTER_CATALOG[cap] || []).indexOf(tool) >= 0;
2030
2065
  }
2031
2066
 
2032
2067
  /** 組み立てだけを行う(設置は呼び出し側)。ライブ差し替えで同じ節を作り直すため分けている */
2033
2068
  /** ツールid→表示名(クライアント側。接続マップの CM_TOOL_LABEL と同じ内容) */
2034
2069
  var CM_TOOL_LABEL_C = {
2035
- backlog: "Backlog", jira: "Jira", "google-meet": "Google Meet", teams: "Teams",
2036
- "google-drive": "Google Drive", box: "Box", local: "ローカル", figma: "Figma",
2037
- slack: "Slack", github: "GitHub", none: "",
2070
+ backlog: "Backlog", "google-meet": "Google Meet", "google-drive": "Google Drive",
2071
+ figma: "Figma", slack: "Slack", github: "GitHub",
2038
2072
  };
2039
2073
 
2040
2074
  /**
@@ -2073,11 +2107,6 @@ ${capabilityState.toString()}
2073
2107
  // 能力ごとの導出済みの状態(#125② の正本)。持たない古いデータでは {}
2074
2108
  var caps = fleetVal("capabilities") || {};
2075
2109
  var hasCaps = Object.keys(caps).length > 0;
2076
- // 能力→ツールの宣言(例: チャット: "none")。**capabilities が無いときだけ効く劣化表示。**
2077
- // "none" の能力は「未使用」のグレー行で存在だけ見せる
2078
- var tools = (!hasCaps && fleetVal("tools")) || {};
2079
- var chatOff = tools["チャット"] === "none";
2080
- var devOff = tools["開発"] === "none";
2081
2110
  // **この節に入るものが1つでもあるなら出す。**
2082
2111
  //
2083
2112
  // 条件を「データ行があるか」で書いていて、2回続けて漏らした——
@@ -2087,9 +2116,8 @@ ${capabilityState.toString()}
2087
2116
  //
2088
2117
  // 個別に足し続けると次も漏らすので、**この節に出す候補を列挙して判定する**形にする。
2089
2118
  var hasSecrets = ((SITE.secretKinds || []).filter(function (k) { return SECRET_FORMS[k]; })).length > 0;
2090
- // **fleet のデータが取れているか、だけを見る(#125②)。** 宣言(tools)は廃止したが、
2091
- // capabilities を持たない古いスナップショットで節ごと消えないよう条件には残す
2092
- var hasCapModel = hasCaps || Object.keys(tools).length > 0;
2119
+ // **fleet のデータが取れているか、だけを見る(#125②)。** 宣言(tools)は撤去した(⑤)
2120
+ var hasCapModel = hasCaps;
2093
2121
  // **投入の口も候補。** fleet-status.json はGitHub Actionsが夜間に生成するので、
2094
2122
  // 立ち上げ直後の案件には存在しない。それを理由に節ごと消すと、
2095
2123
  // 文字起こし・資料を入れる口が**一番必要な瞬間に**画面から無くなる
@@ -2246,7 +2274,7 @@ ${capabilityState.toString()}
2246
2274
  });
2247
2275
  }
2248
2276
 
2249
- if (sources.length || chatOff || devOff) {
2277
+ if (sources.length) {
2250
2278
  // **見出しは付けない。** #101 で行は能力ごとのモーダルへ移ったので、
2251
2279
  // ここに見出しを置くと中身ゼロの見出しが節の先頭に立つ(実際に8案件すべてで出ていた)
2252
2280
  // ツール(type)別にグループ化し、多いグループ(4件以上)は折りたたむ
@@ -2258,9 +2286,6 @@ ${capabilityState.toString()}
2258
2286
  g.items.push(s);
2259
2287
  });
2260
2288
  var UNIT = { "slack": "チャンネル", "github-issues": "リポジトリ", "github-discussions": "リポジトリ" };
2261
- // チャット未使用: Slackグループの位置(先頭)に「存在するが未設定」のグレー行を出す
2262
- // **アンカーは渡さない**(タイルが飛び先。同 §id の重複を作らない)
2263
- if (chatOff) pushCap("チャット", inactiveSourceRow("💬", "チャット", null));
2264
2289
  groups.forEach(function (g) {
2265
2290
  var meta = DS_TYPE[g.type] || { icon: "🔗", label: g.type || "その他" };
2266
2291
  var cards = el("div", { class: "ds-list" });
@@ -2297,8 +2322,6 @@ ${capabilityState.toString()}
2297
2322
  }
2298
2323
  });
2299
2324
  }
2300
- // 開発未使用: グレー行も能力の受け皿へ
2301
- if (devOff) pushCap("開発", inactiveSourceRow("🐙", "GitHub", null));
2302
2325
 
2303
2326
  // **追加フォームはグループの外に置く。** グループは0件だと描かれず、4件以上だと
2304
2327
  // 畳まれる(既定は閉)。中に入れると**新規案件(0件)=一番追加したい場面**で消える。
@@ -2450,10 +2473,11 @@ ${capabilityState.toString()}
2450
2473
  // 両方に出すと、同じ失敗が1画面に2回並ぶ。実行状況はパイプライン節に任せ、
2451
2474
  // ここでは直し方だけをその行に添える(下の renderPipelines で使う)
2452
2475
  var PIPE_CHECK = { "update-gold": "nightly_decisionlog", "sync-backlog": "nightly_backlog", "sync-designs": "figma_sync" };
2476
+ // **engine が出しているチェックだけを並べる。** meeting_minutes / channels_json は
2477
+ // 同語反復(適用されるときは必ず ok)だったので engine が撤去した(#125 ⑤)。
2478
+ // 割り当ての無いチェックは消えず、下の「セットアップの残り」へ落ちる
2453
2479
  var CHECK_CAP = {
2454
2480
  backlog_secrets: "課題管理", backlog_synced: "課題管理",
2455
- meeting_minutes: "会議",
2456
- channels_json: "チャット",
2457
2481
  figma_token: "デザイン", figma_inventory: "デザイン",
2458
2482
  dev_sources_registered: "開発", dev_issues_derived: "開発",
2459
2483
  };
@@ -2515,12 +2539,12 @@ ${capabilityState.toString()}
2515
2539
  // 上のマップと直下の一覧で語が割れない
2516
2540
  parts.push((CAP_STATE_MARK[st] || "") + (CAP_STATE_TEXT[derived] || "状態が判定できません"));
2517
2541
  } else {
2518
- // **off には2つの意味が同居している。** 「使わないと宣言した」(tools が none)と
2542
+ // **off には2つの意味が同居している。** 「意図的に止めている」(enabled:false)と
2519
2543
  // 「まだ何も分かっていない」(fleet-status.json を夜間バッチがまだ作っていない)。
2520
2544
  // 後者を「未使用」と書くと、立ち上げ直後の案件で顧客が
2521
2545
  // 「Backlog も Slack も使わない案件だ」と読む——繋ぐ前提で契約しているのに
2522
- var declaredOff = tools[cap] === "none" || (internal != null && internal.enabled === false);
2523
- parts.push(st === "off" && !declaredOff ? "未設定" : STATE_LABEL[st] || "");
2546
+ var intentionalOff = internal != null && internal.enabled === false;
2547
+ parts.push(st === "off" && !intentionalOff ? "未設定" : STATE_LABEL[st] || "");
2524
2548
  }
2525
2549
  // 読み取り範囲。**この画面は顧客も見る。何をGoldに読んでいるかの開示が主眼**
2526
2550
  //
@@ -2564,11 +2588,9 @@ ${capabilityState.toString()}
2564
2588
  // ——未接続のタイルは「これから繋げる入口」で、消すと繋ぎ方を知る場所が無くなる。
2565
2589
  // カタログは製品の定義なので、案件データが何であってもここは変わらない
2566
2590
  var adapters = (hasCaps && ADAPTER_CATALOG[cap]) || [];
2567
- // 旧データ(capabilities 無し)は従来どおり「宣言があれば行が無くても出す」
2568
- var declared = tools[cap] != null;
2569
- // **手動投入はアダプタに依存しない。** 宣言や行の有無で弾くと、
2591
+ // **手動投入はアダプタに依存しない。** 行の有無で弾くと、
2570
2592
  // fleet-status.json がまだ無い新規案件で投入の口ごと消える
2571
- if (!adapters.length && !items.length && !declared && !manualBox[cap]) return;
2593
+ if (!adapters.length && !items.length && !manualBox[cap]) return;
2572
2594
  anyCap = true;
2573
2595
  sec.appendChild(el("div", { class: "ds-cap", text: cap }));
2574
2596
  if (adapters.length) {
@@ -2579,11 +2601,11 @@ ${capabilityState.toString()}
2579
2601
  adapters.forEach(function (a, ai) {
2580
2602
  renderAdapterTile(cap, items, a, a === active ? capabilityDisplayState(cap, caps) : "unset", ai);
2581
2603
  });
2582
- } else if (items.length || declared) {
2583
- // 旧データ: アダプタのタイルは宣言か行があるときだけ。手動投入だけが理由でここへ来た
2584
- // 新規案件に、中身の無い「会議」タイルを立てても押す先が無い
2585
- var legacyTool = tools[cap] && tools[cap] !== "none" ? tools[cap] : "";
2586
- renderAdapterTile(cap, items, legacyTool, null, 0);
2604
+ } else if (items.length) {
2605
+ // capabilities を持たないデータ: アダプタのタイルは行があるときだけ。手動投入だけが
2606
+ // 理由でここへ来た新規案件に、中身の無い「会議」タイルを立てても押す先が無い。
2607
+ // アダプタ名は名乗らない(宣言を撤去したので、名乗れる根拠がここには無い・⑤)
2608
+ renderAdapterTile(cap, items, "", null, 0);
2587
2609
  }
2588
2610
  renderManualTile(cap);
2589
2611
  });
@@ -2591,7 +2613,7 @@ ${capabilityState.toString()}
2591
2613
  /**
2592
2614
  * 能力×アダプタ1件のタイル。
2593
2615
  *
2594
- * @param adapter アダプタ識別子(カタログ由来。旧データでは宣言 tools 由来)
2616
+ * @param adapter アダプタ識別子(カタログ由来。capabilities が無いデータでは空)
2595
2617
  * @param derived engine が導出した5値。旧データ(capabilities 無し)では null
2596
2618
  * @param ai その能力の中での並び順。先頭だけが接続マップの飛び先(ds-<能力>)になる
2597
2619
  */
@@ -2600,7 +2622,7 @@ ${capabilityState.toString()}
2600
2622
  var st = derived === "unset" && capabilityDisplayState(cap, caps) !== "unset"
2601
2623
  // カタログにあるが導出が答えていないアダプタ(今日は発生しない。#107 の前方互換)
2602
2624
  ? "idle"
2603
- : capabilityState(cap, { tools: tools, capabilities: caps, internals: internals, externals: sources, pipes: pipes });
2625
+ : capabilityState(cap, { capabilities: caps, internals: internals, externals: sources, pipes: pipes });
2604
2626
  // **ロゴはタイルの左に独立して置く。** 名前の行に入れると名前の高さに
2605
2627
  // 縛られて小さくなる。ロゴで能力を見分けられるのが一覧の主な用途
2606
2628
  var logo = ADAPTER_ICONS[adapter];
@@ -2647,7 +2669,8 @@ ${capabilityState.toString()}
2647
2669
  // **注記の条件は、入力欄を出す条件と同じにする(0.4.71)。** 別々にすると
2648
2670
  // 「入力欄はあるのに未設定だと言われない」「言われるのに入力欄が無い」のどちらかが起きる。
2649
2671
  // 0.4.70 は unset で入力欄を畳んでいたので注記も抑えていたが、案A で欄を出すようにしたため揃えた
2650
- var tokenRelevant = derived != null ? derived !== "off" : tools[cap] !== "none";
2672
+ // 導出が無いデータでは判断材料が無いので、注記を出す側(=入力欄と揃う側)に倒す
2673
+ var tokenRelevant = derived != null ? derived !== "off" : true;
2651
2674
  if (!tokenIsFallback && tokenRelevant && sk && (SITE.secretKinds || []).indexOf(sk) >= 0 && !((fleetVal("secretSources") || {})[sk])) {
2652
2675
  body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ トークンが未設定です" }));
2653
2676
  }
@@ -2772,17 +2795,8 @@ ${capabilityState.toString()}
2772
2795
  var sec = buildDataSources();
2773
2796
  if (sec) app.appendChild(sec);
2774
2797
  }
2775
- // 未使用能力のプレースホルダ行(アダプターとして存在するが、この案件では設定されていないもの)
2776
- function inactiveSourceRow(icon, typeLabel, anchorId) {
2777
- return el("div", { class: "ds-card ds-inactive", id: anchorId }, [
2778
- el("div", { class: "ds-card-main" }, [
2779
- el("span", { class: "ds-icon", text: icon }),
2780
- el("span", { class: "ds-type", text: typeLabel }),
2781
- el("span", { class: "ds-name", text: "未使用" }),
2782
- ]),
2783
- el("span", { class: "ds-inactive-note", text: "この案件では設定されていません" }),
2784
- ]);
2785
- }
2798
+ // **「未使用」のグレー行は持たない(#125 ⑤)。** 宣言が none の能力にだけ出していた行で、
2799
+ // 宣言を撤去したいま出す条件が無い。未接続の能力は能力タイル(常時表示)が状態と入口を持つ。
2786
2800
  // 外部ソース1件のカード(グループ化表示・展開表示の両方から使う)
2787
2801
  /**
2788
2802
  * Gold昇格の切り替えボタン。押すと設定ファイル(channels.json)が書き換わる。
@@ -3156,9 +3170,9 @@ ${capabilityState.toString()}
3156
3170
  /**
3157
3171
  * 外部ソースの追加フォーム(Slack・GitHub)。
3158
3172
  *
3159
- * **カタログでゲートする(#125②)。** 宣言(tools)は廃止した。古いデータのときだけ
3160
- * 従来どおり宣言を見る(capabilityUsable の項を参照)——別のツールが宣言されている
3161
- * 案件ではサーバが必ず400で断るので、押せるのに必ず失敗するボタンを出さない。
3173
+ * **カタログでゲートする(#125②)。** 宣言(tools)によるゲートは撤去した(⑤)。
3174
+ * カタログはこの製品が実装しているアダプタの定義なので、出したボタンが
3175
+ * 「押せるのに必ず失敗する」ことはない。
3162
3176
  */
3163
3177
  function renderExternalAdders() {
3164
3178
  var box = el("div", {});
@@ -3990,8 +4004,11 @@ ${capabilityState.toString()}
3990
4004
  // 画面で分かるようにする(接続状況は夜間バッチのスナップショットなので、
3991
4005
  // 保存の直後は前日の値のまま。時刻は上の節に出している)。
3992
4006
  var src = (fleetVal("secretSources") || {})[kind] || "";
4007
+ // **前方一致で見る。** #105 の移行期は「Secrets Manager(旧名)」も来る。
4008
+ // 完全一致にすると、旧名で動いている案件だけが repo secret と同じ見た目になり、
4009
+ // 「管理下にあるのに未移行」という一番知りたい状態が画面から消える
3993
4010
  var state = src
3994
- ? el("p", { class: "sec-state" + (src === "Secrets Manager" ? " is-managed" : ""),
4011
+ ? el("p", { class: "sec-state" + (src.indexOf("Secrets Manager") === 0 ? " is-managed" : ""),
3995
4012
  text: "現在: " + src + " から取得中" })
3996
4013
  : el("p", { class: "sec-state is-none", text: "現在: 未設定" });
3997
4014
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.4.74",
3
+ "version": "0.4.75",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,