@takagaki/cortex-decisions-viewer 0.12.127 → 0.12.129

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.
Files changed (2) hide show
  1. package/dist/render.js +357 -264
  2. package/package.json +1 -1
package/dist/render.js CHANGED
@@ -5634,6 +5634,10 @@ a.ds-tile:hover { text-decoration: none; }
5634
5634
  (flex: 1 1 100%)——.ds-toggle は横並びなので、隣の「誰が承認できるか」の説明と
5635
5635
  横に並ぶと、どちらも半分の幅に潰れる(.ds-gate-help と同じ理由) */
5636
5636
  .ds-connect-lead { flex: 1 1 100%; }
5637
+ /* 行の一手(.ds-gate-help)の文の途中に置くコピー・[接続する]・[トークンを入れる](c-07)。
5638
+ 段落の中に置くので inline。ボタンは role=button のリンクか button のまま */
5639
+ .ds-act-inline { display: inline-flex; align-items: center; flex-wrap: wrap; gap: 6px; margin: 0 0 0 6px; vertical-align: middle; }
5640
+ .ds-gate-help code.rt-copy-text { font-size: 12px; padding: 1px 8px; }
5637
5641
  .ds-toggle-btn { font-size: 12px; padding: 3px 10px; border-radius: 999px; border: 1px solid var(--border); background: #fff; color: var(--text); cursor: pointer; }
5638
5642
  .ds-toggle-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
5639
5643
  .ds-toggle-btn:disabled { opacity: .6; cursor: default; }
@@ -5682,6 +5686,9 @@ a.ds-tile:hover { text-decoration: none; }
5682
5686
  .badge.ds-workspace { background: transparent; color: var(--muted); border: 1px solid var(--border); }
5683
5687
  /* 仮置きの行の印(#388)。控えめに——本物が追いつけば消える印なので目立たせない */
5684
5688
  .badge.ds-pending-badge { background: transparent; color: var(--muted); border: 1px dashed var(--border); font-weight: 500; }
5689
+ /* 本物の登録一覧が届く前の行(前の晩の控え・#1328 c-10)。薄く出して「確かめています」と添える */
5690
+ .ds-row-unconfirmed { opacity: 0.5; }
5691
+ .ds-note-checking { color: var(--muted); }
5685
5692
  /* 接続済み Slack ワークスペースの一覧(#104)。追加フォームの中に置く */
5686
5693
  .ds-ws-list { display: flex; flex-direction: column; gap: 6px; margin: 8px 0 10px; }
5687
5694
  .ds-ws { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; font-size: 12.5px; }
@@ -6632,6 +6639,9 @@ ${calendarAddUrl.toString()}
6632
6639
  var DRIVE_INGEST_ACCOUNT = "cortex-notetaker@classmethod.jp";
6633
6640
  // Google Chat の「スペースにアプリを追加する」手順の画像(#389)。理由は
6634
6641
  // src/google-chat-guide-images.ts に書いてある
6642
+ // Google Chat アプリの表示名。**Marketplace 公開名(2026-09-03 オーナー決定で「Cortex Bot」から
6643
+ // 改称・#387)。** 行の一手(not_in_space)と依頼文が使う——直書きしない
6644
+ var GCHAT_APP_NAME = "Cortex by Classmethod";
6635
6645
  var GCHAT_SHOT_STEP1 = "${google_chat_guide_images_1.GCHAT_APP_STEP1}";
6636
6646
  var GCHAT_SHOT_STEP2 = "${google_chat_guide_images_1.GCHAT_APP_STEP2}";
6637
6647
  // 案件カレンダーに予定を作る手順の画像(#452)。理由は src/meet-guide-images.ts
@@ -6641,15 +6651,11 @@ ${calendarAddUrl.toString()}
6641
6651
  var FIGMA_SHOT_STEP2 = "${figma_guide_images_1.FIGMA_GUIDE_STEP2}";
6642
6652
  var FIGMA_SHOT_STEP3 = "${figma_guide_images_1.FIGMA_GUIDE_STEP3}";
6643
6653
 
6644
- function copyRow(text, label) {
6645
- // **input と button を使わない。** 追加フォームの中に置くので、input を足すと
6646
- // 「先頭がURL欄・末尾が名前欄」、button を足すと「最初のボタンが送信」という
6647
- // フォームの並びが崩れる(実際にテストが壊れて気づいた)。
6648
- // 表示は code、コピーは role="button" のリンクで代える。
6649
- var field = el("code", { class: "rt-copy-text", text: text });
6650
- // ボタンの語は既定「コピー」。何を写すかを言い分けたい所(#968 の欄)だけ渡す
6651
- var btn = el("a", { class: "ds-toggle-btn", role: "button", tabindex: "0", text: label || "コピー" });
6652
- var msg = el("div", { class: "ds-toggle-msg" });
6654
+ /**
6655
+ * コピーボタンの動き(copyRow と copyInline の共通部分)。clipboard が使えない環境では
6656
+ * 文字列を選択状態にして次の一手を伝える。
6657
+ */
6658
+ function bindCopy(btn, field, msg, text) {
6653
6659
  btn.addEventListener("click", function () {
6654
6660
  var done = function () { msg.className = "ds-toggle-msg is-ok"; msg.textContent = "✓ コピーしました。"; };
6655
6661
  var fallback = function () {
@@ -6671,9 +6677,38 @@ ${calendarAddUrl.toString()}
6671
6677
  } else fallback();
6672
6678
  } catch (e) { fallback(); }
6673
6679
  });
6680
+ }
6681
+
6682
+ function copyRow(text, label) {
6683
+ // **input と button を使わない。** 追加フォームの中に置くので、input を足すと
6684
+ // 「先頭がURL欄・末尾が名前欄」、button を足すと「最初のボタンが送信」という
6685
+ // フォームの並びが崩れる(実際にテストが壊れて気づいた)。
6686
+ // 表示は code、コピーは role="button" のリンクで代える。
6687
+ var field = el("code", { class: "rt-copy-text", text: text });
6688
+ // ボタンの語は既定「コピー」。何を写すかを言い分けたい所(#968 の欄)だけ渡す
6689
+ var btn = el("a", { class: "ds-toggle-btn", role: "button", tabindex: "0", text: label || "コピー" });
6690
+ var msg = el("div", { class: "ds-toggle-msg" });
6691
+ bindCopy(btn, field, msg, text);
6674
6692
  return el("div", { class: "rt-copy" }, [el("div", { class: "rt-url-row" }, [field, btn]), msg]);
6675
6693
  }
6676
6694
 
6695
+ /**
6696
+ * 行の一手(1 段落)の中に置く、文の途中のコピー(c-07)。copyRow は箱(div)なので段落に入らない。
6697
+ * **文面にその文字列が含まれているとき(showText=false)は[コピー]だけ**、含まれていないときは
6698
+ * 文字列(code)と[コピー]。ボタンは role="button" のリンク(行のボタンの並びを変えない)。
6699
+ */
6700
+ function copyInline(text, showText) {
6701
+ var wrap = el("span", { class: "ds-act-inline" });
6702
+ var field = el("code", { class: "rt-copy-text", text: text });
6703
+ if (showText) wrap.appendChild(field);
6704
+ var btn = el("a", { class: "ds-toggle-btn", role: "button", tabindex: "0", text: "コピー" });
6705
+ var msg = el("span", { class: "ds-toggle-msg" });
6706
+ bindCopy(btn, showText ? field : el("code", { text: text }), msg, text);
6707
+ wrap.appendChild(btn);
6708
+ wrap.appendChild(msg);
6709
+ return wrap;
6710
+ }
6711
+
6677
6712
  // 要素を組み立てるヘルパ。
6678
6713
  // ⚠️ **el() 自体は無害化しない。** class/text 以外のキーはそのまま setAttribute へ渡すので
6679
6714
  // href に javascript: を置けるし、html: は innerHTML への抜け道(実使用は
@@ -7223,6 +7258,27 @@ ${calendarAddUrl.toString()}
7223
7258
  function registrationUnreadable() {
7224
7259
  return liveAnswered && LIVE_REGISTERED === null && LIVE_CAPS === null;
7225
7260
  }
7261
+ /**
7262
+ * 登録の一覧の**本物(registeredNow)がまだ手元に無い**。届く前(確認中)も、届かなかった後も含む。
7263
+ *
7264
+ * この間に出る行は、ビルドに焼いた前の晩のまとめ表の控え——**消したはずのリポジトリが
7265
+ * 本物のように並ぶ**(cortex-engine #1328 c-10)。行は消さず(読めない回に空になるのも嘘)、
7266
+ * **薄く出して「確かめています」と言う**(dimUnconfirmedRows)。仮置き(この画面で押した操作)の
7267
+ * 行は、押した本人が知っている事実なので薄くしない。
7268
+ */
7269
+ function registryUnconfirmed() {
7270
+ return liveConfirmPossible() && LIVE_REGISTERED === null && (!liveAnswered || registrationUnreadable());
7271
+ }
7272
+ /** 本物が来るまでの行を薄くする。仮置きの印(反映待ち)を持つ行はそのまま */
7273
+ function dimUnconfirmedRows(nodes) {
7274
+ if (!registryUnconfirmed()) return nodes;
7275
+ nodes.forEach(function (n) {
7276
+ if (!n || !n.classList || n.classList.contains("ds-note") || n.classList.contains("ds-orgnote")) return;
7277
+ if (n.querySelector && n.querySelector(".ds-pending-badge")) return;
7278
+ n.classList.add("ds-row-unconfirmed");
7279
+ });
7280
+ return nodes;
7281
+ }
7226
7282
  /**
7227
7283
  * 一覧に添える1行。**帯は症状・行は一手・繰り返さない。**
7228
7284
  * 応答がまだ届いていない間は出さない(確かめる前に「確かめられなかった」と言わない)。
@@ -7240,6 +7296,11 @@ ${calendarAddUrl.toString()}
7240
7296
  * 利用者が知りたいのは日付ではなく「自分が足したものが抜けているかどうか」。
7241
7297
  */
7242
7298
  function unverifiedListNote(noun, hasRows) {
7299
+ if (registryUnconfirmed() && !liveAnswered) {
7300
+ // 本物(op=state)がまだ届いていない間。行はビルドに焼いた前の晩の控えなので、
7301
+ // 「今の登録」として読ませない(行は薄く出す・dimUnconfirmedRows)
7302
+ return el("div", { class: "ds-note ds-note-checking", text: "登録されている" + noun + "を確かめています…" });
7303
+ }
7243
7304
  if (!registrationUnreadable()) return null;
7244
7305
  var head = "登録されている" + noun + "を確認できませんでした。";
7245
7306
  return el("div", { class: "ds-note", text: hasRows
@@ -9757,7 +9818,16 @@ ${calendarAddUrl.toString()}
9757
9818
  "ok": { text: "接続OK", cls: "ds-gate-ok" },
9758
9819
  // **状態だけでなく、次にすることを書く。** 「bot未招待」では何をすればよいか分からない
9759
9820
  // (公開チャンネルでも招待が要ることは Slack の仕様で、知らない人のほうが多い)
9760
- "not_in_channel": { text: "Slackで招待してください", cls: "ds-gate-warn" },
9821
+ //
9822
+ // 🔴 **一手は行の中に出す(c-07)。** 札は症状(未招待)、一手は「Cortex が招待されていません:
9823
+ // /invite @Cortex[コピー]」の 1 行。追加の欄の手順 1 にあったコピー行はここへ移した。
9824
+ // help を持たせたので、showsRestingReason は夜間の理由文(機械の語)を重ねない
9825
+ "not_in_channel": {
9826
+ text: "Slackで招待してください",
9827
+ cls: "ds-gate-warn",
9828
+ help: "Cortex が招待されていません:",
9829
+ copy: "/invite @Cortex",
9830
+ },
9761
9831
  // **招待では直らない(2026-08-19 の不具合報告)。** 非公開チャンネルで groups:read が
9762
9832
  // 足りていたのに、実測がこれを「不明」に潰していたので画面には「接続OK」としか出ず、
9763
9833
  // 名前が引けない理由が一切分からなかった(原因の特定に半日かかった)。
@@ -9774,10 +9844,29 @@ ${calendarAddUrl.toString()}
9774
9844
  // (DS_GATE_NO_TOKEN と同じ作り。バッジに長文を入れると行が折り返す)。
9775
9845
  // 判定を返すのは engine の夜間実測(Phase 2)——画面は語彙と文面だけを先に持つ
9776
9846
  // not_in_channel の「招待」とは直す操作が別物なので寄せない
9847
+ //
9848
+ // 🔴 **アプリの追加手順(画面操作・画像 2 枚・管理者の承認)は追加の欄から移した(c-07)。**
9849
+ // help が summary の 1 文・本文は details に畳む(admin_approval_pending と同じ型)。
9850
+ // 画像(shots)は**行の一手(ds-gate-help)にだけ**出し、押した結果・能力タイルには出さない
9851
+ // (gateHelpNodes)
9777
9852
  "not_in_space": {
9778
9853
  text: "スペースにアプリ未追加",
9779
9854
  cls: "ds-gate-warn",
9780
- help: "スペースの「アプリと統合」からアプリを追加してください。",
9855
+ help: "スペースの「アプリと統合」から「" + GCHAT_APP_NAME + "」を追加してください。",
9856
+ request: {
9857
+ lines: [
9858
+ {
9859
+ text: "スペース名 →「アプリと統合」→「アプリを追加」で、「" + GCHAT_APP_NAME + "」を検索して追加します。",
9860
+ shots: [
9861
+ { src: GCHAT_SHOT_STEP1, width: "900", height: "566",
9862
+ alt: "Google Chat のスペースの画面。スペース名の横のメニューが開いていて、その中の「アプリと統合」が赤い枠で示されている。" },
9863
+ { src: GCHAT_SHOT_STEP2, width: "900", height: "491",
9864
+ alt: "「アプリと統合」の画面。右上の「+ アプリを追加」が赤い枠で示され、その下に、このスペースに追加されているアプリの一覧が並んでいる。" },
9865
+ ],
9866
+ },
9867
+ "検索に出ないときは、貴社の Google Workspace 管理者の承認がまだです(承認できるのは管理者だけ・反映まで最大24時間)。",
9868
+ ],
9869
+ },
9781
9870
  },
9782
9871
  // no_token の「トークン未設定」は接続ボタンの無い Google Chat では打つ手を示せないので寄せない。
9783
9872
  // 内部語(サービスアカウント・鍵)は出さない——顧客側に打つ手が無い状態のため
@@ -9802,10 +9891,10 @@ ${calendarAddUrl.toString()}
9802
9891
  help: "貴社の Google Workspace 管理者がこのアプリを承認すると、読み取りが始まります。",
9803
9892
  request: {
9804
9893
  lines: [
9805
- "貴社の Google Workspace 管理者に、次の手順で「Cortex by Classmethod」の利用承認をご依頼ください。",
9894
+ "貴社の Google Workspace 管理者に、次の手順で「" + GCHAT_APP_NAME + "」の利用承認をご依頼ください。",
9806
9895
  "1. Google 管理コンソール(admin.google.com)を開く",
9807
9896
  "2. 「アプリ」→「Google Workspace Marketplace アプリ」を開く",
9808
- "3. 「アプリを追加」から「Cortex by Classmethod」を検索する",
9897
+ "3. 「アプリを追加」から「" + GCHAT_APP_NAME + "」を検索する",
9809
9898
  "4. 組織全体(または該当の組織部門)へのインストールを選び、データへのアクセスを許可する",
9810
9899
  ],
9811
9900
  },
@@ -9905,7 +9994,11 @@ ${calendarAddUrl.toString()}
9905
9994
  customer: {
9906
9995
  text: "トークン未設定",
9907
9996
  cls: "ds-gate-muted",
9908
- help: "このチャンネルがあるお客様の Slack ワークスペースが、まだ接続されていない(または接続が切れている)状態です。「Slack ワークスペースに接続する」から接続してください。",
9997
+ // 🔴 **一手は「この Slack を Cortex に接続してください。[接続する]」の 1 行(c-07)。**
9998
+ // 常設の[Slack ワークスペースに接続する]は撤去し、接続はこの行の一手が持つ。
9999
+ // 主語は「この Slack」(承認画面はワークスペースを指定できない——slackConnectInline の項)
10000
+ help: "この Slack を Cortex に接続してください。",
10001
+ act: "connect",
9909
10002
  },
9910
10003
  /* -------------------------------------------------------------------------
9911
10004
  * GitHub。**「トークン未設定」と言ってはいけない**(2026-08-21)
@@ -9987,6 +10080,14 @@ ${calendarAddUrl.toString()}
9987
10080
  * 「登録から削除」(#354)が担う。区別する信号ができたら probed に足す
9988
10081
  */
9989
10082
  var DS_GATE_RESTING_HELP = {
10083
+ // 🔴 **Slack の「理由不明」の一手(c-07)。** 行の中身(社内か・組織不明か・台帳の接続状態)で
10084
+ // 変わるので関数で持つ(DS_GATE.unreachable は help を持たない)。部品の型は
10085
+ // 文字列 / { link, kind } / { copy } / { act: "connect" }(restingHelpNode)
10086
+ slack: {
10087
+ unreachable: {
10088
+ resting: function (row) { return slackRowHelpParts(row); },
10089
+ },
10090
+ },
9990
10091
  github: {
9991
10092
  // 🔴 **読めない行の中に、理由に合った一手だけを出す(#1233)。** 全体の帯は撤去した。
9992
10093
  // 一手は組織の App の導入状況(githubRowHelpParts)で決まるので、関数で持つ
@@ -10044,20 +10145,73 @@ ${calendarAddUrl.toString()}
10044
10145
  }
10045
10146
  return ["この組織に Cortex の GitHub App を入れてください。", { link: "GitHub App を入れる →", kind: "install" }];
10046
10147
  }
10148
+ /**
10149
+ * Slack の「理由不明」の行(unreachable・組織不明の仮置き)に出す一手(c-07)。
10150
+ * **広い方の 1 行**にする——原因は行の分類では絞れない(0.12.2 の実測)ので、片方に賭けない。
10151
+ *
10152
+ * - ワークスペースが分かっていて、台帳の接続が切れている/未接続 → 「この Slack を Cortex に接続してください。[接続する]」
10153
+ * - ワークスペースが分かっていて、接続が生きている → 招待だけ
10154
+ * - 社内・組織不明(分類が当てにならない)→ 招待 + 「お客様の Slack なら、ワークスペースを接続してください。[接続する]」
10155
+ *
10156
+ * 🔴 **投入の口(SITE.intake)が無い案件では接続を出さない**(押せない案内を置かない)。
10157
+ */
10158
+ function slackRowHelpParts(row) {
10159
+ if (!row) return null;
10160
+ var invite = ["Cortex からこのチャンネルが見えません。Cortex を招待してください:", { copy: "/invite @Cortex" }];
10161
+ var canConnect = !!SITE.intake;
10162
+ if (row.workspace) {
10163
+ var st = tokenStateOf(slackWorkspaces(), row.workspace);
10164
+ if (st === "revoked" || st === "missing") {
10165
+ return canConnect ? ["この Slack を Cortex に接続してください。", { act: "connect" }] : invite;
10166
+ }
10167
+ return invite;
10168
+ }
10169
+ return canConnect
10170
+ ? invite.concat([" お客様の Slack なら、ワークスペースを接続してください。", { act: "connect" }])
10171
+ : invite;
10172
+ }
10047
10173
  /** 押した結果に添えるパーツ(無ければ null)。行と同じ表から引く(写経しない) */
10048
10174
  function probedHelpPartsOf(type, gate) {
10049
10175
  var e = restingHelpEntryOf(type, gate);
10050
10176
  return (e && e.probed) || null;
10051
10177
  }
10052
- /** パーツを1段落にする。包む要素の class は置き場所で違う(行は ds-gate-help・押した結果は ds-probe-help) */
10178
+ /**
10179
+ * パーツを1段落にする。包む要素の class は置き場所で違う(行は ds-gate-help・押した結果は ds-probe-help)。
10180
+ * 部品: 文字列=テキスト / { link, kind } = GitHub App のリンク /
10181
+ * { copy } =コピー付きの 1 語(文字列と[コピー]) / { act } =ボタン(actNode)。
10182
+ * **部品は同じ段落の中に置く**(段を増やさない=行の補足は最大 1 段)。
10183
+ */
10053
10184
  function restingHelpNode(parts, cls) {
10054
10185
  var p = el("p", { class: cls });
10055
10186
  parts.forEach(function (part) {
10056
10187
  if (typeof part === "string") p.appendChild(document.createTextNode(part));
10057
10188
  else if (part && part.link) p.appendChild(el("a", { href: githubAppLinkUrl(part.kind), target: "_blank", rel: "noopener", text: part.link }));
10189
+ else if (part && part.copy) p.appendChild(copyInline(part.copy, true));
10190
+ else if (part && part.act) { var an = actNode(part.act); if (an) p.appendChild(an); }
10058
10191
  });
10059
10192
  return p;
10060
10193
  }
10194
+ /**
10195
+ * 行の一手に置くボタン(c-07)。**押せないボタンは作らない**(null)。
10196
+ * - "connect" … [接続する]。Slack の承認画面へ送る(slackConnectInline)。投入の口が無ければ null
10197
+ * - "figma-key" … [トークンを入れる]/[トークンを入れ直す]。下の「Figma のトークン」の欄の入力へ移る
10198
+ * @param label 省略時は既定の語
10199
+ */
10200
+ function actNode(act, label) {
10201
+ if (!SITE.intake) return null;
10202
+ if (act === "connect") return slackConnectInline(label || "接続する");
10203
+ // 鍵の欄(KEY_SLOTS.figma)が無い構成では、移る先が無いので出さない
10204
+ if (act === "figma-key") return secretKindsShown().indexOf("figma") >= 0 ? figmaKeyJump(label || "トークンを入れる") : null;
10205
+ return null;
10206
+ }
10207
+ /**
10208
+ * 一手の文に添える部品(gate.copy / gate.act)を、**同じ段落の中**へ足す。
10209
+ * copy が文面にそのまま含まれているとき(Drive のアドレス)は[コピー]だけ、含まれていないときは文字列も出す
10210
+ */
10211
+ function appendGateActs(p, gate) {
10212
+ if (gate.copy) p.appendChild(copyInline(gate.copy, String(gate.help || "").indexOf(gate.copy) < 0));
10213
+ if (gate.act) { var an = actNode(gate.act, gate.actLabel); if (an) p.appendChild(an); }
10214
+ }
10061
10215
  /**
10062
10216
  * ボタンの detail が「機械語の痕跡」でしかない能力(#548・設計v2 🔴2)。
10063
10217
  *
@@ -10129,7 +10283,8 @@ ${calendarAddUrl.toString()}
10129
10283
  * (省略=社内。#104 でこの値が画面まで届いていることは slack-workspace.test.mjs が見張る)。
10130
10284
  */
10131
10285
  function isInternalSlack(s) {
10132
- return s.type === "slack" && !s.workspace;
10286
+ // 組織不明(orgUnknown)の行は分類が当てにならない=社内と断定しない(顧客側の一手=接続を出す)
10287
+ return s.type === "slack" && !s.workspace && s.orgUnknown !== true;
10133
10288
  }
10134
10289
  /**
10135
10290
  * Slack チャンネルの表示名。**# は表示のときだけ付ける**(保存値は素・2026-08-19)。
@@ -10204,7 +10359,10 @@ ${calendarAddUrl.toString()}
10204
10359
  unreachable: {
10205
10360
  text: "フォルダを開けません",
10206
10361
  cls: "ds-gate-warn",
10207
- help: DRIVE_INGEST_ACCOUNT + " を閲覧者として招待してください(招待が外れると読めません)。",
10362
+ // 🔴 **一手は「このフォルダを cortex-notetaker@… に閲覧者として共有してください。[コピー]」の 1 行(c-07)。**
10363
+ // コピーは追加の欄の手順 1 にだけあったので、ここへ移した(help の文面にアドレスが含まれるので[コピー]だけ)
10364
+ help: "このフォルダを " + DRIVE_INGEST_ACCOUNT + " に閲覧者として共有してください。",
10365
+ copy: DRIVE_INGEST_ACCOUNT,
10208
10366
  },
10209
10367
  no_token: { text: "管理者の設定が必要です", cls: "ds-gate-muted" },
10210
10368
  // SharePoint の判定表は管理者の同意を前提のまま(チームの RSC にファイルの権限が無い・PoC C で決める)。
@@ -10217,12 +10375,26 @@ ${calendarAddUrl.toString()}
10217
10375
  },
10218
10376
  issues: { no_token: { text: "APIキーが未登録です", cls: "ds-gate-muted" } },
10219
10377
  design: {
10220
- no_token: { text: "トークンが未登録です", cls: "ds-gate-muted" },
10221
- // 「アクセス不可」では、トークンの失効なのかキーの誤りなのかが行から辿れない(#523 S4)。
10222
- // 直し方は書き手(engine の figma-gate の写し)が理由(「トークンが無効か…(HTTP 403)」/
10223
- // 「ファイルが見つかりません(HTTP 404…)」)として運ぶので、**help は持たせない**
10224
- // (持たせると理由と 2 段になり「繰り返さない」に反する。理由は appendReachableReason が出す)
10225
- unreachable: { text: "ファイルを読めません", cls: "ds-gate-warn" },
10378
+ // 🔴 **鍵が無い行の一手は、行の中の 1 行(c-07)。** [トークンを入れる]が下の「Figma のトークン」の欄の入力へ移る
10379
+ no_token: {
10380
+ text: "トークンが未登録です",
10381
+ cls: "ds-gate-muted",
10382
+ help: "Figma のトークンを入れてください。",
10383
+ act: "figma-key",
10384
+ },
10385
+ // 「アクセス不可」では、トークンの失効なのか(403)ファイルを共有していないのか(404)が
10386
+ // 画面から見分けられない(理由が文字の中だけ=機械の語に依存した分岐は書かない・c-07)。
10387
+ // **広い方の 1 行に両方を入れる。** engine の理由文(「HTTP 403」「HTTP 404」が混ざる)は
10388
+ // 出さない——help を持たせると appendReachableReason が先にこちらを返す。
10389
+ // Figma は「Cortex の共有アカウント」ではなく**トークンを発行した人のアカウント**で読むので、
10390
+ // 招待先のアドレスは画面が知らない(コピーは付けない。決定メモの分類 B)
10391
+ unreachable: {
10392
+ text: "ファイルを読めません",
10393
+ cls: "ds-gate-warn",
10394
+ help: "このファイルに、トークンを発行したアカウントを閲覧者として招待してください(トークンが切れているときは入れ直してください)。",
10395
+ act: "figma-key",
10396
+ actLabel: "トークンを入れ直す",
10397
+ },
10226
10398
  },
10227
10399
  };
10228
10400
  /**
@@ -10253,16 +10425,32 @@ ${calendarAddUrl.toString()}
10253
10425
  * (下の probeSection の stale 処理)ので、混同すると二重表示か消え忘れが起きる
10254
10426
  */
10255
10427
  function gateHelpNodes(gate, cls) {
10428
+ // 🔴 **押せない一手は出さない(c-07)。** 接続・トークンの口(SITE.intake)が無い案件に
10429
+ // 「接続してください」だけを置かない(押せない案内)
10430
+ if (gate.act && !SITE.intake) return [];
10256
10431
  if (gate.request) {
10257
10432
  var det = el("details", { class: cls + " ds-gate-request" }, [
10258
10433
  el("summary", { text: gate.help || "詳しい手順" }),
10259
10434
  ]);
10260
10435
  gate.request.lines.forEach(function (line) {
10261
- det.appendChild(el("p", { class: "ds-gate-request-line", text: line }));
10436
+ // 行は文字列か { text, shots }。**画像は行の一手(ds-gate-help)にだけ**出す——押した結果
10437
+ // (ds-probe-help)にも能力タイルにも出さない(同じ画像が 2 か所に並ぶのを避ける)
10438
+ var lineText = typeof line === "string" ? line : (line && line.text) || "";
10439
+ var lp = el("p", { class: "ds-gate-request-line", text: lineText });
10440
+ det.appendChild(lp);
10441
+ if (line && typeof line === "object" && line.shots && cls === "ds-gate-help") {
10442
+ var shotBox = el("div", { class: "ds-gate-request-shots" });
10443
+ line.shots.forEach(function (sh) { shotBox.appendChild(guideShot(sh)); });
10444
+ det.appendChild(shotBox);
10445
+ }
10262
10446
  });
10263
10447
  return [det];
10264
10448
  }
10265
- if (gate.help) return [el("p", { class: cls, text: gate.help })];
10449
+ if (gate.help) {
10450
+ var p = el("p", { class: cls, text: gate.help });
10451
+ appendGateActs(p, gate);
10452
+ return [p];
10453
+ }
10266
10454
  return [];
10267
10455
  }
10268
10456
  /**
@@ -10732,24 +10920,21 @@ ${calendarAddUrl.toString()}
10732
10920
  }
10733
10921
 
10734
10922
  /**
10735
- * Slack の承認画面へ進むボタン(2026-08-20 に接続カードから切り出した)。
10923
+ * Slack の承認画面へ進むボタン(2026-08-20 に接続カードから切り出し・c-07 で行の一手の中へ移した)。
10736
10924
  *
10737
- * ## 押す人についての注意を必ず添える
10925
+ * ## 置き場所
10926
+ *
10927
+ * **常設のボタンと、組織不明・接続切れの枠は撤去した(c-07)。** 接続は、必要になった行の一手
10928
+ * 「この Slack を Cortex に接続してください。[接続する]」の中にだけ置く(押す理由はその一文が言う)。
10929
+ * 一手の段落(ds-gate-help・押した結果の ds-probe-help)の**中**に置くので、段は増えない。
10930
+ *
10931
+ * ## 押す人についての注意(説明は上部の説明文に 1 回だけ)
10738
10932
  *
10739
10933
  * **承認画面はワークスペースを指定できない**(開始の口に team を渡す口が無い)。
10740
10934
  * **承認する人がログインしているワークスペース**にボットが入る。だから
10741
10935
  * 「〈お客様の名前〉と接続する」とは書けない——保証できないことを書かない。
10742
- *
10743
- * お客様の Slack なら、クラスメソッドの人間は押せないことが多い
10744
- * (その Slack の管理者にお願いすることになる)。それを書かないと、
10745
- * 押しても進まない人が「壊れている」と読む。
10746
- *
10747
- * ## 常設の入口として置くときは lead を渡す(0.12.2)
10748
- *
10749
- * 「組織不明」の案内の中では、直前の箇条書きが**なぜ接続するのか**を言っている。
10750
- * 追加フォームの後に単独で置くときは**その文脈が無い**ので、押す理由が画面から
10751
- * 読み取れない——下の説明文は「誰が承認できるか」の話しかしていない。
10752
- * 1行だけ添える口を開けておく。**見出しは付けない**(節を増やすと一覧が遠くなる)。
10936
+ * お客様の Slack なら、クラスメソッドの人間は押せないことが多い(その Slack の管理者に
10937
+ * お願いすることになる)。それは、チャットのモーダルの説明文が 1 回だけ言う。
10753
10938
  *
10754
10939
  * ## 承認画面から戻ってきたとき(#343)
10755
10940
  *
@@ -10761,18 +10946,15 @@ ${calendarAddUrl.toString()}
10761
10946
  * 戻す口は**起動時に1本だけ張った listener が data-leaving を DOM から探す**形にした
10762
10947
  * ——モーダルを開くたびにこのボタンは作り直されるので、登録簿に貯めると
10763
10948
  * 閉じたモーダルの DOM を掴み続ける。印を付けるのは遷移が確定した瞬間だけで、
10764
- * 失敗経路(その場でボタンを戻す)には付けない。
10949
+ * 失敗経路(その場でボタンを戻す)には付けない。戻すとき結果の文は同じ .ds-toggle の
10950
+ * 中の .ds-toggle-msg を空にする(restoreLeavingButtons)ので、ここも同じ箱の形にしてある。
10765
10951
  *
10766
- * @param lead 省略可。押す理由の1行(案内の中から呼ぶときは渡さない)
10952
+ * @param label ボタンの語(既定は「接続する」)
10767
10953
  */
10768
- function slackConnectButton(lead) {
10769
- var wrap = el("div", { class: "ds-toggle" });
10770
- // **「この」を付けない(2026-08-20・オーナー指摘)。**
10771
- // どのワークスペースを指すのか読み取れない——そもそも**指定できない**
10772
- // (承認する人がログインしているワークスペースに入る)ので、
10773
- // 特定のものを指すような書き方は嘘にもなる
10774
- var btn = el("button", { class: "ds-toggle-btn", type: "button", text: "Slack ワークスペースに接続する" });
10775
- var msg = el("div", { class: "ds-toggle-msg" });
10954
+ function slackConnectInline(label) {
10955
+ var wrap = el("span", { class: "ds-toggle ds-act-inline" });
10956
+ var btn = el("button", { class: "ds-toggle-btn", type: "button", text: label || "接続する" });
10957
+ var msg = el("span", { class: "ds-toggle-msg" });
10776
10958
  btn.addEventListener("click", async function () {
10777
10959
  var intake = SITE.intake;
10778
10960
  if (!intake) return;
@@ -10791,10 +10973,7 @@ ${calendarAddUrl.toString()}
10791
10973
  }
10792
10974
  // **同じタブで遷移する。** 別タブだと、承認後の「接続しました」を見た人が
10793
10975
  // 元のタブに戻る動線を自分で見つけることになる
10794
- //
10795
- // **遷移が確定したこの瞬間にだけ印を付ける(#343)。** ブラウザバックで
10796
- // bfcache から復元されたとき、起動時の pageshow の listener がこの印を頼りに
10797
- // ボタンと「準備しています…」を素の状態へ戻す(restoreLeavingButtons)
10976
+ // **遷移が確定したこの瞬間にだけ印を付ける(#343)**
10798
10977
  btn.setAttribute("data-leaving", "1");
10799
10978
  location.href = j.url;
10800
10979
  } catch (e) {
@@ -10803,20 +10982,29 @@ ${calendarAddUrl.toString()}
10803
10982
  btn.disabled = false;
10804
10983
  }
10805
10984
  });
10806
- // **lead は「なぜ押すのか」・下の説明は「誰が押せるのか」。** 別のことを言っているので
10807
- // 1文にまとめない(まとめると、どちらの話なのか読み取れない長文になる)
10808
- if (lead) wrap.appendChild(el("p", { class: "ds-group-note ds-connect-lead", text: lead }));
10809
- wrap.appendChild(el("p", {
10810
- class: "ds-group-note",
10811
- text: "ボタンを押すと Slack の承認画面が開きます。承認できるのは、その Slack の管理者だけです"
10812
- + "(お客様の Slack なら、お客様の管理者にお願いしてください)。"
10813
- + "お願いするのは、登録したチャンネルを読む権限と、通知を投稿する権限です。",
10814
- }));
10815
10985
  wrap.appendChild(btn);
10816
10986
  wrap.appendChild(msg);
10817
10987
  return wrap;
10818
10988
  }
10819
10989
 
10990
+ /**
10991
+ * [トークンを入れる]/[トークンを入れ直す](Figma の行の一手・c-07)。
10992
+ * 一覧の直後にある「Figma のトークン」の欄(KEY_SLOTS.figma)の入力へ移って focus する。
10993
+ * **欄は 0 件のあいだ畳まれている**が、行がある=ファイルが 1 件以上あるので畳まれていない。
10994
+ * 入力が見つからなければ何もしない(鍵の欄を作る口が無い構成)。
10995
+ */
10996
+ function figmaKeyJump(label) {
10997
+ var btn = el("button", { class: "ds-toggle-btn ds-act-inline", type: "button", text: label || "トークンを入れる" });
10998
+ btn.addEventListener("click", function () {
10999
+ var slot = KEY_SLOTS.figma;
11000
+ var input = slot && slot.querySelector("input, textarea");
11001
+ if (!input) return;
11002
+ try { if (input.scrollIntoView) input.scrollIntoView({ block: "center" }); } catch (e) { /* 動かせなくても focus は試す */ }
11003
+ input.focus();
11004
+ });
11005
+ return btn;
11006
+ }
11007
+
10820
11008
  /** 台帳から、そのワークスペースの接続状態を引く(無ければ null=断定しない) */
10821
11009
  function tokenStateOf(workspaces, teamId) {
10822
11010
  var list = workspaces || [];
@@ -10863,6 +11051,7 @@ ${calendarAddUrl.toString()}
10863
11051
  // 「反映したのに何も変わらない」と読まれてもう一度押すことになる
10864
11052
  var note = document.querySelector(".ds-org-resolved");
10865
11053
  if (!note) return;
11054
+ note.hidden = false;
10866
11055
  note.textContent = "✓ " + j.resolved + "件の所属を反映しました(この一覧の表示は次回の更新・毎晩22時ごろから正しくなります)。";
10867
11056
  })
10868
11057
  .catch(function () {
@@ -11080,10 +11269,10 @@ ${calendarAddUrl.toString()}
11080
11269
  * どの組織かは**行のラベル**が示す(同じチャンネル名が2つの組織に並びうるので、
11081
11270
  * ラベル自体は必要)。
11082
11271
  *
11083
- * ## 「組織が分かりません」の案内だけは別
11272
+ * ## 囲む枠は出さない(c-07)
11084
11273
  *
11085
- * 3つの原因と、招待コマンド・接続ボタン・登録の解除が要る。**行ごとに繰り返すと
11086
- * 一覧が案内で埋まる**ので、その並びの手前に1つだけ置く。
11274
+ * 「組織が分かりません」「接続が切れた」の枠は撤去した。困っているのはその行だけなので、
11275
+ * 一手は行の中(slackRowHelpParts・DS_GATE)が持つ。
11087
11276
  */
11088
11277
  function slackRows(items, meta) {
11089
11278
  var ws = slackWorkspaces();
@@ -11096,22 +11285,15 @@ ${calendarAddUrl.toString()}
11096
11285
  // OAuth の戻り先は固定ページ(この画面には戻らない)ので、
11097
11286
  // **利用者が操作タブを開き直したこの瞬間**が唯一の合図になる
11098
11287
  requestOrgResolve();
11099
- // **案内が行を抱える(2026-08-20・オーナー指摘)。**
11100
- // 「次のチャンネル」と書いても、囲われていないと**どれを指すのか分からない**。
11101
- // 折りたたみは付けない——開閉させたいのではなく、**範囲を示したい**
11102
- out.push(orgNoteBox(unknownOrgNodes(), cards));
11103
- return;
11104
- }
11105
- if (g.key !== "__internal__") {
11106
- var st = tokenStateOf(ws, g.key);
11107
- // **接続が切れたときの導線を必ず残す。** それまでは接続カードの中の一覧に
11108
- // 出していた。カードごと消したので、ここへ移す——移さないと
11109
- // **接続が切れたことに気づけず、直す手段も画面から消える**
11110
- if (st === "revoked" || st === "missing") {
11111
- out.push(orgNoteBox(brokenOrgNodes(g.title, st), cards));
11112
- return;
11113
- }
11288
+ // 🔴 **囲む枠は出さない(c-07)。** 判定し直しの結果は、行の前に 1 段(結果が書かれるまで hidden)。
11289
+ // 一手は読めない行の中(slackRowHelpParts)が持つ
11290
+ var resolved = el("p", { class: "ds-group-note ds-org-resolved" });
11291
+ resolved.hidden = true;
11292
+ out.push(resolved);
11114
11293
  }
11294
+ // 🔴 **接続が切れた/未接続のワークスペースの枠(brokenOrgNodes)は撤去した(c-07)。**
11295
+ // そのワークスペースの読めていない行が、一手「この Slack を Cortex に接続してください。[接続する]」を
11296
+ // 持つ(slackRowHelpParts と DS_GATE_NO_TOKEN.customer)。入口を失わないための約束は決定メモに書いた
11115
11297
  cards.forEach(function (n) { out.push(n); });
11116
11298
  });
11117
11299
  return out;
@@ -11132,57 +11314,6 @@ ${calendarAddUrl.toString()}
11132
11314
  return box;
11133
11315
  }
11134
11316
 
11135
- /** 「組織不明」の案内。**原因を1つに絞らない** */
11136
- function unknownOrgNodes() {
11137
- // **接続だけを勧めると、招待すれば済む人に「直らない処方」を出す。**
11138
- // 非公開チャンネルは、同じ Slack でも招待前は「存在しない」と返る
11139
- var out = [];
11140
- // **「読めませんでした」では、行の「Slackで招待してください」と区別が付かない**
11141
- //(オーナー指摘・2026-08-20)。どちらも読めていないが、**分かっていることが違う**:
11142
- // 行に「招待してください」… クラスメソッドのチャンネルだと**分かっている**。招待だけが足りない
11143
- // この案内 … **どのSlackのものかすら分からない**(どの鍵でも届かなかった)
11144
- // 状態の名前を「分かりません」に揃える
11145
- out.push(el("div", { class: "ds-orgnote-title", text: "⚠️ 組織不明のチャンネル" }));
11146
- out.push(el("p", {
11147
- class: "ds-group-note",
11148
- text: "Cortex が持っているどの鍵でも、このチャンネルに届きませんでした。次のどれかです。",
11149
- }));
11150
- var ul = el("ul", { class: "ds-group-note" });
11151
- // **公開との違いをここで言い切る。** 「同じ未招待なのに、なぜ上の行は
11152
- // 組織が分かっていて、こちらは分からないのか」が読み取れないと、
11153
- // 利用者は同じ状態が2通りに見えることに戸惑う
11154
- ul.appendChild(el("li", { text: "非公開チャンネルで、まだ Cortex を招待していない → 下のコマンドを Slack のそのチャンネルに貼ってください" }));
11155
- ul.appendChild(el("li", { text: "お客様の Slack にあり、まだつながっていない → 下のボタンから接続します" }));
11156
- ul.appendChild(el("li", { text: "チャンネルがアーカイブ/削除された、URLが違う → 「登録を解除」を押してください" }));
11157
- out.push(ul);
11158
- out.push(copyRow("/invite @Cortex"));
11159
- out.push(slackConnectButton());
11160
- // 判定し直しの結果を出す場所(何も起きなければ空のまま)
11161
- out.push(el("p", { class: "ds-group-note ds-org-resolved" }));
11162
- return out;
11163
- }
11164
-
11165
- /** 接続が切れた/未接続のワークスペースの案内 */
11166
- function brokenOrgNodes(title, state) {
11167
- var out = [];
11168
- out.push(el("div", {
11169
- class: "ds-orgnote-title",
11170
- text: state === "revoked" ? "⚠️ " + title + " との接続が切れています" : "⚠️ " + title + " はまだ接続されていません",
11171
- }));
11172
- out.push(el("p", {
11173
- class: "ds-group-note",
11174
- // **revoked と missing を分ける。** 前者は「接続し直して」、後者は「まだ完了していない」で
11175
- // 次の一手が違う。unknown には何も出さない——繋がっているのに
11176
- // 「接続し直して」と言う方が、黙っているより悪い
11177
- text: state === "revoked"
11178
- ? "Slack 側で Cortex のアプリが削除されたか、接続が無効になっています。もう一度接続してください。"
11179
- : "このワークスペースのチャンネルが登録されていますが、接続が見つかりません。もう一度接続してください。",
11180
- }));
11181
- out.push(slackConnectButton());
11182
- return out;
11183
- }
11184
-
11185
-
11186
11317
  function dsFmtTime(iso) {
11187
11318
  var d = new Date(iso);
11188
11319
  if (isNaN(d.getTime())) return "";
@@ -12398,7 +12529,25 @@ ${calendarAddUrl.toString()}
12398
12529
  var chatScope = adapter === "slack"
12399
12530
  ? ""
12400
12531
  : " Cortex のアプリは、会話の読み取りと、通知先に設定したスペースへの確認・通知の投稿だけに使われます。読み取った会話は、本文そのものを保存するのではなく、決定事項・用語などの抽出結果としてプロジェクトの記録に反映されます。";
12401
- m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいる" + chatNoun + "は、例外なく全て毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。読み取りを止めるには「登録を解除」してください。" + chatScope }));
12532
+ // 🔴 **追加の欄から外した前提の説明は、ここに 1 回だけ足す(c-07)。** 説明文は 1 段のまま、文だけ足す:
12533
+ // - Slack … お客様の Slack は、その Slack の管理者がワークスペースの接続を承認すると読める
12534
+ // (承認できる人と渡す権限。以前は常設の接続ボタンの下の説明段落が持っていた)
12535
+ // - Google Chat … DM(1 対 1)は対象外(以前は追加の欄の手順 3 が持っていた)
12536
+ // - 混載の 1 枚タイル(アダプタ不明・どちらのフォームも並ぶ)は両方の文を持つ
12537
+ var chatExtra = (adapter !== "google-chat"
12538
+ ? " お客様の Slack のチャンネルは、その Slack の管理者がワークスペースの接続を承認すると読めるようになります(チャンネルを読む権限と、通知を投稿する権限を渡します)。"
12539
+ : "")
12540
+ + (adapter !== "slack" ? " DM(1対1のチャット)は対象外です。" : "");
12541
+ m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいる" + chatNoun + "は、例外なく全て毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。読み取りを止めるには「登録を解除」してください。" + chatScope + chatExtra }));
12542
+ } else if (cap === "デザイン" && (!adapter || adapter === "figma")) {
12543
+ // 🔴 **Figma の説明文(c-07・新設)。** 追加の欄の手順 4(何が同期されるか)と手順 1 の前提
12544
+ // (トークンの登録)を、ここに 1 回だけ置く。鍵の欄は 0 件のあいだ畳まれているので、入れる欄が
12545
+ // 出ること(ファイルを足すと出る)も 1 句で言う。発行の手順そのものは鍵の欄(正本)が持つ
12546
+ m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいる Figma ファイルの画面名とコメントを、Figma へのリンクつきで毎晩同期します(画像は取り込まれません)。あらかじめ Figma のトークンの登録が要ります(ファイルを足すと入れる欄が出ます)。やめるには「このファイルを外す」を押してください。" }));
12547
+ } else if (cap === "共有資料" && (!adapter || adapter === "google-drive")) {
12548
+ // 🔴 **Google Drive 専用の説明文(c-07・新設)。** SharePoint 等の別のアダプタの窓には出さない
12549
+ // (Markdown 化の約束は Drive の取り込みの話)。追加の欄の手順 3 が持っていた文
12550
+ m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいるフォルダの中身を、毎晩 共有資料 にコピーして Markdown 化します。やめるには「このフォルダを外す」を押してください。" }));
12402
12551
  } else if (cap === "開発") {
12403
12552
  // 🔴 **開発の開示文は「記録に使う・決定の抽出には使わない」(#660)。** 2026-09-18 の
12404
12553
  // オーナー決定でソースの性質は種類で固定になり、GitHub は「作業の記録」に固定された。
@@ -12438,11 +12587,23 @@ ${calendarAddUrl.toString()}
12438
12587
  ? ""
12439
12588
  : (CAP_STATE_TEXT[dNow] || "状態が判定できません") + "(" + detailNow + ")";
12440
12589
  }
12441
- var noteEl = el("p", { class: "modal-desc", text: noteText });
12442
- // **消さずに hidden で伏せる。** 節点を残しておかないと、この一文を探している
12443
- // 検査が「文言が変わった」と「箱ごと無くなった」を見分けられない
12444
- noteEl.hidden = noteText === "";
12445
- m.modal.appendChild(noteEl);
12590
+ // 🔴 **Figma・Drive の「未接続(…が登録されていません)」は作らない(c-07)。** 0 件のとき、
12591
+ // 同じ窓に一覧の ds-empty「…はまだ登録されていません。」が出ていて二重だった。しかも一覧は
12592
+ // liveList で追加に追随するが、この一文は開いた時点の値なので、1 件足すと「未接続」が足した行の
12593
+ // 上に残る。**一覧の 1 行だけ**を残す。off・broken の一文は残す(Backlog・Slack・GitHub も触らない)。
12594
+ // 🔴 **「登録されていません」と言うだけの一文だけを落とす。** 「雛形のプレースホルダです」のように、
12595
+ // 一覧の 1 行が言わない手がかりを運ぶ一文は残す(なぜ未接続扱いなのかが読めなくなる)
12596
+ var unsetDup = noteIsUnset
12597
+ && /登録されていません/.test(noteText)
12598
+ && ((cap === "デザイン" && (!adapter || adapter === "figma"))
12599
+ || (cap === "共有資料" && (!adapter || adapter === "google-drive")));
12600
+ if (!unsetDup) {
12601
+ var noteEl = el("p", { class: "modal-desc", text: noteText });
12602
+ // **消さずに hidden で伏せる。** 節点を残しておかないと、この一文を探している
12603
+ // 検査が「文言が変わった」と「箱ごと無くなった」を見分けられない
12604
+ noteEl.hidden = noteText === "";
12605
+ m.modal.appendChild(noteEl);
12606
+ }
12446
12607
  }
