@takagaki/cortex-decisions-viewer 0.4.61 → 0.4.63

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
@@ -148,6 +148,26 @@ async function readFleetSources(repoRoot) {
148
148
  origin: s.origin != null ? String(s.origin) : undefined,
149
149
  }))
150
150
  : undefined;
151
+ // セットアップの充足チェック。**missing だけを通す。**
152
+ // na(使わないと宣言した能力)を通すと「設定する必要が無いものに設定を促す」偽の警告になり、
153
+ // #101 で潰した穴が復活する。unknown は AWS/gh へ到達できなかった回に出るので、
154
+ // 夜間実行がこけた日に嘘が出る。
155
+ // score / nextActions は通さない——nextActions は checks[].action を平坦化した
156
+ // 同じ内容の別キーで、通すと同じ文が2箇所に出る。
157
+ const checks = Array.isArray(obj.checks)
158
+ ? obj.checks
159
+ .filter((c) => c != null && typeof c === "object")
160
+ .filter((c) => String(c.status ?? "") === "missing")
161
+ .map((c) => ({
162
+ id: String(c.id ?? ""),
163
+ label: String(c.label ?? ""),
164
+ category: String(c.category ?? ""),
165
+ status: String(c.status ?? ""),
166
+ action: c.action != null ? String(c.action) : undefined,
167
+ detail: c.detail != null ? String(c.detail) : undefined,
168
+ }))
169
+ .filter((c) => c.id !== "")
170
+ : undefined;
151
171
  const internalSources = Array.isArray(obj.internalSources)
152
172
  ? obj.internalSources
153
173
  .filter((s) => s != null && typeof s === "object")
