@takagaki/cortex-decisions-viewer 0.4.44 → 0.4.45

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
@@ -135,6 +135,9 @@ async function readFleetSources(repoRoot) {
135
135
  ref: s.ref != null ? String(s.ref) : undefined,
136
136
  url: s.url != null ? String(s.url) : undefined,
137
137
  gold: typeof s.gold === "boolean" ? s.gold : undefined,
138
+ // goldState は gold の理由。`gold: false` だけだと「意図的に外した」「宣言し忘れ」
139
+ // 「exclude による除外」が同じ値になり、**正常な除外にまで警告を出して麻痺する**。
140
+ goldState: s.goldState != null ? String(s.goldState) : undefined,
138
141
  notify: typeof s.notify === "boolean" ? s.notify : undefined,
139
142
  gate: s.gate != null ? String(s.gate) : undefined,
140
143
  }))
@@ -178,7 +181,8 @@ async function readFleetSources(repoRoot) {
178
181
  if (!Object.keys(tools).length)
179
182
  tools = undefined;
180
183
  }
181
- return { externalSources, internalSources, pipelines, tools };
184
+ const fleetGeneratedAt = typeof obj.generatedAt === "string" ? obj.generatedAt : undefined;
185
+ return { externalSources, internalSources, pipelines, tools, fleetGeneratedAt };
182
186
  }
183
187
  /** パスがディレクトリとして存在するか */