12447
12608
  if (!items.length) {
12448
12609
  m.modal.appendChild(el("p", { class: "ds-empty", text: "この能力にはまだ設定できるものがありません。" }));
@@ -12836,7 +12997,7 @@ ${calendarAddUrl.toString()}
12836
12997
  *(0件の一覧は行が増えないので、行だけを見ていると変化が現れない)
12837
12998
  */
12838
12999
  function externalsSig(kind, extra) {
12839
- return JSON.stringify([liveExternals(kind), extra, registrationUnreadable()]);
13000
+ return JSON.stringify([liveExternals(kind), extra, registrationUnreadable(), registryUnconfirmed()]);
12840
13001
  }
12841
13002
  /**
12842
13003
  * **空の箱を置いてよいのは、そこへ足せる案件だけ。** 投入の口を持たない構成
@@ -12855,7 +13016,7 @@ ${calendarAddUrl.toString()}
12855
13016
  if (noteSlack) box.appendChild(noteSlack);
12856
13017
  if (!items.length) return;
12857
13018
  var meta = DS_TYPE["slack"] || { icon: "🔗", label: "Slack" };
12858
- slackRows(items, meta).forEach(function (n) { box.appendChild(n); });
13019
+ dimUnconfirmedRows(slackRows(items, meta)).forEach(function (n) { box.appendChild(n); });
12859
13020
  },
12860
13021
  ), "slack");
12861
13022
  // Google Chat の箱(#153)。行は平置き(組織情報が無いので組ごとの並べ替え・囲いは
@@ -12870,7 +13031,8 @@ ${calendarAddUrl.toString()}
12870
13031
  if (noteGchat) box.appendChild(noteGchat);
12871
13032
  if (!items.length) return;
12872
13033
  var meta = DS_TYPE["google-chat"] || { icon: "💬", label: "Google Chat" };
12873
- items.forEach(function (s) { box.appendChild(extSourceCard(s, meta)); });
13034
+ dimUnconfirmedRows(items.map(function (s) { return extSourceCard(s, meta); }))
13035
+ .forEach(function (n) { box.appendChild(n); });
12874
13036
  },
12875
13037
  ), "google-chat");
12876
13038
  if (liveListWanted("github")) pushCap("開発", liveList(
@@ -12883,7 +13045,7 @@ ${calendarAddUrl.toString()}
12883
13045
  if (noteDev) box.appendChild(noteDev);
12884
13046
  if (!items.length) return;
12885
13047
  // **meta は行ごとに引く**(issues と discussions でアイコンとラベルが違う)
12886
- githubRows(items).forEach(function (n) { box.appendChild(n); });
13048
+ dimUnconfirmedRows(githubRows(items)).forEach(function (n) { box.appendChild(n); });
12887
13049
  },
12888
13050
  ));
12889
13051
 
@@ -12904,45 +13066,25 @@ ${calendarAddUrl.toString()}
12904
13066
  // 0件なら繋ぐ相手もまだ無いので、入口が要る場面にならない。
12905
13067
  pushCap("チャット", adders.querySelector('[data-adder="slack"]'), "slack");
12906
13068
  /* -------------------------------------------------------------------------
12907
- * 🔴 接続の入口を常設する(0.12.2・実オンボーディングで踏んだ)
13069
+ * 🔴 接続の入口は、必要になった行の一手の中にだけある(c-07)
12908
13070
  * -------------------------------------------------------------------------
12909
- * 直前の版まで、このボタンは**「組織不明」の案内の中にしか無かった**。
12910
- * 2026-08-20 に独立した節をやめたときの理屈は「チャンネルを追加すれば
12911
- * 読み取れていない組として現れ、そこに接続ボタンが出る」だったが、
12912
- * **印が付かない経路で登録された行には、そこへ行く道が1本も無い**:
12913
- *
12914
- * - channels.json に手で書き足した行は orgUnknown を持たない
12915
- * (印を載せるのはサーバの登録経路だけ)ので、お客様の Slack の
12916
- * チャンネルなのに「クラスメソッド」の並びに落ちる
12917
- * - Slack コネクトの共有チャンネルは **URL がどちらの組織のドメインにもなりうる**
12918
- * ので、そもそも分類が当てにならない。「クラスメソッド」に分類された行の
12919
- * 正しい対処が接続(OAuth)のこともある(実測: 招待しようとしたら
12920
- * 「組織外のユーザー」で断られた)
12921
- *
12922
- * **お客様の Slack かどうかは、行の状態から決まらない。** だから入口は
12923
- * 行の状態と無関係に置く——**組織不明の組が1つも無くても出る**。
12924
- *
12925
- * ⚠ **「組織不明」の案内の中のボタンは残す。** あの文脈では、直前の箇条書きが
12926
- * 「なぜ接続するのか」を言っており、次の一手として正しい。同じ関数を2箇所から
12927
- * 呼ぶだけで、押したときの動きは1つ(承認の口も1つ)。
12928
- *
12929
- * **置き場所は追加フォームの後。** フォームの手順3が「組織不明と出たら…接続する」と
12930
- * 言っているので、フォームより前に出すと読む順と操作の順が食い違う。
12931
- *
12932
- * **投入の口が無い構成では出さない。** 承認の開始は intake Lambda を叩くので、
12933
- * 無い案件では押しても始まらない(renderExternalAdders も同じ条件で空を返す)。
13071
+ * 以前は[Slack ワークスペースに接続する]を追加の欄の後ろに常設していた(0.12.2・
13072
+ * 印が付かない経路で登録された行に道が無かったため)。**常設は撤去した**——どの行の話かが読めず、
13073
+ * 「先に読ませる画面」になっていた(オーナーの違和感・#1233 と同じ)。
13074
+ * 入口を失わないための約束(決定メモ 20261010-1328):
13075
+ * - 顧客の Slack を足した直後、サーバが鍵で実測して組織を決める(応答の pendingOrg)。
13076
+ * どの鍵でも届かなければ orgUnknown が付き、**その行が[接続する]を持つ**(slackRowHelpParts)
13077
+ * - 台帳の接続が切れている/未接続のワークスペースの読めていない行は、全部その一手を持つ
13078
+ * - 社内の読めていない行も持つ(Slack コネクトの共有チャンネルが社内に分類された実例)。
13079
+ * 社内の no_token だけは持たない(2026-08-18 の事故)
13080
+ * - 投入の口(SITE.intake)が無い案件は、押せないので一手ごと出さない
13081
+ * - 割り切り: 足した行が**まだ測られていない間**は、どの窓にも一手が出ない
13082
+ * ([接続を確認]の結果に一手が付く)
12934
13083
  * ----------------------------------------------------------------------- */
12935
- if (SITE.intake) {
12936
- // "slack" 札は #153 の合流で付けた——無いと Google Chat の箱にも OAuth ボタンが出る
12937
- //(Google Chat の承認は Admin install で、押す先の画面が無い)。常設の意図は Slack の箱の中で保つ
12938
- pushCap("チャット", slackConnectButton(
12939
- "お客様の Slack や、Slack コネクトで共有されたチャンネルを読むには、先にワークスペースの接続が要ります。",
12940
- ), "slack");
12941
- }
12942
13084
  // Google Chat のフォーム(#153)。接続(OAuth)のボタンは存在しない——顧客の
12943
13085
  // Workspace 管理者による Admin install が承認そのもので、画面に押す先が無い。
12944
- // 案内(アプリ追加の操作列)は独立の箱ではなくフォームの手順リストが持つ(#170/#174 の
12945
- // 圧縮方針。「貼る前に読ませる」は手順が入力欄より前にあることで担保し、テストで固定)
13086
+ // アプリ追加の操作列(画像つき)と管理者の承認は、追加の欄の手順ではなく、**そのスペースの
13087
+ // 行の一手**(DS_GATE.not_in_space の details)が持つ(c-07)
12946
13088
  pushCap("チャット", adders.querySelector('[data-adder="google-chat"]'), "google-chat");
12947
13089
  // **組織ごとの接続状況は、行と一体にした(2026-08-20・Slack からの横展開)。**
12948
13090
  //
@@ -13930,11 +14072,15 @@ ${calendarAddUrl.toString()}
13930
14072
  ul.appendChild(el("li", {
13931
14073
  text: "非公開チャンネル … 下のコマンドをそのチャンネルに貼って Cortex を招待してから、もう一度「接続を確認」を押してください",
13932
14074
  }));
13933
- ul.appendChild(el("li", {
14075
+ // 🔴 **指す先の常設ボタンは撤去した(c-07)ので、同じ段にインラインの[接続する]を置く**
14076
+ var connectLi = el("li", {
13934
14077
  text: "お客様の Slack、または Slack コネクトで共有されたチャンネル … "
13935
- + "下の「Slack ワークスペースに接続する」から、そのワークスペースに Cortex を追加するリクエストを送ってください。"
14078
+ + "[接続する]から、そのワークスペースに Cortex を追加するリクエストを送ってください。"
13936
14079
  + "接続できたら、もう一度「接続を確認」を押してください",
13937
- }));
14080
+ });
14081
+ var connectBtn = actNode("connect");
14082
+ if (connectBtn) connectLi.appendChild(connectBtn);
14083
+ ul.appendChild(connectLi);
13938
14084
  ul.appendChild(el("li", {
13939
14085
  text: "アーカイブ・削除された、URL が違う … 「登録を解除」を押してください",
13940
14086
  }));
@@ -13984,9 +14130,12 @@ ${calendarAddUrl.toString()}
13984
14130
  // **社内/顧客の出し分けは変えない。** 社内は画面から設定できるものが無いので
13985
14131
  // 連絡先、顧客は接続へ誘導する(2026-08-18 の不具合の再発防止)。
13986
14132
  // 長い説明は落とし、次の行動の1行だけ残す
13987
- return [p(isInternalSlack(s)
13988
- ? "cortex の担当者にご連絡ください。"
13989
- : "「Slack ワークスペースに接続する」から接続してください。")];
14133
+ if (isInternalSlack(s)) return [p("cortex の担当者にご連絡ください。")];
14134
+ // 常設のボタンは撤去した(c-07)。同じ段にインラインの[接続する]を置く
14135
+ var connectP = p("この Slack を Cortex に接続してください。");
14136
+ var connectNow = actNode("connect");
14137
+ if (connectNow) connectP.appendChild(connectNow);
14138
+ return [connectP];
13990
14139
  }