@@ -157,6 +177,9 @@ async function readFleetSources(repoRoot) {
157
177
  label: String(s.label ?? ""),
158
178
  url: s.url != null ? String(s.url) : undefined,
159
179
  lastSync: s.lastSync != null ? String(s.lastSync) : undefined,
180
+ // 取り込み済みの件数。**「同期が走った」と「実際に入った」は別物。**
181
+ // これが無いと、初回全量同期をしていない案件が「接続OK」に見える
182
+ itemCount: typeof s.itemCount === "number" ? s.itemCount : undefined,
160
183
  // 会議名に入れる合図。**古い fleet-status.json の matchKeys は引き継がない**
161
184
  // (client名と廃止した meetingNamePatterns の寄せ集めで、いま照合には使われていない。
162
185
  // 出すと「この語を会議名に入れれば拾われる」と読めてしまう)。
@@ -225,7 +248,7 @@ async function readFleetSources(repoRoot) {
225
248
  const secretSources = obj.secretSources && typeof obj.secretSources === "object"
226
249
  ? obj.secretSources
227
250
  : undefined;
228
- return { externalSources, internalSources, pipelines, tools, fleetGeneratedAt, secretSources };
251
+ return { externalSources, internalSources, pipelines, tools, fleetGeneratedAt, secretSources, checks };
229
252
  }
230
253
  /** パスがディレクトリとして存在するか */
231
254
  async function isDirectory(dirAbs) {
@@ -1101,6 +1124,7 @@ async function build(opts) {
1101
1124
  internalSources: fleet.internalSources,
1102
1125
  pipelines: fleet.pipelines,
1103
1126
  tools: fleet.tools,
1127
+ checks: fleet.checks,
1104
1128
  };
1105
1129
  // サムネイル(デザイン画面等)をサイトに同梱し、ノードのパスをサイト内相対に書き換える
1106
1130
  await node_fs_1.promises.mkdir(outAbs, { recursive: true });
package/dist/render.js CHANGED
@@ -224,7 +224,13 @@ function capabilityState(cap, ctx) {
224
224
  const pipeSkipped = ctx.pipes.some(function (p) {
225
225
  return pipeRe != null && pipeRe.test(p.id) && p.applicable === false;
226
226
  });
227
- const syncedForReal = internal != null && ((internal.lastSync && !pipeSkipped) || internal.url);
227
+ // **「同期が走った」と「実際にデータが入った」は別物。** 初回全量同期をしていない案件は、
228
+ // 毎晩の差分同期が成功し続けても0件のままになる(cortex-context の課題管理が実例で、
229
+ // lastSync はあるのに取り込み件数は0だった)。件数を見ないと、タイルが「接続OK」と言い、
230
+ // 同じ画面のセットアップ残りが「初回同期がまだです」と言う状態になる。
231
+ // 件数が無い(古い fleet-status.json・件数を持たない能力)ときは、いままでの判定に従う。
232
+ const emptyForReal = internal != null && internal.itemCount === 0;
233
+ const syncedForReal = internal != null && !emptyForReal && ((internal.lastSync && !pipeSkipped) || internal.url);
228
234
  const idle = !off &&
229
235
  (isLive
230
236
  ? liveExts.length === 0
@@ -944,6 +950,19 @@ a.ds-name:hover { text-decoration: underline; }
944
950
  width: 40px; height: 40px; flex: 0 0 40px; font-size: 26px; line-height: 1;
945
951
  }
946
952
  .ds-tile-logo svg { width: 100%; height: 100%; }
953
+ /* セットアップの残り。**直し方を状態と同じ大きさで出す**——
954
+ 何が足りないかだけ分かっても、次の一歩が分からなければ画面の意味が薄い */
955
+ .ds-tile-todo { color: var(--muted); }
956
+ .ds-checks { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
957
+ .ds-check-cat { font-size: 12px; font-weight: 700; color: var(--muted); margin-top: 8px; }
958
+ .ds-check {
959
+ display: flex; flex-direction: column; gap: 3px;
960
+ border-left: 3px solid #fcd34d; background: #fffbeb;
961
+ border-radius: 0 8px 8px 0; padding: 8px 12px;
962
+ }
963
+ .ds-check-label { font-size: 13px; font-weight: 700; color: var(--text); }
964
+ .ds-check-action { font-size: 13px; color: var(--text); line-height: 1.7; }
965
+ .ds-check-detail { font-size: 12px; color: var(--muted); }
947
966
  .ds-tile-desc { font-size: 12.5px; color: var(--muted); }
948
967
  .ds-tile-sub { font-size: 12.5px; margin-top: 2px; }
949
968
  .ds-tile-chev { color: var(--muted); }
@@ -969,6 +988,9 @@ a.ds-name:hover { text-decoration: underline; }
969
988
  .ds-gate-warn { color: #b45309; }
970
989
  .ds-gate-muted { color: #6b7280; }
971
990
  .ds-pipes { display: flex; flex-direction: column; gap: 8px; }
991
+ /* 実行状況の行に直し方を添える。同じ run の未達を別の節にも出すと画面に2回並ぶ */
992
+ .ds-pipe-wrap { display: flex; flex-direction: column; gap: 3px; }
993
+ .ds-pipe-todo { font-size: 12.5px; color: var(--muted); line-height: 1.7; padding-left: 4px; }
972
994
  .ds-pipe {
973
995
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 12px;
974
996
  padding: 10px 14px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow);
@@ -1708,7 +1730,10 @@ ${capabilityState.toString()}
1708
1730
  m.modal.appendChild(el("div", { class: "modal-actions" }, [
1709
1731
  el("button", { class: "ds-toggle-btn", type: "button", text: "閉じる" }),
1710
1732
  ]));
1711
- m.modal.querySelector(".modal-actions button").addEventListener("click", m.close);
1733
+ // **close をそのまま渡さない。** ハンドラに渡すと close(MouseEvent) になる。
1734
+ // いまは skipRefresh の判定が厳密比較なので効いているが、truthy 判定に
1735
+ // 緩めた瞬間「閉じる」ボタンだけが黙って取り込まなくなる
1736
+ m.modal.querySelector(".modal-actions button").addEventListener("click", function () { m.close(); });
1712
1737
  m.overlay.appendChild(m.modal);
1713
1738
  showModal(m.overlay, m.modal);
1714
1739
  }
@@ -1842,7 +1867,11 @@ ${capabilityState.toString()}
1842
1867
  col.appendChild(el("span", { class: "ds-inactive-note", text: "未使用(この案件では設定されていません)" }));
1843
1868
  }
1844
1869
  // id は接続マップのノードclickの飛び先(アンカー)
1845
- var card = el("div", { class: "ds-card" + (inactive ? " ds-inactive" : ""), id: "ds-" + (s.kind || "") }, [
1870
+ // **id は付けない。** #101 以前はこのカードが接続マップの飛び先だったが、
1871
+ // いまはタイルが飛び先。両方に付けると同じ id が2つある不正なHTMLになり、
1872
+ // モーダルの中身がタイルより前に来る変更が入った瞬間、getElementById が
1873
+ // 「開いていないモーダルの中」を指して黙って無反応になる
1874
+ var card = el("div", { class: "ds-card" + (inactive ? " ds-inactive" : "") }, [
1846
1875
  el("div", { class: "ds-card-main" }, [
1847
1876
  el("span", { class: "ds-icon", text: DS_KIND_ICON[s.kind] || "📁" }),
1848
1877
  el("span", { class: "ds-type", text: s.kind || "" }),
@@ -1873,15 +1902,10 @@ ${capabilityState.toString()}
1873
1902
  });
1874
1903
  var UNIT = { "slack": "チャンネル", "github-issues": "リポジトリ", "github-discussions": "リポジトリ" };
1875
1904
  // チャット未使用: Slackグループの位置(先頭)に「存在するが未設定」のグレー行を出す
1876
- if (chatOff) pushCap("チャット", inactiveSourceRow("💬", "チャット", "ds-チャット"));
1877
- var chatAnchored = chatOff;
1878
- var devAnchored = devOff;
1905
+ // **アンカーは渡さない**(タイルが飛び先。同 §id の重複を作らない)
1906
+ if (chatOff) pushCap("チャット", inactiveSourceRow("💬", "チャット", null));
1879
1907
  groups.forEach(function (g) {
1880
1908
  var meta = DS_TYPE[g.type] || { icon: "🔗", label: g.type || "その他" };
1881
- // 接続マップのノードclickの飛び先(アンカー)。チャット=Slack群の先頭・開発=GitHub群の先頭
1882
- var anchorId = null;
1883
- if (g.type === "slack" && !chatAnchored) { anchorId = "ds-チャット"; chatAnchored = true; }
1884
- if (g.type.indexOf("github") === 0 && !devAnchored) { anchorId = "ds-開発"; devAnchored = true; }
1885
1909
  var cards = el("div", { class: "ds-list" });
1886
1910
  g.items.forEach(function (s) { cards.appendChild(extSourceCard(s, meta)); });
1887
1911
  if (g.items.length >= 4) {
@@ -1906,10 +1930,8 @@ ${capabilityState.toString()}
1906
1930
  : "⚠️ " + bad + "件に問題" }),
1907
1931
  ]);
1908
1932
  var det = el("details", { class: "ds-group" }, [sum, cards]);
1909
- if (anchorId) det.id = anchorId;
1910
1933
  pushCap(capOf(g.type), det);
1911
1934
  } else {
1912
- if (anchorId && cards.firstChild) cards.firstChild.id = anchorId;
1913
1935
  // **appendChild と違い pushCap はノードを動かさない。** firstChild を条件にすると
1914
1936
  // 永久に真のままになる(実際に無限ループを作った)。先に配列へ取り出す。
1915
1937
  var kids = [];
@@ -1919,7 +1941,7 @@ ${capabilityState.toString()}
1919
1941
  });
1920
1942
  }
1921
1943
  // 開発未使用: グレー行も能力の受け皿へ
1922
- if (devOff) pushCap("開発", inactiveSourceRow("🐙", "GitHub", "ds-開発"));
1944
+ if (devOff) pushCap("開発", inactiveSourceRow("🐙", "GitHub", null));
1923
1945
 
1924
1946
  // **追加フォームはグループの外に置く。** グループは0件だと描かれず、4件以上だと
1925
1947
  // 畳まれる(既定は閉)。中に入れると**新規案件(0件)=一番追加したい場面**で消える。
@@ -1957,6 +1979,50 @@ ${capabilityState.toString()}
1957
1979
  var STATE_LABEL = { ok: "● 接続OK", idle: "⦿ 未接続", off: "未使用", warn: "⚠️" };
1958
1980
  var SECRET_OF_CAP = { "デザイン": "figma", "課題管理": "backlog", "開発": "external-sources" };
1959
1981
 
1982
+ // ---- セットアップの残り(checks)----
1983
+ //
1984
+ // **能力に紐づくものはその能力のタイルへ、紐づかないものは節にまとめる。**
1985
+ // 独立した一覧にすると、鍵カードの「現在: 未設定」やタイルの状態と
1986
+ // 同じことを別の言い方で二度言うことになり、ズレたときに読み手が
1987
+ // どちらを信じるか決められない(#101 で判定を1箇所に集めたのと同じ理由)。
1988
+ //
1989
+ // **missing 以外は出さない。** na は「使わないと宣言した能力」で、出すと
1990
+ // 「設定する必要が無いものに設定を促す」偽の警告になる(#101 で潰した穴)。
1991
+ // ビルド時とライブの両方で絞っているが、ここでも見る——経路が増えたときに
1992
+ // 絞り忘れると、いちばん見せてはいけないものが出る側に倒れるため。
1993
+ // **夜間 run の項目は、下の「自動化パイプライン」節が同じ run を出している。**
1994
+ // 両方に出すと、同じ失敗が1画面に2回並ぶ。実行状況はパイプライン節に任せ、
1995
+ // ここでは直し方だけをその行に添える(下の renderPipelines で使う)
1996
+ var PIPE_CHECK = { "update-gold": "nightly_decisionlog", "sync-backlog": "nightly_backlog", "sync-designs": "figma_sync" };
1997
+ var CHECK_CAP = {
1998
+ backlog_secrets: "課題管理", backlog_synced: "課題管理",
1999
+ meeting_minutes: "会議",
2000
+ channels_json: "チャット",
2001
+ figma_token: "デザイン", figma_inventory: "デザイン",
2002
+ submodules: "開発", dev_issues_derived: "開発",
2003
+ };
2004
+ var allChecks = (fleetVal("checks") || []).filter(function (c) {
2005
+ return c != null && c.status === "missing";
2006
+ });
2007
+ // **タイルに出せたものを覚えておく。** 振り分け先の能力タイルが立たない案件
2008
+ //(tools に宣言が無い・行も無い)では出す場所が無くなり、項目が**黙って消える**。
2009
+ // 消えた分は下の節へ落とす——振り分けを間違えても、消えるよりは出る方がよい
2010
+ var shownOnTile = {};
2011
+ function checksFor(cap) {
2012
+ return allChecks.filter(function (c) { return CHECK_CAP[c.id] === cap; });
2013
+ }
2014
+ /** 残りの1件を、直し方まで含めて1行にする */
2015
+ function checkLine(c) {
2016
+ var row = el("div", { class: "ds-check" });
2017
+ row.appendChild(el("span", { class: "ds-check-label", text: c.label }));
2018
+ if (c.action) row.appendChild(el("span", { class: "ds-check-action", text: c.action }));
2019
+ // **detail は内部値が入ることがある**("none" / "success")。読めるものだけ出す
2020
+ if (c.detail && !/^(none|success|failure|skipped|unknown)$/.test(c.detail)) {
2021
+ row.appendChild(el("span", { class: "ds-check-detail", text: c.detail }));
2022
+ }
2023
+ return row;
2024
+ }
2025
+
1960
2026
  /** タイルの2行目(状態と読み取り範囲)。**件数は出せる能力だけ出す** */
1961
2027
  function tileSub(cap, st) {
1962
2028
  var internal = internals.filter(function (s) { return s.kind === cap; })[0];
@@ -2066,6 +2132,18 @@ ${capabilityState.toString()}
2066
2132
  if (tools[cap] !== "none" && sk && (SITE.secretKinds || []).indexOf(sk) >= 0 && !((fleetVal("secretSources") || {})[sk])) {
2067
2133
  body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ トークンが未設定です" }));
2068
2134
  }
2135
+ // セットアップの残りのうち、この能力のもの。**鍵の注記と同じ位置**に出して
2136
+ // 「何が足りないか」と「何をすればよいか」を同じ場所で読めるようにする
2137
+ checksFor(cap).forEach(function (c) {
2138
+ // **直し方が無いものはタイルに出さないが、出したことにもしない。**
2139
+ // shownOnTile を先に立てると節からも外れ、画面のどこにも出なくなる
2140
+ //(実データで mitsubishi の nightly_backlog / figma_token、verdy の
2141
+ // nightly_backlog が消えていた)。タイルは状態を読む場所なので
2142
+ //「足りない」だけの行では埋めず、節の側で拾う
2143
+ if (!c.action) return;
2144
+ shownOnTile[c.id] = true;
2145
+ body.appendChild(el("div", { class: "ds-tile-note ds-tile-todo", text: "→ " + c.action }));
2146
+ });
2069
2147
  var tile = el("button", { class: "ds-tile", type: "button", id: "ds-" + cap }, [
2070
2148
  ic,
2071
2149
  body,
@@ -2124,11 +2202,46 @@ ${capabilityState.toString()}
2124
2202
  }
2125
2203
  children.push(el("span", { class: "ds-pipe-label", text: p.label || p.id || "" }));
2126
2204
  if (t) children.push(el("span", { class: "ds-pipe-time", text: "最終成功: " + t }));
2127
- plist.appendChild(el("div", { class: "ds-pipe" }, children));
2205
+ var row = el("div", { class: "ds-pipe" }, children);
2206
+ // **直し方はこの行に添える。** 同じ run の未達を「セットアップの残り」にも出すと、
2207
+ // 同じ失敗が1画面に2回並ぶ。実行状況はここが正本なので、直し方だけを持ってくる
2208
+ var pc = allChecks.filter(function (c) { return c.id === PIPE_CHECK[p.id]; })[0];
2209
+ if (pc && pc.action) {
2210
+ shownOnTile[pc.id] = true;
2211
+ row = el("div", { class: "ds-pipe-wrap" }, [row, el("div", { class: "ds-pipe-todo", text: "→ " + pc.action })]);
2212
+ }
2213
+ plist.appendChild(row);
2128
2214
  });
2129
2215
  sec.appendChild(plist);
2130
2216
  }
2131
2217
 
2218
+ // ---- セットアップの残り(どこにも出せなかったもの)----
2219
+ //
2220
+ // **パイプライン節より後に計算する。** 夜間 run の直し方はその行に添えるので、
2221
+ // 先に計算すると shownOnTile がまだ立っておらず、同じ文が2箇所に出る
2222
+ //
2223
+ // **8/8案件で未達の scaffold_drift が、ここで初めて誰かの目に入る。**
2224
+ // 能力のタイルに居場所が無い項目(基盤・Cortex・シークレット)は、
2225
+ // ここに出さないとどこにも出ない。
2226
+ var loose = allChecks.filter(function (c) { return !shownOnTile[c.id]; });
2227
+ if (loose.length) {
2228
+ sec.appendChild(el("div", { class: "ds-sub", text: "セットアップの残り" }));
2229
+ sec.appendChild(el("p", { class: "ds-note", text: "毎晩の自動チェックで見つかった、まだ済んでいない設定です。" }));
2230
+ var byCat = {};
2231
+ var catOrder = [];
2232
+ loose.forEach(function (c) {
2233
+ var k = c.category || "その他";
2234
+ if (!byCat[k]) { byCat[k] = []; catOrder.push(k); }
2235
+ byCat[k].push(c);
2236
+ });
2237
+ var todo = el("div", { class: "ds-checks" });
2238
+ catOrder.forEach(function (k) {
2239
+ todo.appendChild(el("div", { class: "ds-check-cat", text: k }));
2240
+ byCat[k].forEach(function (c) { todo.appendChild(checkLine(c)); });
2241
+ });
2242
+ sec.appendChild(todo);
2243
+ }
2244
+
2132
2245
  return sec;
2133
2246
  }
2134
2247
  function renderDataSources() {
@@ -3417,7 +3530,7 @@ ${capabilityState.toString()}
3417
3530
  nameInput.addEventListener("input", update);
3418
3531
 
3419
3532
  // 閉じる経路(Escape・背景クリック)は makeModal 側に用意されている
3420
- cancelBtn.addEventListener("click", mk.close);
3533
+ cancelBtn.addEventListener("click", function () { mk.close(); });
3421
3534
  submitBtn.addEventListener("click", function () { submitIntake(kind, selectedFiles, nameInput.value, status, submitBtn); });
3422
3535
  }
3423
3536
  async function submitIntake(kind, files, submitter, status, submitBtn) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.4.61",
3
+ "version": "0.4.63",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,