@takagaki/cortex-decisions-viewer 0.12.69 → 0.12.71

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 +298 -120
  2. package/package.json +1 -1
package/dist/render.js CHANGED
@@ -20,6 +20,7 @@ exports.sanitizeStateNow = sanitizeStateNow;
20
20
  exports.devExcludedSkip = devExcludedSkip;
21
21
  exports.devExcludedRefs = devExcludedRefs;
22
22
  exports.devExcludedLabel = devExcludedLabel;
23
+ exports.devExcludedBadgeVisible = devExcludedBadgeVisible;
23
24
  exports.overlayStateOnFleet = overlayStateOnFleet;
24
25
  exports.chatRowsToAdd = chatRowsToAdd;
25
26
  exports.gchatRowsToAdd = gchatRowsToAdd;
@@ -71,6 +72,8 @@ exports.liveRowVerdict = liveRowVerdict;
71
72
  exports.liveRegistryOf = liveRegistryOf;
72
73
  exports.liveToolKey = liveToolKey;
73
74
  exports.liveToolItems = liveToolItems;
75
+ exports.capabilityPipeRe = capabilityPipeRe;
76
+ exports.capabilityWarnSource = capabilityWarnSource;
74
77
  exports.renderConnectionMap = renderConnectionMap;
75
78
  const node_crypto_1 = require("node:crypto");
76
79
  const google_chat_guide_images_1 = require("./google-chat-guide-images");
@@ -252,13 +255,20 @@ const CAP_STATE_TEXT = {
252
255
  };
253
256
  /** 状態の印。CAP_STATE_TEXT の前に付ける(マップは付けない——⚠️バッジと二重になる) */
254
257
  const CAP_STATE_MARK = { ok: "● ", idle: "⦿ ", warn: "⚠️ ", off: "" };
258
+ /**
259
+ * 接続マップに並べる能力と、その絵文字。
260
+ *
261
+ * 🔴 **能力→パイプラインの対応表をここに持たない(#654)。** かつて `pipe:` の欄が
262
+ * あったが誰も読んでおらず(死んだ写し)、正本は `capabilityPipeRe` 1 本。
263
+ * 写しを置くと、次に触る人が「表はここにもある」と読んで 2 枚目を育てる。
264
+ */
255
265
  const CM_CAPS = [
256
- { cap: "課題管理", icon: "📋", pipe: /sync-backlog/ },
257
- { cap: "会議", icon: "🎙", pipe: /ingest-minutes/ },
258
- { cap: "共有資料", icon: "📁", pipe: /sync-materials/ },
259
- { cap: "デザイン", icon: "🎨", pipe: /sync-designs|update-design-notes/ },
260
- { cap: "チャット", icon: "💬", pipe: null },
261
- { cap: "開発", icon: "🐙", pipe: null },
266
+ { cap: "課題管理", icon: "📋" },
267
+ { cap: "会議", icon: "🎙" },
268
+ { cap: "共有資料", icon: "📁" },
269
+ { cap: "デザイン", icon: "🎨" },
270
+ { cap: "チャット", icon: "💬" },
271
+ { cap: "開発", icon: "🐙" },
262
272
  ];
263
273
  /**
264
274
  * ツールid→表示名(接続マップのノードラベル用)。
@@ -558,8 +568,8 @@ const TOOL_KIND_ICONS = {
558
568
  };
559
569
  /**
560
570
  * 状態の表示語と色。**5 語+「登録状況を確認中です」しか出さない**(読み取り中/
561
- * 接続OK(読み取りの対象外)/承認待ち/停止中/未確認、そして登録簿にしか無く
562
- * まだ測っていない行の「確認中」・#581 / #640)。
571
+ * 接続OK(活動を記録中)/承認待ち/停止中/未確認、そして登録簿にしか無く
572
+ * まだ測っていない行の「確認中」・#581 / #640 / #660)。
563
573
  *
564
574
  * 色は既存の `ds-gate-*` を流用する——同じ画面で「緑=読めている・橙=人の操作待ち・
565
575
  * 灰=分からない」の意味を 2 通りに増やさない。
@@ -573,12 +583,17 @@ function toolStateLabel(state) {
573
583
  if (state === "reading")
574
584
  return { text: "読み取り中", cls: "ds-gate-ok" };
575
585
  // 届いてはいるが、Gold の材料にしていない(#640・engine #399)。**「読み取り中」と
576
- // 言わない**——読みに行かないと決めた接続先なので、言えば画面が嘘をつく。
586
+ // 言わない**——決定・用語の材料にしないと決めた接続先なので、言えば画面が嘘をつく。
577
587
  // **警告色にもしない**(`ds-gate-ok` のまま)——直すものが無い中立の事実で、橙にすると
578
588
  // 直しに行かせる。状態名を `"excluded"` にしないのは、`liveToolItems` の中で
579
589
  // `match.excluded`(登録簿の devExcluded =「停止中」)が別の意味で使われているため
590
+ //
591
+ // 🔴 **語は行の札・タイルと同じ「活動を記録中」(#660)。** 2026-09-18 のオーナー決定で
592
+ // ソースの性質は種類で固定になり、GitHub は「作業の記録」に固定された。「読み取りの
593
+ // 対象外」は**何もしていない**ように読めるが、実際は活動を記録している——engine #480 で
594
+ // Issues / Discussions の活動が毎晩の観測に載るので、「対象外」は事実としても古い
580
595
  if (state === "ok_not_ingested")
581
- return { text: "接続OK(読み取りの対象外)", cls: "ds-gate-ok" };
596
+ return { text: "接続OK(活動を記録中)", cls: "ds-gate-ok" };
582
597
  if (state === "waiting")
583
598
  return { text: "承認待ち", cls: "ds-gate-warn" };
584
599
  // 止めているものは警告色にしない(直す必要が無いので、橙にすると直しに行かせてしまう)
@@ -667,7 +682,7 @@ function regRowOfToolItem(item) {
667
682
  * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
668
683
  */