13991
14140
  if (r.gate === "unreachable") {
13992
14141
  // **ここだけ本文が2つ以上になる(0.12.2)。** 原因が3つあり、しかも
@@ -15631,7 +15780,7 @@ ${calendarAddUrl.toString()}
15631
15780
  // 仮置きの印(drivePending)も署名に入れる——外れたときに印だけ残さない(#388)
15632
15781
  // 「確かめられたか」も混ぜる(#409。混ぜないと注記の要否が変わっても作り直されない)
15633
15782
  // 「届くか」も混ぜる(#523 S1。混ぜないと、ストアの reachable が届いても札が付かない)
15634
- return JSON.stringify([d.driveFolderIds || [], d.driveFolderNames || {}, d.driveState || "", d.drivePending || {}, registrationUnreadable(),
15783
+ return JSON.stringify([d.driveFolderIds || [], d.driveFolderNames || {}, d.driveState || "", d.drivePending || {}, registrationUnreadable(), registryUnconfirmed(),
15635
15784
  reachSigOf("materials", d.driveFolderIds || [])]);
15636
15785
  }, function (listBox) {
15637
15786
  var src = driveNow();
@@ -15712,18 +15861,10 @@ ${calendarAddUrl.toString()}
15712
15861
  placeholder: "https://drive.google.com/drive/folders/…",
15713
15862
  namePlaceholder: "メモ(任意)",
15714
15863
  requiredNote: "フォルダのURLを入力してください。",
15715
- // **手順のリストにする(2026-08-20・Slack からの横展開)。**
15716
- //
15717
- // **取り込み用アカウントのアドレスを出す(オーナー判断・分類A)。**
15718
- // それまでは「取り込み用アカウントを招待してください」としか書いておらず、
15719
- // **誰を招待すればいいのかが画面から分からなかった**——担当者に聞かないと
15720
- // 完了できない手順になっていた。
15721
- // 招待されない限り何も読めないアカウントなので、出す判断をした。
15722
- hint: [
15723
- { text: "読み取りたい Google Drive のフォルダを開き、下のアカウントを閲覧者として招待します(招待が無いと読めません)。", node: copyRow(DRIVE_INGEST_ACCOUNT) },
15724
- "そのフォルダのURLを下の欄に貼って「追加」を押します。",
15725
- "追加したフォルダの中身が 共有資料 にコピーされ、Markdown 化されます。止めるときは、その行の「このフォルダを外す」を押します。",
15726
- ],
15864
+ // 🔴 **追加の欄は入力と[追加]だけ(c-07)。** 以前は「アカウントを招待 → URL を貼る → 何が起きるか」の
15865
+ // 手順(コピー行つき)を置いていたが、足してから読めなければ、その行の中が一手を出す
15866
+ // (DS_GATE_CAP.materials.unreachable: 「このフォルダを cortex-notetaker@… に閲覧者として共有してください。[コピー]」)。
15867
+ // 「中身が 共有資料 にコピーされ Markdown 化される・止め方」はモーダル上部の説明文が 1 回だけ持つ。
15727
15868
  build: function (url, name) { return { op: "add-drive-folder", url: url, name: name }; },
15728
15869
  }));