184
188
  async function isDirectory(dirAbs) {
@@ -667,7 +671,7 @@ function buildGraph(decisions, home, sections, content, targetUrls, repoBaseUrl,
667
671
  const deduped = edges.filter((e) => {
668
672
  if (e.from === e.to)
669
673
  return false;
670
- const key = `${e.from}${e.to}${e.rel}`;
674
+ const key = `${e.from}\u0000${e.to}\u0000${e.rel}`;
671
675
  if (seen.has(key))
672
676
  return false;
673
677
  seen.add(key);
@@ -979,6 +983,7 @@ async function build(opts) {
979
983
  }
980
984
  : undefined,
981
985
  externalSources: fleet.externalSources,
986
+ fleetGeneratedAt: fleet.fleetGeneratedAt,
982
987
  internalSources: fleet.internalSources,
983
988
  pipelines: fleet.pipelines,
984
989
  tools: fleet.tools,
package/dist/render.js CHANGED
@@ -91,6 +91,23 @@ const CM_TOOL_LABEL = {
91
91
  * 状態: 健全=色付き+流れるアニメーション / 問題あり=黄+⚠️ / 未使用=グレー。
92
92
  * fleet-status由来の情報が何も無ければ null(マップを出さない・後方互換)。
93
93
  */
94
+ /**
95
+ * このソースが「注意が必要」か。接続マップ(ビルド時生成)とデータソース一覧(クライアント側)で
96
+ * **同じ判定を使う**ためにここに置く。片方だけ直すと、同じ画面でマップが⚠️・直下が「接続OK」になる。
97
+ *
98
+ * 判定の考え方:
99
+ * - 明示的に Gold 対象から外したもの(`gold === false`)は、読めなくて当たり前なので警告しない。
100
+ * ただし通知先(`notify`)に使っているなら、届いていないこと自体が問題なので拾う。
101
+ * - `gold` が無い(旧 fleet-status.json)ときは**警告する側に倒す**。「不明」を「対象外」と扱うと、
102
+ * 本当に壊れているものが黙って隠れる(フェイルオープン)。
103
+ */
104
+ function sourceNeedsAttention(s) {
105
+ if (s.gate === "ok")
106
+ return false;
107
+ if (s.gold === false && s.notify !== true)
108
+ return false;
109
+ return true;
110
+ }
94
111
  function renderConnectionMap(site) {
95
112
  const internals = site.internalSources ?? [];
96
113
  const externals = site.externalSources ?? [];
@@ -119,7 +136,9 @@ function renderConnectionMap(site) {
119
136
  // 問題あり: ライブ参照はgate≠okが1件以上、同期ソースは対応パイプラインの直近runがfailure
120
137
  const warn = !off &&
121
138
  (isLive
122
- ? liveExts.some((s) => s.gate !== "ok")
139
+ // カード側の集計(sourceNeedsAttention)と同じ判定にする。ここだけ接続状態だけを見ていると、
140
+ // マップが⚠️なのに直下のグループが「接続OK」という矛盾が同じ画面に出る。
141
+ ? liveExts.some((s) => sourceNeedsAttention(s))
123
142
  : pipes.some((p) => c.pipe != null && c.pipe.test(p.id) && p.applicable !== false && p.lastConclusion === "failure"));
124
143
  const state = off ? "cm-off" : warn ? "cm-warn" : "cm-ok";
125
144
  const toolName = toolVal && toolVal !== "none"
@@ -657,7 +676,8 @@ a:hover { text-decoration: underline; }
657
676
  /* ---- データソースと自動化 ---- */
658
677
  .datasources { margin-top: 34px; }
659
678
  .datasources-head { font-size: 18px; font-weight: 700; color: var(--text); margin: 0 0 4px; }
660
- .datasources-intro { color: var(--muted); font-size: 13.5px; line-height: 1.8; margin: 0 0 14px; }
679
+ .datasources-intro { color: var(--muted); font-size: 13.5px; line-height: 1.8; margin: 0 0 6px; }
680
+ .datasources-asof { color: var(--muted); font-size: 12.5px; line-height: 1.7; margin: 0 0 14px; }
661
681
  .ds-sub { font-size: 13px; font-weight: 700; color: var(--muted); margin: 20px 0 8px; }
662
682
  .ds-list { display: flex; flex-direction: column; gap: 10px; }
663
683
  .ds-card {
@@ -722,6 +742,7 @@ a.ds-name:hover { text-decoration: underline; }
722
742
  .ds-badges { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; flex: 0 0 auto; }
723
743
  .badge.ds-gold { background: #fef3c7; color: #92400e; border: 1px solid #fcd34d; }
724
744
  .badge.ds-excluded { background: #eceef1; color: #6b7280; }
745
+ .badge.ds-undeclared { background: #fff4e5; color: #9a6700; }
725
746
  .badge.ds-notify { background: #eff6ff; color: #1e40af; border: 1px solid #bfdbfe; }
726
747
  .ds-gate { display: inline-flex; align-items: center; gap: 5px; font-size: 12.5px; font-weight: 600; flex: 0 0 auto; }
727
748
  .ds-gate::before { content: ""; width: 8px; height: 8px; border-radius: 999px; background: currentColor; }
@@ -1348,6 +1369,14 @@ const CLIENT_JS = `
1348
1369
  var sec = el("section", { class: "datasources" });
1349
1370
  sec.appendChild(el("h2", { class: "datasources-head", text: "データソースと自動化" }));
1350
1371
  sec.appendChild(el("p", { class: "datasources-intro", text: "このCortexが接続している情報源と、毎晩自動でGoldに昇格する処理の一覧です。" }));
1372
+ // **いつ時点の情報かを必ず出す。** この節は夜間バッチが作るスナップショットを読んでいるので、
1373
+ // 設定を変えても次の生成までは古い値のまま。時刻が無いと「変えたのに反映されない」ではなく
1374
+ // 「変えたはずなのに違う値が出ている」と読まれてしまう(実際に設定変更直後にそうなった)。
1375
+ var asOf = SITE.fleetGeneratedAt ? dsFmtTime(SITE.fleetGeneratedAt) : "";
1376
+ if (asOf) {
1377
+ sec.appendChild(el("p", { class: "datasources-asof",
1378
+ text: "接続状況は " + asOf + " 時点の自動チェック結果です。設定を変えた場合、ここに反映されるのは次回の実行後になります。" }));
1379
+ }
1351
1380
 
1352
1381
  if (internals.length) {
1353
1382
  sec.appendChild(el("div", { class: "ds-sub", text: "リポジトリ内の情報源(自動同期)" }));
@@ -1414,14 +1443,27 @@ const CLIENT_JS = `
1414
1443
  var cards = el("div", { class: "ds-list" });
1415
1444
  g.items.forEach(function (s) { cards.appendChild(extSourceCard(s, meta)); });
1416
1445
  if (g.items.length >= 4) {
1417
- // 折りたたみ(既定は閉)。summaryに件数と状態の要約を出す
1446
+ // 折りたたみ(既定は閉)。summaryに件数と状態の要約を出す。
1447
+ // 判定は接続マップ側の sourceNeedsAttention と揃える(片方だけ直すと同じ画面で矛盾する)。
1448
+ // 意図的に外したソースが接続できないのは当たり前なので数えない。ただし通知先に
1449
+ // 使っているなら届いていないこと自体が問題なので拾う。gold 不明は警告する側に倒す。
1418
1450
  var bad = 0;
1419
- g.items.forEach(function (s) { if (s.gate !== "ok") bad++; });
1451
+ var noToken = 0;
1452
+ g.items.forEach(function (s) {
1453
+ if (s.gate === "ok") return;
1454
+ if (s.gold === false && s.notify !== true) return;
1455
+ bad++;
1456
+ if (s.gate === "no_token") noToken++;
1457
+ });
1420
1458
  var sum = el("summary", {}, [
1421
1459
  el("span", { text: meta.icon + " " + meta.label + "(" + g.items.length + (UNIT[g.type] || "件") + ")" }),
1422
1460
  bad === 0
1423
1461
  ? el("span", { class: "badge ds-sum-ok", text: "接続OK" })
1424
- : el("span", { class: "badge ds-sum-warn", text: "⚠️ " + bad + "件に問題" }),
1462
+ // 原因が1つに集約できるときは原因を書く。「19件に問題」より「トークンが未設定」の方が、
1463
+ // 読んだ人が次に何をすればよいか分かる(実際、19件の原因がトークン1本ということがあった)。
1464
+ : el("span", { class: "badge ds-sum-warn", text: noToken === bad
1465
+ ? "⚠️ " + bad + "件が読めていません(トークン未設定)"
1466
+ : "⚠️ " + bad + "件に問題" }),
1425
1467
  ]);
1426
1468
  var det = el("details", { class: "ds-group" }, [sum, cards]);
1427
1469
  if (anchorId) det.id = anchorId;
@@ -1439,7 +1481,7 @@ const CLIENT_JS = `
1439
1481
  // 注記は内部・外部の両グループの後ろに1つ(内部の直下だけに置くと外部ソースが
1440
1482
  // Gold昇格の対象外に見えるため)。除外の方法もここで案内する。
1441
1483
  if (internals.length || sources.length || chatOff || devOff) {
1442
- sec.appendChild(el("p", { class: "ds-note", text: "上記の情報源はすべて毎晩の Gold 昇格が読み取ります(「除外」バッジのものを除く。チャンネル・リポジトリ単位で除外できます)。" }));
1484
+ sec.appendChild(el("p", { class: "ds-note", text: "「Gold昇格の読み取り対象」のものだけを毎晩の Gold 昇格が読み取ります。「除外」は意図的に外したもの、「未宣言(対象外)」はまだ判断されていないもので、どちらも読み取りません(チャンネル・リポジトリ単位で切り替えられます)。" }));
1443
1485
  }
1444
1486
 
1445
1487
  if (pipes.length) {
@@ -1485,7 +1527,10 @@ const CLIENT_JS = `
1485
1527
  // 外部ソース1件のカード(グループ化表示・展開表示の両方から使う)
1486
1528
  function extSourceCard(s, meta) {
1487
1529
  var badges = el("div", { class: "ds-badges" });
1530
+ // 「意図的に外した」と「宣言し忘れ」を分けて出す。同じ「除外」と書くと、
1531
+ // 判断されていないだけのものが「判断済み」に見えてしまう。
1488
1532
  if (s.gold === true) badges.appendChild(el("span", { class: "badge ds-gold", text: "Gold昇格の読み取り対象" }));
1533
+ else if (s.goldState === "undeclared") badges.appendChild(el("span", { class: "badge ds-undeclared", text: "未宣言(対象外)" }));
1489
1534
  else if (s.gold === false) badges.appendChild(el("span", { class: "badge ds-excluded", text: "除外" }));
1490
1535
  if (s.notify === true) badges.appendChild(el("span", { class: "badge ds-notify", text: "通知先" }));
1491
1536
  var gate = DS_GATE[s.gate] || DS_GATE["unknown"];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.4.44",
3
+ "version": "0.4.45",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,