669
684
  function toolsFoldSummary(states) {
670
- // 表示の順は状態の語と同じ(読み取り中→接続OK(読み取りの対象外)→承認待ち→停止中→
685
+ // 表示の順は状態の語と同じ(読み取り中→接続OK(活動を記録中)→承認待ち→停止中→
671
686
  // 未確認→登録状況を確認中です)。🔴 **新しい語をここへ足し忘れると、下の `order.indexOf`
672
687
  // が `unknown` へ倒すので、畳んだ 1 行だけが「未確認」と言う**(開くと違う語が出る・#640)
673
688
  var order = ["reading", "ok_not_ingested", "waiting", "stopped", "unknown", "pending"];
@@ -1306,23 +1321,55 @@ function devExcludedRefs(rows) {
1306
1321
  return out;
1307
1322
  }
1308
1323
  /**
1309
- * 中立の行の理由文から、画面に出す短語を作る(#640)。
1324
+ * 中立の行に添える札の語(#660。#640 では理由文から性質を切り出していた)。
1310
1325
  *
1311
- * 🔴 **engine が名指しを避けた性質を、画面が断定しない。** engine は性質が語彙
1312
- * (会話/作業物/参照資料)に載っているときだけ名指しし、載っていなければ
1313
- * 「ソースの性質により Gold の材料にしません」と書く(`gatedIngestedRow`)。
1314
- * viewer に性質の語彙の写しは持たない——持つと 4 枚目の写しになり、engine が
1315
- * 門に掛ける性質を増やした日に黙ってずれる(engine 設計レビュー🟡6 と同じ罠)。
1316
- * **理由文が名指ししている語をそのまま括弧に入れる**のが、写しを増やさない唯一の形。
1326
+ * 🔴 **性質を括弧に入れない。** 2026-09-18 のオーナー決定(分類 A)で、ソースの性質は
1327
+ * 登録ごとの選択肢ではなくなり**種類で固定**された。GitHub は「作業の記録」に固定で、
1328
+ * 利用者が画面から変えられるものではない——変えられないものを行に出しても、
1329
+ * 「どうすればよいか」が一つも増えない(#356 の原則「行は一手」)。
1330
+ *
1331
+ * 🔴 **「読み取りの対象外」とも言わない。** engine #480 から Issues / Discussions の活動は
1332
+ * **毎晩の観測として貯まる**ので、「対象外」=何もしていない、は事実として古い。
1333
+ * 出すのは「活動を記録中」の 1 語だけで、「決定の材料にはしない」はモーダルの見出し・
1334
+ * 注記・札の `title` が持つ(帯は症状・行は一手・繰り返さない・2026-09-02)。
1335
+ *
1336
+ * ⚠ **引数 `detail`(行の `ingestExcludedDetail`)は読まない**が、欄は残す——engine の理由文を材料にする実装へ
1337
+ * 戻すときに呼び出し側を触らずに済む。**旧文面(`性質が◯◯のため…`)と新文面
1338
+ * (`活動の記録として観測しています…`)のどちらでも同じ札**になることの番人は
1339
+ * `dev-activity-words.test.mjs`(印の判定から DOM まで通す形で見る)。
1317
1340
  *
1318
1341
  * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
1319
1342
  */
1320
- function devExcludedLabel(detail) {
1321
- var d = typeof detail === "string" ? detail : "";
1322
- var at = d.indexOf("性質が");
1323
- var to = d.indexOf("のため");
1324
- var nature = at === 0 && to > 3 ? d.slice(3, to) : "";
1325
- return nature ? "読み取りの対象外(" + nature + ")" : "読み取りの対象外";
1343
+ function devExcludedLabel(_detail) {
1344
+ return "活動を記録中";
1345
+ }
1346
+ /**
1347
+ * その行に「活動を記録中」の札を出すか(#660)。**判定を 1 本にするための関数。**
1348
+ *
1349
+ * 🔴 **アクセス不可の行には出さない。** 届いていない接続先に「記録中」と出すと、
1350
+ * **アクセス不可を直す一手より先に「問題ない」と読める**。#640 は「片方を隠すと
1351
+ * 『アクセス不可を直せば読まれる』と誤読させる」として常に出していたが、性質が
1352
+ * 種類で固定になった(=直せば読まれるようになる、という誤読の余地が消えた)いま、
1353
+ * 残るのは「届いていないのに記録していると言う嘘」のほうなので、発注書 #660 の
1354
+ * 「届くようにする一手が先」に倒す。タイル(規則2が規則3より先)と Home
1355
+ * (`v.status === "ok"` の中だけ)は元からこの倒し方で、行だけが例外だった(R10)。
1356
+ *
1357
+ * 🔴 **測れていない回(`gate` が空)は出す。** engine は中立の 2 行を同じ晩に書くが、
1358
+ * 片方しか届かない窓がある。そこを「出さない」に倒すと、画面が知っている事実を捨てる
1359
+ * (`overlayStateOnFleet` の項・#640)。隠すのは**測った結果が悪かったとき**だけ。
1360
+ *
1361
+ * 🔴 **「読み取り停止中」(人が `dev-sources.json` の exclude で止めた)にも出さない。**
1362
+ * 止めた行は engine が観測しなくなる=古い中立の行が永久に残るうえ、止めてある以上は
1363
+ * 記録もされない。灰色の札が 2 つ並ぶのも「繰り返さない」に反する(#640 実装レビュー🟡4)。
1364
+ *
1365
+ * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
1366
+ * `probeSection` が押した結果でも同じ 1 本を呼ぶ——判定を 2 枚にしない)。
1367
+ */
1368
+ function devExcludedBadgeVisible(s) {
1369
+ if (!s || s.ingestExcluded !== true || s.goldState === "excluded")
1370
+ return false;
1371
+ var g = String(s.gate == null ? "" : s.gate);
1372
+ return g === "" || g === "ok";
1326
1373
  }
1327
1374
  /**
1328
1375
  * ストアの「届くか」を行に重ねる。**まとめ表の時刻とは比べない。**
@@ -1368,7 +1415,9 @@ function devExcludedLabel(detail) {
1368
1415
  * **開発の行だけ、`ingested` の中立の行も載せる(#640)。** 「接続できる」と「Gold の
1369
1416
  * 材料にしている」は別の状態で、`reachable: ok` だけを載せると、読みに行かないと決めた
1370
1417
  * リポジトリが画面で「読み取り中」になる(engine #399 の申し送り)。載せる欄は
1371
- * `ingestExcluded`(併記の札を出すか)と `ingestExcludedDetail`(語の括弧に入れる性質)。
1418
+ * `ingestExcluded`(併記の札を出しうるか)と `ingestExcludedDetail`(engine が書いた理由文の
1419
+ * 生値)。⚠ **理由文はいま画面に出ない**(#660 で札は engine の文面に依らない 1 語になった)
1420
+ * ——engine の文面を材料にする実装へ戻すときのために、欄だけ運び続ける。
1372
1421
  * 🔴 **`goldState: "excluded"`(人が `dev-sources.json` の exclude で止めた)と別物。**
1373
1422
  */
1374
1423
  function overlayStateOnFleet(view, rows) {
@@ -3190,11 +3239,18 @@ function deriveCapTile(cap, ctx) {
3190
3239
  if (okSeen && sumFailed > 0) {
3191
3240
  return { rule: 3, state: "ok", text: "接続OK(" + sumOk + " 件取り込み・" + sumFailed + " 件失敗)", keyMissing: false };
3192
3241
  }
3193
- // **読みに行かないと決めた接続先は、件数で言う(#640)。** 「接続OK」の一言で丸めると
3194
- // 「決定・用語の材料になっている」と読めるし、⚠️ に落とすと直すものが無いのに直しに
3195
- // 行かせる。**帯は症状だけ**——性質を変える手順は行の補足が 1 行だけ持つ
3242
+ // **決定・用語の材料にしていない接続先があれば、そう言う(#640・#660)。** 「接続OK」の
3243
+ // 一言で丸めると「決定・用語の材料になっている」と読めるし、⚠️ に落とすと直すものが
3244
+ // 無いのに直しに行かせる。**帯は症状だけ**——詳しい説明はタイルを開いた先の見出しが持つ。
3245
+ //
3246
+ // 🔴 **件数は出さない(#660)。** #640 は「読み取りの対象外が N 件」と数えていたが、
3247
+ // 性質が種類で固定になった以上、GitHub の登録は**全件がこれ**で、N を出すと
3248
+ // 「残りは読んでいる」と読める(#640 の申し送りにオーナー判断として上げてあった)。
3249
+ // 語は行の札・Home と同じ「活動を記録中」に揃える(R10:タイルと行が違うことを言わない)。
3250
+ // **`excluded` は分岐の材料としてそのまま使う**——孤児の篩(#523 決定2)と鮮度の門
3251
+ // (`devExcludedRefs`)を通った件数が 0 なら、この枝に来てはいけない
3196
3252
  if (okSeen && excluded > 0) {
3197
- return { rule: 3, state: "ok", text: "接続OK(読み取りの対象外が " + excluded + " 件)", keyMissing: false };
3253
+ return { rule: 3, state: "ok", text: "接続OK(活動を記録中)", keyMissing: false };
3198
3254
  }
3199
3255
  if (okSeen)
3200
3256
  return { rule: 3, state: "ok", text: "接続OK", keyMissing: false };
@@ -3705,17 +3761,78 @@ function liveToolItems(tools, registered, rows) {
3705
3761
  }
3706
3762
  return out;
3707
3763
  }
3708
- function capabilityState(cap, ctx, adapter) {
3709
- // **能力→パイプラインの対応はここに持つ。** 呼び出し側に渡させると、
3710
- // クライアント側(この関数を .toString() で受け取る側)が同じ表を書き写すことになり、
3711
- // 書き忘れると warn 判定が丸ごと死ぬ——実データで、東京ヴェルディの課題管理が
3712
- // マップは⚠️・タイルは「接続OK」になる。**この関数が読むものは、この関数が持つ。**
3764
+ /**
3765
+ * その能力を同期するワークフローの id を当てる正規表現(持たない能力は `null`)。
3766
+ *
3767
+ * 🔴 **能力→パイプラインの対応表はここが唯一の持ち主。** 呼び出し側に渡させると、
3768
+ * クライアント側(この関数を `.toString()` で受け取る側)が同じ表を書き写すことになり、
3769
+ * 書き忘れると warn 判定が丸ごと死ぬ——実データで、東京ヴェルディの課題管理が
3770
+ * マップは⚠️・タイルは「接続OK」になった。**この関数が読むものは、この関数が持つ。**
3771
+ *
3772
+ * 🔴 **この表を読む先は 2 つある(#654 設計レビュー🔴3)。** `capabilityWarnSource`
3773
+ * (直近の run が `failure`)と、`capabilityState` の `pipeSkipped`
3774
+ * (`applicable === false`=空振りした run の時刻を「繋がっている証拠」にしない)。
3775
+ * **warn 側だけ 1 本にして `pipeSkipped` に写しを残さないこと**——残すと、表を直した回に
3776
+ * 「⚠️ は消えたが『接続OK/未接続』だけ別物」という割れ方をする。
3777
+ *
3778
+ * 🔴 **表は関数の中に literal で置く(モジュール直下の `const` にしない)。** export した
3779
+ * 瞬間に tsc が `exports.X` へ書き換え、`.toString()` で客先へ渡った関数が
3780
+ * ブラウザで未定義を参照する——ライブ取得が丸ごと落ちて画面が静的表示に戻る
3781
+ * (`ingestFacts` の項と同じ穴)。
3782
+ *
3783
+ * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
3784
+ */
3785
+ function capabilityPipeRe(cap) {
3713
3786
  var PIPE = {
3714
3787
  "課題管理": /sync-backlog/,
3715
3788
  "会議": /ingest-minutes/,
3716
3789
  "共有資料": /sync-materials/,
3717
3790
  "デザイン": /sync-designs|update-design-notes/,
3718
3791
  };
3792
+ return PIPE[cap] || null;
3793
+ }
3794
+ /**
3795
+ * その能力の⚠️が、**どの材料から立ったか**(#654)。`null` なら warn の材料が無い。
3796
+ *
3797
+ * - `"live"` … ライブ参照の行(`externalSources` の gate)。チャット・開発・その他。
3798
+ * `stateCtx()` が接続状態ストアを**重ねたあとの行**なので、まとめ表由来でも
3799
+ * **その晩の観測**まで追いついている
3800
+ * - `"pipeline"` … まとめ表の `pipelines`(ワークフローの run の結論)。課題管理・会議・
3801
+ * 共有資料・デザイン。**ビルド時に焼かれ、ライブで取り直されない**
3802
+ * (`stateCtx()` が上書きを当てるのは `externalSources` だけ)ので、一度赤くなると
3803
+ * **次のビルドまで赤いまま**になる
3804
+ *
3805
+ * 🔴 **この 2 つを混ぜないために切り出してある(#654)。** タイルには「導出(接続状態
3806
+ * ストア)が warn を降格しない」という門があるが、**焼いた `pipelines` の赤にまで
3807
+ * その門を効かせると、ストアが「たった今 取り込めた」と言っている能力に
3808
+ * 「⚠️ 同期が失敗しています」と出る**——gift-stvv が 2026-09-17 に踏んだ形で、
3809
+ * 同じページのモーダルと接続マップは「接続OK」と言っていた
3810
+ * (`ops-ui-controls-policy.md` R10「状態の出どころが複数あるときは、新しい方に揃える」)。
3811
+ *
3812
+ * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
3813
+ * 引くのは `capabilityPipeRe` / `sourceMatchesAdapter` / `sourceNeedsAttention` / `CAP_OTHER`
3814
+ * の 4 つだけで、どれも関数宣言か `var`——**流し込む順は効かない**)。
3815
+ */
3816
+ function capabilityWarnSource(cap, ctx, adapter) {
3817
+ // **ライブ参照かどうかは能力名で決める。** 行の有無で決めると、行が 0 件の回に
3818
+ // 「パイプライン由来」へ倒れる——チャット・開発には対応するパイプラインが無いので
3819
+ // 答えは同じ null だが、**判定の意味が変わる**(この関数の答えは門の可否に使われる)
3820
+ var isLive = cap === "チャット" || cap === "開発" || cap === CAP_OTHER;
3821
+ if (isLive) {
3822
+ // **アダプタが指定されたら、その行だけで数える(#153)。** チャットが2アダプタに
3823
+ // なったので、Slack のタイルの warn 判定に Google Chat の gate 落ちが混ざると、
3824
+ // 直す場所が違う方を指す。絞り方の正本は sourceMatchesAdapter
3825
+ return (ctx.externals || []).filter(function (s) { return sourceMatchesAdapter(s.type, cap, adapter); })
3826
+ .some(function (s) { return sourceNeedsAttention(s); }) ? "live" : null;
3827
+ }
3828
+ var re = capabilityPipeRe(cap);
3829
+ return (ctx.pipes || []).some(function (p) {
3830
+ return re != null && re.test(p.id) && p.applicable !== false && p.lastConclusion === "failure";
3831
+ }) ? "pipeline" : null;
3832
+ }
3833
+ function capabilityState(cap, ctx, adapter) {
3834
+ // **能力→パイプラインの対応は capabilityPipeRe が持つ(#654)。** ここに写しを
3835
+ // 作らない——写しがずれると「マップは⚠️・タイルは接続OK」が復活する
3719
3836
  // **ライブ参照かどうかは、能力名ではなく渡された行から決める。** 名前で決めていたとき、
3720
3837
  // 未知の種別を寄せた「その他」は行を2件持っているのに「未使用」と言い切っていた
3721
3838
  // (読み手は未使用を信じて押さないので、行が消えるより悪い)
@@ -3725,7 +3842,10 @@ function capabilityState(cap, ctx, adapter) {
3725
3842
  const liveExts = ctx.externals.filter(function (s) { return sourceMatchesAdapter(s.type, cap, adapter); });
3726
3843
  const isLive = cap === "チャット" || cap === "開発" || cap === CAP_OTHER;
3727
3844
  const internal = ctx.internals.filter(function (s) { return s.kind === cap; })[0];
3728
- const pipeRe = PIPE[cap];
3845
+ // 🔴 **空振りした run(applicable: false)を篩うのにも同じ表を引く(#654 設計レビュー🔴3)。**
3846
+ const pipeRe = capabilityPipeRe(cap);
3847
+ // ⚠️ の材料と、その出どころ(#654)。**warn の判定はこの 1 本だけを通す**
3848
+ const warnSrc = capabilityWarnSource(cap, ctx, adapter);
3729
3849
  // ---- #125②: 導出(capabilities)があるならそれが状態の正本 ----
3730
3850
  //
3731
3851
  // **ここが唯一の分岐点。** 導出の有無で描き分けるのはこの1箇所に閉じ、
@@ -3754,13 +3874,7 @@ function capabilityState(cap, ctx, adapter) {
3754
3874
  if (derived === "broken")
3755
3875
  return "warn";
3756
3876
  if (derived === "on") {
3757
- return (isLive
3758
- ? liveExts.some(function (s) { return sourceNeedsAttention(s); })
3759
- : ctx.pipes.some(function (p) {
3760
- return pipeRe != null && pipeRe.test(p.id) && p.applicable !== false && p.lastConclusion === "failure";
3761
- }))
3762
- ? "warn"
3763
- : "ok";
3877
+ return warnSrc ? "warn" : "ok";
3764
3878
  }
3765
3879
  // unset / empty / 未知の値。**未知を ok にも off にも寄せない**——どちらも嘘になる。
3766
3880
  // 「まだ繋がっていない」側に倒すのが安全(繋がっていないのに線を引かない)。
@@ -3774,7 +3888,7 @@ function capabilityState(cap, ctx, adapter) {
3774
3888
  // 状態が unset(engine がこのアダプタをまだ知らない移行期間)だからと黙らせると、
3775
3889
  // 同じ行から描く接続マップだけが⚠️になり、「マップが⚠️・直下は静か」という
3776
3890
  // 既知の最悪事故の変形が移行期間のデフォルトになる
3777
- if (liveExts.some(function (s) { return sourceNeedsAttention(s); }))
3891
+ if (warnSrc === "live")
3778
3892
  return "warn";
3779
3893
  return "idle";
3780
3894
  }
@@ -3803,14 +3917,8 @@ function capabilityState(cap, ctx, adapter) {
3803
3917
  (isLive && liveExts.length > 0);
3804
3918
  const off = (internal != null && internal.enabled === false) || !used;
3805
3919
  // 問題あり: ライブ参照はgate≠okが1件以上、同期ソースは対応パイプラインの直近runがfailure
3806
- const warn = !off &&
3807
- (isLive
3808
- ? liveExts.some(function (s) {
3809
- return sourceNeedsAttention(s);
3810
- })
3811
- : ctx.pipes.some(function (p) {
3812
- return (pipeRe != null && pipeRe.test(p.id) && p.applicable !== false && p.lastConclusion === "failure");
3813
- }));
3920
+ //(判定の実体は capabilityWarnSource 1 本・#654)
3921
+ const warn = !off && !!warnSrc;
3814
3922
  // 「実際に繋がっている」の証拠は共通で次のどれか:
3815
3923
  // - 一度でも同期が走った(lastSync)
3816
3924
  // - 正本のURLが導出できる(url。設定ファイルが実在して初めて出る)
@@ -3970,7 +4078,11 @@ function renderConnectionMap(site) {
3970
4078
  const sub = `<text class="cm-sync" x="${cx}" y="67" text-anchor="middle"` +
3971
4079
  (statusTs ? ` data-ts="${escapeHtml(statusTs)}"` : "") +
3972
4080
  `>${escapeHtml(statusText)}</text>`;
3973
- const badge = warn ? `<text class="cm-badge" x="${cx + 62}" y="24" text-anchor="middle">⚠️</text>` : "";
4081
+ // 🔴 **6ノードに常時焼いて、表示だけを切り替える(線と同じ形・#654)。** warn
4082
+ // ノードにだけ焼いていたときは、`applyStateToConnMap` が当て直せず
4083
+ // **「緑のノードに⚠️が付いたまま」**が残った——ライブで接続OKに直っても、
4084
+ // ビルド時に赤かった能力は次のビルドまで⚠️を出し続ける(#654 の症状の片割れ)
4085
+ const badge = `<text class="cm-badge${warn ? "" : " is-hidden"}" x="${cx + 62}" y="24" text-anchor="middle">⚠️</text>`;
3974
4086
  // クリックでページ内の該当詳細カード(アンカー)へスクロールする
3975
4087
  const anchor = `ds-${c.cap}`;
3976
4088
  nodes.push(`<g class="cm-node ${state}" data-target="${anchor}" tabindex="0" role="link" aria-label="${escapeHtml(c.cap)}の詳細へ">` +
@@ -4969,6 +5081,7 @@ a.ds-name:hover { text-decoration: underline; }
4969
5081
  .cm-sync { font-size: 10.5px; fill: var(--muted); font-variant-numeric: tabular-nums; }
4970
5082
  .cm-node.cm-off .cm-sync { fill: #9ca3af; }
4971
5083
  .cm-badge { font-size: 13px; }
5084
+ .cm-badge.is-hidden { display: none; }
4972
5085
  /* 「入ってこない」ときに自分で確かめる口。一覧の直前に置く */
4973
5086
  .ds-syncnow { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin: 0 0 .6rem; }
4974
5087
  .ds-syncnow-label { font-size: .82rem; color: var(--muted); }
@@ -5063,7 +5176,7 @@ a.ds-tile:hover { text-decoration: none; }
5063
5176
  .ds-check-label, .ds-check-action, .ds-check-detail,
5064
5177
  .ds-pipe-label, .ds-pipe-todo,
5065
5178
  .ds-empty, .sec-how, .modal-desc, .ds-toggle-msg,
5066
- .ds-drive-note, .ds-inactive-note, .ds-gate-help, .ds-not-ingested-note,
5179
+ .ds-drive-note, .ds-inactive-note, .ds-gate-help,
5067
5180
  /* 案件カレンダーの行(#428)。**カレンダー ID は空白の無い長い連なり**で、
5068
5181
  失敗の理由(detail)も長さが読めない。ここを外すと会議のモーダルが横に溢れる */
5069
5182
  .mt-cal-banner, .mt-cal-note, .mt-cal-warn, .mt-cal-primary, .mt-cal-sub, .mt-cal-id .rt-copy-text,
@@ -5112,13 +5225,11 @@ a.ds-tile:hover { text-decoration: none; }
5112
5225
  トグルを撤去したいま、読み取られていないことを示すのはこのバッジだけ。
5113
5226
  **判断のバッジなので、由来や組織のバッジより強く出す** */
5114
5227
  .badge.ds-excluded { background: #f3f4f6; color: #4b5563; border: 1px solid #d1d5db; }
5115
- /* 「接続はできるが Gold の材料にしていない」印(#640)。**「読み取り停止中」と同じ灰色**
5116
- ——どちらも「いまこの接続先がどう扱われているか」の印で、直すものが無い中立の事実。
5117
- 橙(警告)にすると、直しようのないものを直しに行かせる */
5228
+ /* 「接続はできるが、決定・用語集の材料にしていない」印(#640・語は #660)。
5229
+ **「読み取り停止中」と同じ灰色**——どちらも「いまこの接続先がどう扱われているか」の印で、
5230
+ 直すものが無い中立の事実。橙(警告)にすると、直しようのないものを直しに行かせる。
5231
+ 届いていない回は 「hidden」 で伏せる(上の [hidden] の !important が inline-block に勝つ) */
5118
5232
  .badge.ds-not-ingested { background: #f3f4f6; color: #4b5563; border: 1px solid #d1d5db; }
5119
- /* その理由と、変えたいときの一手(1 行だけ)。**行の補足(.ds-gate-help)と同じ見た目**だが、
5120
- 「接続を確認」で隠される棚には置かない(押しても変わらない事実のため) */
5121
- .ds-not-ingested-note { font-size: 12px; color: #6b7280; line-height: 1.6; margin: 4px 0 0; flex: 1 1 100%; }
5122
5233
  /* 登録の由来(#118)。**薄く出す**——読み取りの可否や Gold 対象かより弱い情報で、
5123
5234
  ここが目立つと「除外」「未宣言」の判断のバッジと同じ強さに見えてしまう */
5124
5235
  /* どの Slack ワークスペースのチャンネルか(#104)。**由来のバッジと同じ強さにする**——
@@ -5639,6 +5750,13 @@ ${capabilityDisplayState.toString()}
5639
5750
  ${pending_ops_1.backlogTargetOf.toString()}
5640
5751
  ${issuesRefOf.toString()}
5641
5752
  ${sourceNeedsAttention.toString()}
5753
+ // 能力→パイプラインの対応表(#654)と、⚠️ の出どころ。**並びは読みやすさのためで、
5754
+ // 依存の順ではない**——関数宣言はこの IIFE の中で巻き上がる(実装レビュー🟡4 で実測)。
5755
+ // 🔴 **効くのは「対応表をモジュール直下の const にしない」のほう**——そこへ出して
5756
+ // export すると、tsc が参照を書き換えて、流し込んだ関数がブラウザで未定義を見る
5757
+ //(番人は state-vocab-parity / ingest-partial-failure の「素の JS だけを渡す」検査)
5758
+ ${capabilityPipeRe.toString()}
5759
+ ${capabilityWarnSource.toString()}
5642
5760
  ${capabilityState.toString()}
5643
5761
  // ライブ確認の見切り(#256)。**書き写さない**——テストが固定している値と
5644
5762
  // 画面が使う値が割れると、「10秒で戻る」検査が通ったまま実機では戻らなくなる
@@ -5733,6 +5851,7 @@ ${sanitizeStateNow.toString()}
5733
5851
  ${devExcludedSkip.toString()}
5734
5852
  ${devExcludedRefs.toString()}
5735
5853
  ${devExcludedLabel.toString()}
5854
+ ${devExcludedBadgeVisible.toString()}
5736
5855
  ${overlayStateOnFleet.toString()}
5737
5856
  // Home の「使用ツール」(#275)。**判定と表示語の実体はビルド時の定義**——
5738
5857
  // 画面側に書き写すと、状態の言い換えが 2 枚になって片方だけ直る。
@@ -8923,20 +9042,14 @@ ${calendarAddUrl.toString()}
8923
9042
  // Issues と Discussions は同じ接続先(同じ dev 行)なので、理由・補足は1本目だけが
8924
9043
  // 持つ(#346)。同じ長文が2行に並ぶと一覧が案内で埋まる
8925
9044
  var seenRef = {};
8926
- // 「材料にしていない」の 1 行は、**この組織で実際に出した行があってから**畳む(#640)。
8927
- // 🔴 「seenRef」 を写経して「1 本目かどうか」でフラグを立てると、1 本目が
8928
- // 併記を持たない行だったときにフラグだけ消費され、**その組織のどの行にも
8929
- // 補足が出ない**(設計レビュー🟡5)。**出した行だけを数える**
8930
- var shownExcluded = false;
9045
+ // 「quietExcluded」(「材料にしていない」の補足を組織で 1 回だけ出す仕掛け)は
9046
+ // #660 で撤去した——補足そのものを撤去したので、畳む対象が無い。**足し戻すときは
9047
+ // 「1 本目かどうか」ではなく「実際に出した行があったか」で数えること**(1 本目が
9048
+ // 出さない行だったときにフラグだけ消費され、その組織のどの行にも出なくなる・#640 設計レビュー🟡5
8931
9049
  var cards = g.items.map(function (x) {
8932
9050
  var again = !!(x.ref && seenRef[x.ref]);
8933
9051
  if (x.ref) seenRef[x.ref] = 1;
8934
- // 🔴 **フラグを立てるのは、その行が実際に補足を出すときだけ。** 出さない行
8935
- //(同じ ref の 2 本目・「読み取り停止中」)でフラグを消費すると、**その組織の
8936
- // どの行にも補足が出ない**(設計レビュー🟡5 と同じ罠。条件は 「extSourceCard」 と揃える)
8937
- var quietExcluded = shownExcluded;
8938
- if (!again && x && x.ingestExcluded === true && x.goldState !== "excluded") shownExcluded = true;
8939
- return extSourceCard(x, DS_TYPE[x.type] || { icon: "🔗", label: x.type || "その他" }, { quietHelp: again, quietExcluded: quietExcluded });
9052
+ return extSourceCard(x, DS_TYPE[x.type] || { icon: "🔗", label: x.type || "その他" }, { quietHelp: again });
8940
9053
  });
8941
9054
  if (g.state === "warn") {
8942
9055
  out.push(orgNoteBox(githubNotConnectedNodes(g), cards));
@@ -10284,7 +10397,11 @@ ${calendarAddUrl.toString()}
10284
10397
  : " Cortex のアプリは、会話の読み取りと、通知先に設定したスペースへの確認・通知の投稿だけに使われます。読み取った会話は、本文そのものを保存するのではなく、決定事項・用語などの抽出結果としてプロジェクトの記録に反映されます。";
10285
10398
  m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいる" + chatNoun + "は、例外なく全て毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。読み取りを止めるには「登録を解除」してください。" + chatScope }));
10286
10399
  } else if (cap === "開発") {
10287
- m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいるリポジトリの Issues を毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。読み取りをやめるには「登録から削除」してください。" }));
10400
+ // 🔴 **開発の開示文は「記録に使う・決定の抽出には使わない」(#660)。** 2026-09-18
10401
+ // オーナー決定でソースの性質は種類で固定になり、GitHub は「作業の記録」に固定された。
10402
+ // **この 2 文が「決定の材料にはしない」を言う唯一の場所**——行の札は「活動を記録中」と
10403
+ // だけ言い、同じ文を行ごとに繰り返さない(帯は症状・行は一手・繰り返さない・2026-09-02)
10404
+ m.modal.appendChild(el("p", { class: "modal-desc", text: "ここに並んでいるリポジトリの Issues と PR の活動を、毎晩記録として読みます(誰が何をしているかの把握に使います)。決定・用語集の抽出には使いません。記録をやめるには「登録から削除」してください。" }));
10288
10405
  }
10289
10406
  // **なぜこの状態なのかを、押した先でも読めるようにする(#125②)。** タイルは常時出す
10290
10407
  // ようになったので、未接続の能力を押したときに中身が空になる場面が増えた。
@@ -11202,7 +11319,8 @@ ${calendarAddUrl.toString()}
11202
11319
  // **unset を idle に短絡しない(#153)。** 短絡すると gate 異常(読めていない行)の
11203
11320
  // warn 判定を一度も通らず、接続マップだけが⚠️になる。アダプタごとの状態の解決
11204
11321
  //(adapters[] / 旧データの代表値の扱い)は capabilityState 自身が持つ
11205
- var st = capabilityState(cap, stateCtx(), adapter);
11322
+ var stCtx = stateCtx();
11323
+ var st = capabilityState(cap, stCtx, adapter);
11206
11324
  // 🔴 **状態の正本は「登録・実測からの導出」(0.12.0・#231 / #235 / #227)。**
11207
11325
  // 設定が済んでいること(capabilities が on)と、実際に動いていることは別。
11208
11326
  // 設定だけを見て「● 接続OK」と出すと、**画面が動いていないものを動いていると言う**
@@ -11223,8 +11341,17 @@ ${calendarAddUrl.toString()}
11223
11341
  // 残る**——夜間がまだ一度も走っていない案件・ストアが読めなかった回。そこで
11224
11342
  // fleet の行から出た warn を導出(規則4=idle)が上書きすると
11225
11343
  // 「接続グラフは⚠️・タイルは静か」——この仕組みが殺したはずの食い違いが再発する
11226
- if (derive && !(st === "warn" && derive.state !== "warn")) st = derive.state;
11227
- else if (derive && st === "warn") derive = null;
11344
+ //
11345
+ // 🔴 **ただし門が効くのは「ライブの行」から立った warn だけ(#654)。** まとめ表の
11346
+ // pipelines(ワークフローの run の結論)は**ビルド時に焼かれ、ライブで取り直されない**
11347
+ // ——stateCtx が接続状態ストアを重ねるのは externalSources だけなので、一度赤く
11348
+ // なると次のビルドまで赤いまま。そこに門を効かせると、**ストアが「たった今 取り込めた」と
11349
+ // 言っている能力のタイルだけが「⚠️ 同期が失敗しています」**になる(gift-stvv・
11350
+ // 2026-09-17。同じページのモーダルと接続マップは「接続OK」と言っていた)。
11351
+ // 出どころの判定は capabilityWarnSource 1 本——ここに写しを作らない
11352
+ var liveWarn = st === "warn" && capabilityWarnSource(cap, stCtx, adapter) === "live";
11353
+ if (derive && !(liveWarn && derive.state !== "warn")) st = derive.state;
11354
+ else if (derive) derive = null;
11228
11355
  // **ロゴはタイルの左に独立して置く。** 名前の行に入れると名前の高さに
11229
11356
  // 縛られて小さくなる。ロゴで能力を見分けられるのが一覧の主な用途
11230
11357
  var logo = ADAPTER_ICONS[adapter];
@@ -11256,7 +11383,12 @@ ${calendarAddUrl.toString()}
11256
11383
  // (意図的に止めた・設定が壊れている)で導出が止まった回にストアの ok で「接続OK」と
11257
11384
  // 言うと、表 5 の優先順位(OFF・設定に異常が導出より先)を裏切る(実装レビュー🟡2)
11258
11385
  var pendNow = pend && (derived == null || derived === "on" || derived === "unset") ? pendingTileVerdict(cap, adapter) : null;
11259
- if (pendNow && st === "warn" && pendNow.cls === "ds-gate-ok") pendNow = null;
11386
+ // **判定の写しを 2 つにしないため liveWarn に寄せてある(#654)。** ここは
11387
+ // 実際には st === "warn" と等価——pendNow が立つのは derived が unset で導出を
11388
+ // 止めた経路だけで、その経路の warn はライブの行からしか立たない(実装レビュー🟡3)。
11389
+ // **「焼いた pipelines がここを通る」という経路は無い**ので、そう読んで条件を
11390
+ // いじらないこと
11391
+ if (pendNow && liveWarn && pendNow.cls === "ds-gate-ok") pendNow = null;
11260
11392
  // 🔴 **初回のライブ確認が答えるまで、状態を断言しない(#256・設計v2 の5)。**
11261
11393
  // この2行目は焼いた capabilities 由来の状態を先に言っていたので、図と同じ瞬き
11262
11394
  // (旧状態を見せて1〜2秒後に訂正)をタイルでも起こしていた。**兄弟機能の同型の
@@ -11440,8 +11572,11 @@ ${calendarAddUrl.toString()}
11440
11572
  }
11441
11573
 
11442
11574
  // 注記は全能力の後ろに1つ。**何が Gold の材料になるのかを1文で言う。**
11575
+ // 🔴 **GitHub を同じ文に混ぜない(#660)。** 性質が種類で固定になり、GitHub は
11576
+ // 決定・用語集の材料にしない。ひとまとめに「材料になります」と言うと、この注記だけが
11577
+ // 画面の他の全部(見出し・行の札・タイル)と食い違う
11443
11578
  if (anyCap) {
11444
- sec.appendChild(el("p", { class: "ds-note", text: "登録されている Slack チャンネル・Google Chat スペース・GitHub リポジトリは、毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。チャンネルとスペースは登録を解除すると、リポジトリは登録から削除すると、以後は読み取られません。" }));
11579
+ sec.appendChild(el("p", { class: "ds-note", text: "登録されている Slack チャンネル・Google Chat スペースは、毎晩の Gold 昇格が読み取り、Decision・用語集の材料になります。GitHub リポジトリは Issues と PR の活動を記録として読み、決定・用語集の材料にはしません。チャンネルとスペースは登録を解除すると、リポジトリは登録から削除すると、以後は読み取られません。" }));
11445
11580
  }
11446
11581
 
11447
11582
  if (pipes.length) {
@@ -11890,6 +12025,17 @@ ${calendarAddUrl.toString()}
11890
12025
  var v = probeVerdict(s, j);
11891
12026
  gateEl.className = "ds-gate " + v.cls;
11892
12027
  gateEl.textContent = v.text;
12028
+ // 🔴 **「活動を記録中」の札も、押した結果で出し入れする(#660)。** ここを忘れると、
12029
+ // アクセス不可の行を直して押したときに**行は「接続OK」なのに札だけ消えたまま**に
12030
+ // なり、「決定の材料になっている」と読める(逆向きも同じで、届かなくなった行が
12031
+ // 「記録中」と言い続ける)。**判定は 「devExcludedBadgeVisible」 1 本**——
12032
+ // 描画時と押した後で別の条件を書くと、同じ行が 2 つのことを言う
12033
+ var exBadgeNow = card.querySelector(".badge.ds-not-ingested");
12034
+ if (exBadgeNow) {
12035
+ exBadgeNow.hidden = !devExcludedBadgeVisible({
12036
+ ingestExcluded: s.ingestExcluded, goldState: s.goldState, gate: j.gate,
12037
+ });
12038
+ }
11893
12039
  // 古い「X分前に確認」を残さない(下に出るいまの結果と、時刻が2つ並ぶ)
11894
12040
  var oldAt = card.querySelector(".ds-gate-at");
11895
12041
  if (oldAt) oldAt.remove();
@@ -13854,7 +14000,10 @@ ${calendarAddUrl.toString()}
13854
14000
  hint: [
13855
14001
  { text: "読み取りたいリポジトリがある組織に、Cortex の GitHub App を入れます(Issues の読み取りだけ)。", node: appInstallLink() },
13856
14002
  "そのリポジトリを下の欄に入れて「追加」を押します(owner/repo でも、GitHub のURLでも構いません)。",
13857
- "追加したリポジトリの Issues が、毎晩の Gold 昇格の材料になります。やめるときは、その行の「登録から削除」を押します。",
14003
+ // #660: Gold 昇格の材料になります」は嘘になった(性質が種類で固定・GitHub は
14004
+ // 決定の抽出に使わない)。**手順の最終行が伝える重さは「記録に入る」**——
14005
+ // 追加が Gold 昇格の意思決定になるチャット(上の 2 つ)とは別の文にする
14006
+ "追加したリポジトリの Issues と PR の活動が、毎晩の記録に入ります(決定・用語集の材料にはしません)。やめるときは、その行の「登録から削除」を押します。",
13858
14007
  ],
13859
14008
  build: function (repo, name) { return { op: "add-github-source", repo: repo, name: name }; },
13860
14009
  adder: "github",
@@ -14540,11 +14689,7 @@ ${calendarAddUrl.toString()}
14540
14689
  title: "このリポジトリは読み取り対象から除外されています(開発の登録ファイル dev-sources.json の exclude)。この画面からは戻せません。",
14541
14690
  }));
14542
14691
  }
14543
- // 「接続できる」と「Gold の材料にしている」を別の状態として併記する(#640・engine #399)。
14544
- //
14545
- // **常に出す(gate の値で隠さない)。** 届かないことと材料にしていないことは別の事実で、
14546
- // 片方を隠すと「アクセス不可を直せば読まれる」と誤読させる(#523・オーナー決定
14547
- // 「操作タブは常に真実を表示する」)。
14692
+ // 「接続できる」と「決定の材料にしている」を別の状態として併記する(#640・#660)。
14548
14693
  //
14549
14694
  // 🔴 **「ds-gate」 ではなく 「badge」 で出す。** 行の「判定のバッジは 1 つ」を
14550
14695
  // 「connection-probe.test.mjs」 が見張っており、「probeSection」 も 「gateEl」 の参照 1 本で
@@ -14553,17 +14698,32 @@ ${calendarAddUrl.toString()}
14553
14698
  // ——どちらも「判定」ではなく「この接続先がいまどう扱われているか」の印。
14554
14699
  //
14555
14700
  // 🔴 **警告色にしない。** 直すものが無い中立の事実で、橙にすると直しに行かせる。
14556
- // 語は短く(長文を入れると行が折り返す)——性質の変え方は下の補足 1 行が持つ
14557
- // 🔴 **「読み取り停止中」の行には出さない(実装レビュー🟡4)。** 人が exclude で止めた
14558
- // 行は engine が観測しなくなる= 2 行とも書き直されないので、古い中立の行が永久に残る。
14559
- // しかも止めてある以上、性質を「会話」にしても読まれない——**効かない一手**を案内する
14560
- // ことになる。灰色の札が 2 つ並ぶのも「繰り返さない」に反する
14561
- if (s.ingestExcluded === true && s.goldState !== "excluded") {
14562
- badges.appendChild(el("span", {
14701
+ // 語は短く(長文を入れると行が折り返す)——「決定の材料にはしない」はモーダルの
14702
+ // 見出しと 「title」 が持つ(#660。行ごとに繰り返さない・2026-09-02)。
14703
+ //
14704
+ // 🔴 **要素は必ず作り、出し入れは 「hidden」 でやる(#660)。** 出す条件は
14705
+ // 「devExcludedBadgeVisible」 1 本だが、「probeSection」 は押した結果で 「.ds-gate」 しか
14706
+ // 差し替えない。作らずに条件分岐で消すと、**アクセス不可の行を直して「接続を確認」を
14707
+ // 押しても札が戻らない**(行は「接続OK」なのに札が無い=材料にしていると読める)。
14708
+ // 「[hidden]」 に 「display:none !important」 を当ててあるので、「.badge」 の
14709
+ // 「display:inline-block」 に負けない
14710
+ // 🔴 **作るかどうかも 「devExcludedBadgeVisible」 に決めさせる(判定を 2 枚にしない)。**
14711
+ // 「gate を ok にしたら出るか」=「この行はそもそも札を持ちうるか」。条件を書き写すと、
14712
+ // 「読み取り停止中」の扱いを片方だけ直した日に、**作られるが永久に伏せられる札**や
14713
+ // **止めてある行の札**が生まれる(実装レビュー🟡3)
14714
+ if (devExcludedBadgeVisible(Object.assign({}, s, { gate: "ok" }))) {
14715
+ var exBadge = el("span", {
14563
14716
  class: "badge ds-not-ingested",
14564
14717
  text: devExcludedLabel(s.ingestExcludedDetail),
14565
- title: "接続はできています。この登録は Gold(決定・用語・メンバー・ルール)の材料にしていません。",
14566
- }));
14718
+ // **「接続はできています」と書かない。** 到達性をまだ測れていない回(gate が空)も
14719
+ // 札は出るので、行の判定が「登録状況を確認中です」なのに title だけが接続を断言する
14720
+ //(同じ行が 2 つのことを言う・実装レビュー🟡2)
14721
+ title: "Issues と PR の活動を記録として読みます。決定・用語集の材料にはしません。",
14722
+ });
14723
+ // ⚠ 「el」 は属性で渡す(「setAttribute("hidden", false)」 は 「hidden="false"」 になり、
14724
+ // HTML では**存在するだけで真**)。真偽はプロパティで入れる
14725
+ exBadge.hidden = !devExcludedBadgeVisible(s);
14726
+ badges.appendChild(exBadge);
14567
14727
  }
14568
14728
  if (s.notify === true) badges.appendChild(notifyBadge());
14569
14729
  // **どのワークスペースのチャンネルかを、必ず行に出す(#104・2026-08-20 に社内も)。**
@@ -14726,9 +14886,16 @@ ${calendarAddUrl.toString()}
14726
14886
  // **「読み取りを止める」への案内は消した(2026-08-21)。** そのボタンごと
14727
14887
  // 撤去したので、指す先が画面に無い。**代わりの手段は書かない**
14728
14888
  //——削除がその手段そのもので、他に無い
14729
- "次回の Gold 昇格から、このリポジトリの Issues は読み取られません。",
14889
+ "次回の夜間の処理から、このリポジトリの Issues PR の活動は記録されません。",
14730
14890
  "",
14731
- "すでに作られた Decision・用語集は消えません。",
14891
+ // 🔴 **不可逆性を必ず書く**(docs/setup-ui-spec.md の確認ダイアログの規則)。
14892
+ // #660 で「すでに作られた Decision・用語集は消えません。」は落とした
14893
+ //——GitHub は決定・用語集の材料にしないと画面が 4 か所で言い切る以上、
14894
+ // この 1 行だけが「GitHub が Decision を作った」と言うことになる。
14895
+ // **代わりに、いま本当に残るもの**を書く: 観測の台帳(engine の obs/)は
14896
+ // Delete が Deny で**誰にも消せない**ので、「記録中」と言い切るぶん
14897
+ // #640 のときより強い事実になっている
14898
+ "すでに記録した活動は消えません。",
14732
14899
  "",
14733
14900
  "削除しますか?",
14734
14901
  ];
@@ -14762,28 +14929,13 @@ ${calendarAddUrl.toString()}
14762
14929
  if (reason) card.appendChild(neededNode(reason, "ds-gate-help ds-gate-reason"));
14763
14930
  }
14764
14931
  }
14765
- // 「材料にしていない」の理由と、変えたいときの一手(#640)。**1 行だけ。**
14766
- //
14767
- // 🔴 **「ds-gate-help」 を付けない。** あちらは「接続を確認」を押した瞬間に
14768
- // 「probeSection」 がまとめて隠す札で、押した人がいちばん誤読しやすい瞬間
14769
- //(「確認したから読まれるようになった」)に、この 1 行だけが消える。
14770
- // 押しても変わらない事実なので、隠される棚に置かない(幅の扱いは CSS で揃える)。
14771
- //
14772
- // 🔴 **その組織のグループで最初の 1 行にだけ出す(「opts.quietExcluded」)。** GitHub の
14773
- // 既定は「作業物」なので、全行に出すと一覧が案内で埋まる(#356・オーナー却下の原則)。
14774
- // 同じリポジトリの 2 本目(Discussions)にも出さない(「opts.quietHelp」 と同じ理由)
14775
- if (s.ingestExcluded === true && s.goldState !== "excluded" && !(opts && (opts.quietHelp || opts.quietExcluded))) {
14776
- // 🔴 **性質を直書きしない(実装レビュー🟡2)。** すぐ上の札は理由文が名指しした語だけを
14777
- // 括弧に入れているのに、ここで「作業物」と書くと、engine が名指しを避けた回に
14778
- // **同じ行が 2 つのことを言う**(札は「読み取りの対象外」・補足は「作業物」)。
14779
- // 名指しできた回だけ性質を言い、できなかった回は事実だけを言う
14780
- var exNature = devExcludedLabel(s.ingestExcludedDetail).replace(/^読み取りの対象外(?/, "").replace(/)$/, "");
14781
- card.appendChild(el("p", {
14782
- class: "ds-not-ingested-note",
14783
- text: (exNature ? "この登録は「" + exNature + "」なので、" : "この登録は、")
14784
- + "決定・用語・メンバー・ルールの材料にしていません。議論の場として読ませるなら、登録簿で性質を「会話」にしてください",
14785
- }));
14786
- }
14932
+ // 🔴 **「材料にしていない」の補足(「ds-not-ingested-note」)は撤去した(#660)。**
14933
+ // #640 の文は「この登録は『作業物』なので…議論の場として読ませるなら、登録簿で性質を
14934
+ // 『会話』にしてください」で、**画面に定義の無い語で JSON を手で直せと言っていた**
14935
+ //(実測: 艦隊 49 案件・GitHub 登録 53 行で切り替えた案件は 0 件)。2026-09-18 の
14936
+ // オーナー決定で性質は種類で固定になり、**利用者が変えられる一手はもう無い**ので、
14937
+ // 行に長文を置く理由が消えた。言うべきことはモーダルの見出し(能力を開いた先の 2 文)と
14938
+ // 札の 「title」 が持つ。**足し戻さないこと**(行は一手・繰り返さない・2026-09-02)
14787
14939
  // 「接続を確認」(2026-08-19)。**押せる人を絞らない**——読むだけで設定を変えないので、
14788
14940
  // 気づいた人がその場で確かめられるほうがよい(オーナー判断)。書き込みになる
14789
14941
  // 「表示名を更新する」は、押した結果の中から別のボタンとして出る(probeSection)。
@@ -15941,6 +16093,28 @@ ${calendarAddUrl.toString()}
15941
16093
  if (capConfigState(cap)) { restoreBakedConnMap(cap); continue; }
15942
16094
  var d = capDerivation(cap);
15943
16095
  if (!d) { restoreBakedConnMap(cap); continue; } // 規則5: 材料が無い → 焼いた図のまま
16096
+ // 🔴 **図にもタイルと同じ門を効かせる(#654 実装レビュー🔴1)。** ライブの行から
16097
+ // 立った warn(ストアがまだ測っていない接続先の gate)は導出が降格しない
16098
+ //(#153)。タイルだけが門を持っていると「タイルは⚠️・図は無言」——R10 が
16099
+ // 殺したはずの食い違いの鏡像になる。門の材料はタイルと同じ 1 本
16100
+ //(capabilityWarnSource)で、焼いた pipelines の赤はここでも効かない
16101
+ var mapCtx = stateCtx();
16102
+ var mapLiveWarn = capabilityState(cap, mapCtx) === "warn" &&
16103
+ capabilityWarnSource(cap, mapCtx) === "live" && d.state !== "warn";
16104
+ if (mapLiveWarn) {
16105
+ // 焼いた表示(3行目の語)へ戻してから、状態だけ warn に固定する
16106
+ // ——図は幅が 148px しかなく、行と同じ語はタイルが持つ
16107
+ restoreBakedConnMap(cap);
16108
+ var wNode = wrap.querySelector('[data-target="ds-' + cap + '"]');
16109
+ if (wNode) {
16110
+ wNode.setAttribute("class", "cm-node cm-warn");
16111
+ var wBadge = wNode.querySelector(".cm-badge");
16112
+ if (wBadge) wBadge.setAttribute("class", "cm-badge");
16113
+ }
16114
+ var wLink = wrap.querySelector('.cm-link[data-cm-cap="' + cap + '"]');
16115
+ if (wLink) wLink.setAttribute("class", "cm-link cm-warn");
16116
+ continue;
16117
+ }
15944
16118
  var node = wrap.querySelector('[data-target="ds-' + cap + '"]');
15945
16119
  if (!node) continue;
15946
16120
  node.setAttribute("class", "cm-node cm-" + d.state);
@@ -15960,6 +16134,11 @@ ${calendarAddUrl.toString()}
15960
16134
  link.setAttribute("class", "cm-link cm-" + d.state
15961
16135
  + (d.state === "ok" || d.state === "warn" ? "" : " is-hidden"));
15962
16136
  }
16137
+ // 🔴 **⚠️バッジも当て直す(#654)。** 当て直さないと、ビルド時に赤かった能力は
16138
+ // ライブで接続OKに直っても⚠️を出し続ける(「緑のノードに⚠️」)。線と同じ形で
16139
+ // 常時焼いてあるので、ここは表示の切り替えだけ
16140
+ var badge = node.querySelector(".cm-badge");
16141
+ if (badge) badge.setAttribute("class", "cm-badge" + (d.state === "warn" ? "" : " is-hidden"));
15963
16142
  // 確定したので退避は要らない(設計v3)。**残すと、あとの取得が失敗したときに
15964
16143
  // 確定済みの表示へ焼いた値が上書きされる**——訂正した嘘が戻ってくる
15965
16144
  forgetBakedConnMap(node, link);
@@ -15982,11 +16161,10 @@ ${calendarAddUrl.toString()}
15982
16161
  * derived が null / "on")。OFF・異常・未設置の語は設定そのものの話で、ライブが
15983
16162
  * 届いても変わらない——確認中にしたら、応答が来ても戻す相手がいなくなる。
15984
16163
  *
15985
- * ⚠ **⚠️バッジ(cm-badge)は触らない。** あれは applyStateToConnMap も当て直して
15986
- * いない(ビルド時に warn のノードにだけ焼かれ、以後そのまま)。確認中の間だけ
15987
- * 隠すと、応答で warn が確定したときに**失敗が図から消えたまま**になる
15988
- * ——「cm-warn を隠さない」の逆をこちらの都合で作る。バッジごと2段化するなら、
15989
- * 6ノードに常時バッジを焼いて表示を切り替える形(線と同じ)にする別便が要る。
16164
+ * ⚠ **⚠️バッジ(cm-badge)は触らない。** 確認中の間だけ隠すと、応答で warn
16165
+ * 確定したときに**失敗が図から消えたまま**になる——「cm-warn を隠さない」の逆を
16166
+ * こちらの都合で作る。**応答が届いたあとは applyStateToConnMap が当て直す**
16167
+ * (#654 でバッジを 6 ノードに常時焼き、線と同じ「表示の切り替え」にした)。
15990
16168
  */
15991
16169
  function showConfirmingConnMap() {
15992
16170
  var wrap = app.querySelector(".conn-map");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.69",
3
+ "version": "0.12.71",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,