15729
15870
  used = true;
@@ -15786,7 +15927,7 @@ ${calendarAddUrl.toString()}
15786
15927
  // モーダルを開いた人には取り込みが1件も出ない(designIngestSigOf の項に経緯)
15787
15928
  figGroup.appendChild(liveList("デザイン", function () {
15788
15929
  // 「確かめられたか」も混ぜる(#409。混ぜないと注記の要否が変わっても作り直されない)
15789
- return JSON.stringify(figsOf()) + "|" + designIngestSigOf(figsOf()) + "|" + registrationUnreadable();
15930
+ return JSON.stringify(figsOf()) + "|" + designIngestSigOf(figsOf()) + "|" + registrationUnreadable() + "|" + registryUnconfirmed();
15790
15931
  }, function (listBox) {
15791
15932
  var figs = figsOf();
15792
15933
  // 兄弟の穴を一緒に塞ぐ(#409)。ここも同じ registeredNow から行を足している
@@ -15896,18 +16037,9 @@ ${calendarAddUrl.toString()}
15896
16037
  placeholder: "https://www.figma.com/design/…",
15897
16038
  namePlaceholder: "メモ(任意)",
15898
16039
  requiredNote: "FigmaのURLを入力してください。",
15899
- // **手順のリストにする(2026-08-20・Slack からの横展開)。**
15900
- // 一続きの文章だと、読む人が「自分がいま何をすべきか」を自分で切り出すことになる。
15901
- //
15902
- // **手順1・2は要約にとどめ、正本は上の鍵の欄に置く**(SECRET_FORMS.figma の
15903
- // how / note)。全文をここに移すと二重になり、片方だけ直る形になる
15904
- // ——カードは接続マップとタイルの注記と条件を揃えているので、動かすと壊れやすい。
15905
- hint: [
15906
- "Figma でトークンを発行します(読み取りだけ。スコープ2つ。Dev または Full 席の方が発行)。手順は「Figma のトークン」の欄にあります(ファイルを追加すると上に出ます)。",
15907
- "発行したアカウントを、読み取りたい Figma ファイルに閲覧者として招待します(招待が無いと、トークンが正しくても読めません)。",
15908
- "そのファイルのURLを下の欄に貼って「追加」を押します。",
15909
- "追加したファイルは、画面名と Figma へのリンクが毎晩同期されます(画像は取り込まれません)。",
15910
- ],
16040
+ // 🔴 **追加の欄は入力と[追加]だけ(c-07)。** 以前は「トークンを発行 → 招待 → URL → 何が同期されるか」の
16041
+ // 手順を置いていたが、トークンの発行手順の正本は鍵の欄(SECRET_FORMS.figma の how・note・画像)、
16042
+ // 読めないファイルの一手は行の中(DS_GATE_CAP.design)、「何が同期されるか」は上部の説明文が持つ。
15911
16043
  build: function (url, name) { return { op: "add-figma-file", url: url, name: name }; },
15912
16044
  }));
15913
16045
  used = true;
@@ -15948,24 +16080,10 @@ ${calendarAddUrl.toString()}
15948
16080
  // 別名を付けたいときだけ入れてもらう。
15949
16081
  namePlaceholder: "表示名(省略するとSlackから取得します)",
15950
16082
  requiredNote: "チャンネルのURLを入力してください。",
15951
- // **招待の話を先頭に置く。** Slack の仕様で、ボットは**公開チャンネルでも**招待されるまで
15952
- // 中身を1件も読めない(conversations.history が not_in_channel を返す・2026-08-16 実測+公式ドキュメント)。
15953
- // そのあとに「追加=Gold 昇格の対象」を置く(手順と、その手順の重さ)。
15954
- // **手順のリストにする(2026-08-20・オーナー指摘)。**
15955
- // 一続きの文章だと、読む人が「自分がいま何をすべきか」を自分で切り出すことになる。
15956
- //
15957
- // **接続(お客様の Slack への導入)の説明もここに置く。** 独立した節を
15958
- // 廃止したので、**繋ぎ方を知る場所はここしか無い**——
15959
- // 「0件の案件で入口が消える」という過去の事故(renderExternalAdders の
15960
- // 直上コメント)を、この手順が受け止めている。
15961
- hint: [
15962
- // **コマンドは手順1の中に置く。** フォームの下に離すと、どの手順のものか
15963
- // 対応が読めない(手で打たせない理由は copyRow のコメント)
15964
- { text: "読み取りたいチャンネルで、下のコマンドを貼って Cortex を招待します。公開チャンネルでも招待が必要です。", node: copyRow("/invite @Cortex") },
15965
- "下の欄に、そのチャンネルのリンクを貼って「追加」を押します(チャンネル名を右クリック →「リンクをコピー」)。",
15966
- "「組織不明」と出たら、そこにある「Slack ワークスペースに接続する」から承認をもらいます。お客様の Slack のチャンネルは、この承認が済むまで読めません(承認できるのはその Slack の管理者です)。",
15967
- "追加したチャンネルは毎晩の Gold 昇格の対象になり、Decision・用語集の材料になります。止めるときは、その行の「登録を解除」を押します。",
15968
- ],
16083
+ // 🔴 **追加の欄は入力と[追加]だけ(c-07)。** 以前は「/invite を貼る → リンクを貼る → 組織不明なら接続 →
16084
+ // Gold 昇格の対象」の手順 4 行を置いていたが、足してから読めなければ、そのチャンネルの行の中が
16085
+ // 理由に合った一手(「Cortex が招待されていません:/invite @Cortex[コピー]」・「この Slack を Cortex に接続してください。[接続する]」)を出す。
16086
+ // 「毎晩の Gold 昇格の対象・止め方」「お客様の Slack は管理者の承認が要る」は、モーダル上部の説明文が 1 回だけ持つ。
15969
16087
  // **選んだワークスペースを送る(#104)。** 空文字=社内ワークスペースなのでキーごと落とす
15970
16088
  // (サーバ側は「未宣言・null・空」を区別しない約束で、無いことで既定を表す)
15971
16089
  build: function (url, name, workspace) {
@@ -16008,14 +16126,8 @@ ${calendarAddUrl.toString()}
16008
16126
  // 承認そのもので、画面に押す先が無い(Slack の接続ボタンを写経しないこと——
16009
16127
  // 押しても何も起きないボタンが顧客の画面に出る)。
16010
16128
  // **追加時の確認モーダルも出さない**(#167 で「追加のたびの確認」は廃止済み。
16011
- // 「追加=Gold 昇格の対象」という重さは、Slack と同じく手順の最終行が伝える)。
16012
- // 案内(アプリ追加の操作列)は独立の箱にせず、この手順リストが持つ(#170/#174 の圧縮方針。
16013
- // fleet-status にデータが無い案件でもフォームは常設なので、案内も常に出る)
16014
- //
16015
- // Google Chat アプリの表示名。**Marketplace 公開名(2026-09-03 オーナー決定で
16016
- // 「Cortex Bot」から改称・#387。このモーダル分は #389 で済ませた)。**
16017
- // 検索ワードとして本文に組み込む(コピー行は置かない——コマンドではなく検索語のため)
16018
- var GCHAT_APP_NAME = "Cortex by Classmethod";
16129
+ // 「追加=Gold 昇格の対象」という重さは、モーダル上部の説明文が 1 回だけ言う)。
16130
+ // Google Chat アプリの表示名は GCHAT_APP_NAME(Marketplace 公開名・#387)。
16019
16131
  box.appendChild(addForm({
16020
16132
  title: "+ Google Chat スペースを追加",
16021
16133
  // **いま画面に出ている形を例示する(#389)。** 旧UIの /room/ を例にしていたので、
@@ -16023,35 +16135,9 @@ ${calendarAddUrl.toString()}
16023
16135
  placeholder: "https://chat.google.com/app/chat/XXXXXXXXXXX",
16024
16136
  // 素の spaces/<ID> も受ける(#389)ので、URL しか受けないとは書かない
16025
16137
  requiredNote: "スペースの URL か spaces/… を入力してください。",
16026
- // 手順リスト(#170 の型)。アプリ追加の話が先——Slack の招待と同じで、入っていないと
16027
- // 1件も読めない。操作列は Google 公式ヘルプの現行手順に合わせる
16028
- // (support.google.com/chat/answer/7655820)。DM が対象外であることもここで言う——
16029
- // 貼って 400 になってから知るより先に
16030
- //
16031
- // **1行1手に詰める(2026-09-03 オーナー指示・#389)。** 手順の中に事情の説明が
16032
- // 混ざると、読む人が「自分がいま何をすべきか」を自分で切り出すことになる。
16033
- // 🔴 落とさない語(既存テストが固定している): 「アプリと統合」「Gold 昇格の対象になり」
16034
- // 「登録を解除」「22時」——重さと止め方を伝える場所がこの手順しか無いため
16035
- hint: [
16036
- {
16037
- text: "読み取りたいスペースに Cortex のアプリを追加します。スペース名 → 「アプリと統合」 → 「アプリを追加」 で、「" + GCHAT_APP_NAME + "」を検索して追加します。",
16038
- // **画像は補助で、上の1文だけで手順が完結する**(読み上げ・画像が出ない環境)。
16039
- // 寸法は属性で明示——書かないと、モーダルを開いた瞬間に画像の高さのぶん下の手順がずれる。
16040
- // loading=lazy は付けない(data: URI には取得が無いので何もしない・Meet と同じ)。
16041
- // 2枚を div でくるむのは、hintNode の口を広げないため(1手順=1ノードの約束を保つ)。
16042
- // 🔴 **会議カードと同じ guideShot を通す(#455)。** 3 枚が同じ上限・同じ
16043
- // クリックの振る舞いになる。ここで幅を書き足すと、その 1 枚だけ揃わなくなる
16044
- node: el("div", {}, [
16045
- guideShot({ src: GCHAT_SHOT_STEP1, width: "900", height: "566",
16046
- alt: "Google Chat のスペースの画面。スペース名の横のメニューが開いていて、その中の「アプリと統合」が赤い枠で示されている。" }),
16047
- guideShot({ src: GCHAT_SHOT_STEP2, width: "900", height: "491",
16048
- alt: "「アプリと統合」の画面。右上の「+ アプリを追加」が赤い枠で示され、その下に、このスペースに追加されているアプリの一覧が並んでいる。" }),
16049
- ]),
16050
- },
16051
- "検索に出ないときは、貴社の Google Workspace 管理者の承認がまだです(承認できるのは管理者だけ・反映まで最大24時間)。",
16052
- "下の欄に、ブラウザでスペースを開いたときのURL(chat.google.com/app/chat/… または /room/…)を貼って「追加」を押します。DM(1対1のチャット)は対象外です。",
16053
- "追加したスペースは毎晩の Gold 昇格の対象になり、Decision・用語集の材料になります。止めるときは、その行の「登録を解除」を押します(今夜のぶんは22時ごろまでに解除すれば取り込まれません)。",
16054
- ],
16138
+ // 🔴 **追加の欄は入力と[追加]だけ(c-07)。** アプリの追加手順(画像つき)と管理者の承認は、
16139
+ // 足したスペースの行の一手(DS_GATE.not_in_space)が持つ。「DM は対象外」「Gold 昇格の対象・止め方」は
16140
+ // モーダル上部の説明文が持つ。URL の形は placeholder と requiredNote。
16055
16141
  build: function (url) {
16056
16142
  return { op: "add-google-chat-space", url: url };
16057
16143
  },
@@ -16812,7 +16898,7 @@ ${calendarAddUrl.toString()}
16812
16898
  badges.appendChild(el("span", {
16813
16899
  class: "badge ds-workspace ds-workspace-unknown",
16814
16900
  text: "組織不明",
16815
- title: "どの Slack のチャンネルか、まだ分かっていません。上の案内をご覧ください。",
16901
+ title: "どの Slack のチャンネルか、まだ分かっていません。この行の案内をご覧ください。",
16816
16902
  }));
16817
16903
  } else {
16818
16904
  badges.appendChild(el("span", {
@@ -16986,7 +17072,14 @@ ${calendarAddUrl.toString()}
16986
17072
  // を付ける——押した結果で置き換えるとき probeSection がまとめて隠す。
16987
17073
  // **同じリポジトリの2本目の行(Discussions)には出さない**(opts.quietHelp)。
16988
17074
  // 同じ長文が2行に並ぶと一覧が案内で埋まる。URL のリンク化は neededNode(innerHTML は使わない)
16989
- if (!(opts && opts.quietHelp) && showsRestingReason(s)) {
17075
+ // 🔴 **足した直後の行でも、サーバが「組織不明」と実測して返したときだけは一手を出す(c-07)。**
17076
+ // 常設の接続ボタンを撤去したので、0 件の案件に顧客の Slack を足した人が接続へ進む入口はこの行だけ
17077
+ // (#138 型の穴を塞ぐ)。まだ gate が付いていない行(仮置き・登録の反映だけが先に届いた行)でも、
17078
+ // 印(orgUnknown)があれば出す。**印が無い行(測っていない)には何も出さない**
17079
+ if (s.type === "slack" && s.orgUnknown === true && !s.gate && SITE.intake) {
17080
+ var pendingParts = slackRowHelpParts(s);
17081
+ if (pendingParts) card.appendChild(restingHelpNode(pendingParts, "ds-gate-help ds-gate-resting"));
17082
+ } else if (!(opts && opts.quietHelp) && showsRestingReason(s)) {
16990
17083
  var restingParts = restingHelpPartsOf(s.type, s.gate, s);
16991
17084
  if (restingParts) card.appendChild(restingHelpNode(restingParts, "ds-gate-help ds-gate-resting"));
16992
17085
  else if (!(String(s.type).indexOf("github") === 0 && (s.gate === "unreachable" || s.gate === "no_token") && !githubOrgRefused(s))) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.127",
3
+ "version": "0.12.129",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,