@takagaki/cortex-decisions-viewer 0.12.120 → 0.12.122

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
@@ -74,6 +74,7 @@ const repo_1 = require("./repo");
74
74
  const engagement_1 = require("./engagement");
75
75
  const story_1 = require("./story");
76
76
  const ranking_1 = require("./ranking");
77
+ const hands_1 = require("./hands");
77
78
  const rederivation_1 = require("./rederivation");
78
79
  // 直近のGold昇格で追加されたレコードのパス集合。レコード生成の各所から参照するのでモジュールスコープに置く
79
80
  // (引数で引き回すと呼び出し側の改修が広範囲になる)。build() の冒頭で1回だけ解決する。
@@ -2425,6 +2426,8 @@ async function build(opts) {
2425
2426
  const engagement = await (0, engagement_1.readEngagementLedger)(repoRoot, ledgerDir);
2426
2427
  // 目次と同じ順位(engine #551)。**読めないなら `null`** で、つながり画面の点は今までどおり一律
2427
2428
  const memoryRanking = await (0, ranking_1.readMemoryRanking)(repoRoot, ledgerDir);
2429
+ // 人の手の表の写し(engine #1094 q-10)。あれば記憶 v2 の案件で、確認の印とボタンを出さない。**無い案件は今と 1 バイトも同じ**(キーごと無い)
2430
+ const memoryV2 = await (0, hands_1.readMemoryV2)(repoRoot, ledgerDir);
2428
2431
  // 記録の id → リポジトリルート相対のパス(順位のファイルの鍵と突き合わせる。`repoRoot` 起点・cwd 起点にしない)
2429
2432
  const recordRelPaths = new Map();
2430
2433
  const relOf = (dir, fileName) => {
@@ -2577,6 +2580,7 @@ async function build(opts) {
2577
2580
  home,
2578
2581
  homeGrounding: homeGrounding,
2579
2582
  homeUpdatedAt: homeUpdatedAt,
2583
+ ...(memoryV2 ? { memoryV2: true } : {}),
2580
2584
  sections,
2581
2585
  graph,
2582
2586
  categories,
package/dist/hands.js ADDED
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.MEMORY_HANDS_VERSION = exports.MEMORY_HANDS_FILE = void 0;
7
+ exports.isMemoryHandsCopy = isMemoryHandsCopy;
8
+ exports.readMemoryV2 = readMemoryV2;
9
+ /**
10
+ * 人の手の表の写し(`.cortex/memory-hands.json`)を読む(cortex-engine #1094 q-10・仕様 1-6)。
11
+ *
12
+ * 仕様 1-6: **表があること自体が、その案件の記憶 v2 の印**。engine が栓 `human_touch_lock` が on の晩に、表が読めた晩だけ
13
+ * 目次と同じコミットで置く(engine `scripts/memory-hands-copy.mjs` の `HANDS_COPY_PATH`・`handsCopyText`)。ここは**印があるかを読むだけ**で、
14
+ * 記録ごとの欄(lock・touched …)はまだ画面の判定に使わない(残りは段 5・仕様の差 77)。
15
+ *
16
+ * v2 の案件では、画面から「AI生成・未確認」の札・警告帯・[確認済みにする]・「未確認のみ」を出さない(削除は残す)。
17
+ * 読めない・無い・版が違う案件は v2 でないものとして今のまま(`SiteData.memoryV2` はキーごと無い)。
18
+ */
19
+ const promises_1 = __importDefault(require("node:fs/promises"));
20
+ const node_path_1 = __importDefault(require("node:path"));
21
+ const ledger_dir_1 = require("./ledger-dir");
22
+ /** 置き場(engine `scripts/memory-hands-copy.mjs` の `HANDS_COPY_PATH` の `.cortex/` の下) */
23
+ exports.MEMORY_HANDS_FILE = "memory-hands.json";
24
+ /** 形の版(engine の `HANDS_COPY_VERSION`。違ったら v2 の印と読まない) */
25
+ exports.MEMORY_HANDS_VERSION = 1;
26
+ /** 中身が人の手の表の写しの形か(投げない)。版が違う・形が違う・壊れている → `false` */
27
+ function isMemoryHandsCopy(text) {
28
+ try {
29
+ const parsed = JSON.parse(text);
30
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
31
+ return false;
32
+ const o = parsed;
33
+ return o.version === exports.MEMORY_HANDS_VERSION && !!o.records && typeof o.records === "object" && !Array.isArray(o.records);
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ /** 案件リポに写しがあり、読める形か。無い → `false`(黙る)・壊れている → `false`(build のログに 1 行) */
40
+ async function readMemoryV2(repoRoot, ledgerDir) {
41
+ let text;
42
+ try {
43
+ text = await promises_1.default.readFile(node_path_1.default.join((0, ledger_dir_1.ledgerDirOf)(repoRoot, ledgerDir), exports.MEMORY_HANDS_FILE), "utf8");
44
+ }
45
+ catch {
46
+ return false;
47
+ }
48
+ const ok = isMemoryHandsCopy(text);
49
+ if (!ok)
50
+ console.warn(`${exports.MEMORY_HANDS_FILE}: 読めないので、この案件は記憶 v2 でないものとして出します`);
51
+ return ok;
52
+ }
package/dist/render.js CHANGED
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONNECTOR_ROW_ORDER = exports.BACKLOG_SPACE_MAX_AGE_MS = exports.CONNECTOR_KINDS = exports.BACKLOG_MCP_HOST = exports.CLAUDE_CONNECTORS_URL = exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.INGEST_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = exports.PERMISSION_WIRING = void 0;
3
+ exports.CONNECTOR_ROW_ORDER = exports.BACKLOG_SPACE_MAX_AGE_MS = exports.CONNECTOR_KINDS = exports.BACKLOG_MCP_HOST = exports.CLAUDE_CONNECTORS_URL = exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.INGEST_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = exports.PERMISSION_WIRING = exports.LEGACY_VIEWER_NOTICE_PARAM = void 0;
4
4
  exports.renderSite = renderSite;
5
5
  exports.renderSiteWithAssets = renderSiteWithAssets;
6
6
  exports.viewerAssetSources = viewerAssetSources;
7
7
  exports.viewerAssetNames = viewerAssetNames;
8
8
  exports.permissionToolId = permissionToolId;
9
+ exports.withoutLegacyNoticeParam = withoutLegacyNoticeParam;
10
+ exports.legacyAddressNoticeOf = legacyAddressNoticeOf;
9
11
  exports.isRepositorySlug = isRepositorySlug;
10
12
  exports.devRepositoryRefs = devRepositoryRefs;
11
13
  exports.claudeCodeRepositories = claudeCodeRepositories;
@@ -272,6 +274,62 @@ const PERMISSION_TOOL_ID = {
272
274
  function permissionToolId(adapter) {
273
275
  return Object.prototype.hasOwnProperty.call(PERMISSION_TOOL_ID, adapter) ? PERMISSION_TOOL_ID[adapter] : adapter;
274
276
  }
277
+ /**
278
+ * 古いアドレスの案内の印(cortex-engine #1075 R6-0f)。旧ビューア(Amplify)の 301 が、閉じる日を
279
+ * `?cortex_legacy_closes=YYYY-MM-DD` で付けて新しいアドレスへ送ってくる。
280
+ * 🔴 **cortex-tools の infra(`scripts/lib/legacy-viewers.cjs` の `LEGACY_VIEWER_NOTICE_PARAM`)と同じ綴り。**
281
+ * 片方だけ変えると案内が黙って消える(infra の `test/legacy-viewers.test.mjs` が字面を突き合わせる)
282
+ */
283
+ exports.LEGACY_VIEWER_NOTICE_PARAM = "cortex_legacy_closes";
284
+ /**
285
+ * アドレスのクエリから印だけを消したクエリ(R6-0f)。帯を出さない印(壊れた形・過ぎた日)もアドレスに残さない。
286
+ * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
287
+ */
288
+ function withoutLegacyNoticeParam(search, param) {
289
+ var params = new URLSearchParams(search);
290
+ if (!params.has(param))
291
+ return null;
292
+ params.delete(param);
293
+ var rest = params.toString();
294
+ return rest === "" ? "" : "?" + rest;
295
+ }
296
+ /**
297
+ * アドレスのクエリから古いアドレスの案内を読む(R6-0f)。印が無い・日付の形でない・暦に無い日なら null。
298
+ * 返すのは画面に出す日付(`11月10日`)と、**印だけを消したクエリ**(他のクエリ `?id=…` は残す)。
299
+ * `today`(`YYYY-MM-DD`・閲覧者の手元の日付)より前の日・62 日より先の日なら null。
300
+ *
301
+ * 🔴 **印の値をそのまま画面に書かない。** 誰でも作れるリンクなので、日付の形を通ったときだけ、
302
+ * 数字から組み直した文字を出す。
303
+ * **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
304
+ */
305
+ function legacyAddressNoticeOf(search, param, today) {
306
+ var params = new URLSearchParams(search);
307
+ var values = params.getAll(param);
308
+ if (values.length !== 1)
309
+ return null;
310
+ var m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(values[0]);
311
+ if (!m)
312
+ return null;
313
+ var y = Number(m[1]);
314
+ var mo = Number(m[2]);
315
+ var d = Number(m[3]);
316
+ var date = new Date(Date.UTC(y, mo - 1, d));
317
+ if (date.getUTCFullYear() !== y || date.getUTCMonth() !== mo - 1 || date.getUTCDate() !== d)
318
+ return null;
319
+ // 過ぎた日は出さない(誰でも作れるリンクで「使えなくなります」の嘘を出さない・閉じた後に残った印も黙らせる)
320
+ if (values[0] < today)
321
+ return null;
322
+ // 遠すぎる日も出さない(案内は閉じる 1 か月前から。62 日より先は作られたリンクとみなす)
323
+ var t = /^(\d{4})-(\d{2})-(\d{2})$/.exec(today);
324
+ if (!t)
325
+ return null;
326
+ var limit = Date.UTC(Number(t[1]), Number(t[2]) - 1, Number(t[3])) + 62 * 86400000;
327
+ if (date.getTime() > limit)
328
+ return null;
329
+ params.delete(param);
330
+ var rest = params.toString();
331
+ return { label: mo + "月" + d + "日", search: rest === "" ? "" : "?" + rest };
332
+ }
275
333
  /** 能力→アダプタの表を**テストから読むための入れ物**(CLAUDE_CODE_LINK と同じ作法・#841) */
276
334
  exports.PERMISSION_WIRING = { adapterCatalog: ADAPTER_CATALOG };
277
335
  /**
@@ -5146,6 +5204,9 @@ a.pager-btn:hover { text-decoration: none; }
5146
5204
  padding: 10px 12px; font-size: 13.5px; line-height: 1.8; color: #92400e;
5147
5205
  }
5148
5206
  .draft-note > .stale-note { flex: 1 0 100%; margin-bottom: 0; }
5207
+ /* 古いアドレスから来た人への案内(R6-0f)。#app の前に置くので、上の余白だけ足す */
5208
+ .legacy-address-wrap { padding-bottom: 0; }
5209
+ .legacy-address-note { margin: 16px 0 0; }
5149
5210
  .stale-reload-btn {
5150
5211
  flex: 0 0 auto; cursor: pointer; white-space: nowrap;
5151
5212
  padding: 6px 14px; border-radius: 999px;
@@ -5426,6 +5487,7 @@ a.ds-name:hover { text-decoration: underline; }
5426
5487
  border: 1px solid #f0b429; background: #fffdf5; border-radius: 12px;
5427
5488
  padding: 12px 16px; display: flex; flex-direction: column; gap: 8px;
5428
5489
  }
5490
+ .ds-orgnote.is-resolved { border-color: transparent; background: transparent; padding: 0; }
5429
5491
  .ds-orgnote-title { font-weight: 700; font-size: 13.5px; color: #8a5a00; }
5430
5492
  /* 案内が指す行。**囲うことで「どのチャンネルの話か」を示す**(開閉はしない) */
5431
5493
  .ds-orgnote-rows { display: flex; flex-direction: column; gap: 8px; margin-top: 2px; }
@@ -6196,6 +6258,30 @@ var ADAPTER_CATALOG = ${JSON.stringify(ADAPTER_CATALOG)};
6196
6258
  // 小なり記号だけ逃がす(埋め込み形は script 要素の中なので、閉じタグの綴りを作らせない)
6197
6259
  var PERMISSION_TOOL_ID = ${JSON.stringify(PERMISSION_TOOL_ID)};
6198
6260
  ${permissionToolId.toString()}
6261
+ ${legacyAddressNoticeOf.toString()}
6262
+ ${withoutLegacyNoticeParam.toString()}
6263
+ var LEGACY_VIEWER_NOTICE_PARAM = ${JSON.stringify(exports.LEGACY_VIEWER_NOTICE_PARAM)};
6264
+ // 古いアドレスの案内の印(R6-0f)は**起動の最初に**読んで、アドレスから消す。タブや詳細の書き戻し
6265
+ // (replaceState)が先に走ると印が落ちて帯が出ない。帯を描くのは下の showLegacyAddressNotice
6266
+ var LEGACY_ADDRESS_NOTICE = (function () {
6267
+ var now = new Date();
6268
+ var pad = function (n) { return (n < 10 ? "0" : "") + n; };
6269
+ var today = now.getFullYear() + "-" + pad(now.getMonth() + 1) + "-" + pad(now.getDate());
6270
+ var search = location.search;
6271
+ var cleanSearch = withoutLegacyNoticeParam(search, LEGACY_VIEWER_NOTICE_PARAM);
6272
+ if (cleanSearch === null) return null;
6273
+ // 印は帯を出すかに関わらず消す(壊れた形・過ぎた日の印もブックマークに残さない)。
6274
+ // パスは先頭のスラッシュを 1 つにそろえる(2 つで始まると別のホストを指すリンクになる)
6275
+ var cleanUrl = "/" + location.pathname.replace(/^\\/+/, "") + cleanSearch + location.hash;
6276
+ try {
6277
+ history.replaceState(history.state, "", cleanUrl);
6278
+ } catch (e) {
6279
+ // file:// など replaceState が使えない環境では印が残るだけ
6280
+ }
6281
+ var notice = legacyAddressNoticeOf(search, LEGACY_VIEWER_NOTICE_PARAM, today);
6282
+ if (!notice) return null;
6283
+ return { label: notice.label, cleanUrl: cleanUrl };
6284
+ })();
6199
6285
  var PERMISSIONS_DATA = ${JSON.stringify(permissions_data_1.PERMISSIONS_DATA).replace(/</g, "\\u003c")};
6200
6286
  var CAP_STATE_TEXT = ${JSON.stringify(CAP_STATE_TEXT)};
6201
6287
  var CAP_STATE_MARK = ${JSON.stringify(CAP_STATE_MARK)};
@@ -6650,7 +6736,7 @@ ${calendarAddUrl.toString()}
6650
6736
  }
6651
6737
  // 未確認(status: draft = AI生成・人間未確認)の印。Gold層の4型(決定事項・用語集・メンバー・ルール)共通で、active・status無しには何も出さない
6652
6738
  function draftBadge(rec) {
6653
- return rec && machineOwned(confirmPathOf(rec), rec.rstatus) ? el("span", { class: "badge draft", text: "AI生成・未確認" }) : null;
6739
+ return rec && unconfirmed(rec) ? el("span", { class: "badge draft", text: "AI生成・未確認" }) : null;
6654
6740
  }
6655
6741
  // 直近のGold昇格で作られたレコードの印。レコードの date は決定日なので、日付順に並べても
6656
6742
  // 「今回の実行で何が増えたか」は分からない。ビルド時に git から判定した結果(isLatest)を出す
@@ -6667,7 +6753,7 @@ ${calendarAddUrl.toString()}
6667
6753
  // 同じ場面の同じ判断なので、口を離さない。**この帯は draft にしか出ない**ので、
6668
6754
  // draft でない記録の削除の口は操作列の末尾(attachDeleteToEditRow)に置く。
6669
6755
  function draftNote(rec) {
6670
- if (!rec || !machineOwned(confirmPathOf(rec), rec.rstatus)) return null;
6756
+ if (!rec || !unconfirmed(rec)) return null;
6671
6757
  var path = promotableRecordPath(rec);
6672
6758
  var note = el("div", { class: "draft-note" }, [
6673
6759
  el("span", { text: "⚠️" }),
@@ -6832,7 +6918,7 @@ ${calendarAddUrl.toString()}
6832
6918
  if (listState.status === RETIRED_FILTER) return rec && rec._kind === "decision" ? isRetired(rec) : true;
6833
6919
  if (listState.status === RETRACTED_FILTER) return rec && rec._kind === "generic" && rec._section === "Rules" ? isRetracted(rec) : true;
6834
6920
  // 「未確認のみ」は口に聞く(札と同じ答え)。それ以外の値は従来どおりの素の比較
6835
- if (listState.status === "draft") return machineOwned(confirmPathOf(rec), rec.rstatus);
6921
+ if (listState.status === "draft") return unconfirmed(rec);
6836
6922
  return rec.rstatus === listState.status;
6837
6923
  }
6838
6924
  /* -------------------------------------------------------------------------
@@ -7090,7 +7176,7 @@ ${calendarAddUrl.toString()}
7090
7176
 
7091
7177
  // 未確認が1件も無い一覧では絞り込みを出さない
7092
7178
  function hasUnverified(items) {
7093
- return (items || []).some(function (r) { return machineOwned(confirmPathOf(r), r.rstatus); });
7179
+ return (items || []).some(function (r) { return unconfirmed(r); });
7094
7180
  }
7095
7181
 
7096
7182
  // ---------- AI導線(Gold層への人間の書き込み口。claude.ai/code へのディープリンク) ----------
@@ -7200,6 +7286,14 @@ ${calendarAddUrl.toString()}
7200
7286
  if (rec && rec._kind === "generic" && rec._section) return "Cortex/" + rec._section + "/";
7201
7287
  return null;
7202
7288
  }
7289
+ /**
7290
+ * 「AI生成・未確認」(機械の下書き)か。札・警告帯・[確認済みにする]・「未確認のみ」・削除の置き場は**全部これに聞く**。
7291
+ * 🔴 **記憶 v2 の案件(人の手の表の写しがある=SITE.memoryV2・cortex-engine #1094 q-10)では常に偽**。
7292
+ * 画面で人が押すのは記録の削除と名寄せの確定だけで、確認の印は出さない(削除は帯の外の操作列に出る)
7293
+ */
7294
+ function unconfirmed(rec) {
7295
+ return !SITE.memoryV2 && !!rec && machineOwned(confirmPathOf(rec), rec.rstatus);
7296
+ }
7203
7297
  /**
7204
7298
  * 更新提案を AI に適用させるプロンプトの締め(status の扱い)。**3 つに分ける**(cortex-engine #1094 段 4):
7205
7299
  * - 下書き(draft)…… 人が確かめて直したので active に上げさせる
@@ -7208,7 +7302,9 @@ ${calendarAddUrl.toString()}
7208
7302
  * - それ以外(人の値)…… 据え置き(draft に戻させない)
7209
7303
  */
7210
7304
  function statusKeepLine(rec) {
7211
- if (machineOwned(confirmPathOf(rec), rec.rstatus)) {
7305
+ // 記憶 v2 の案件: status は確認の印ではない。どの値でも触らせない
7306
+ if (SITE.memoryV2) return "この記録の status の欄は変えないでください(足さない・書き換えない)。";
7307
+ if (unconfirmed(rec)) {
7212
7308
  return "あなた(人間)が内容を確認して編集したので、status が draft なら active に上げてください。";
7213
7309
  }
7214
7310
  if (!rec.rstatus) return "この記録には status の欄がありません。status の欄は足さないでください。";
@@ -8515,7 +8611,7 @@ ${calendarAddUrl.toString()}
8515
8611
  if (d.quiet) {
8516
8612
  list.appendChild(el("div", { class: "story-quiet" }, [
8517
8613
  el("span", { class: "story-quiet-date", text: d.heading || d.date }),
8518
- el("span", { text: " は動きはありませんでした" }),
8614
+ el("span", { text: d.pending ? " はまだ書けていません" : " は動きはありませんでした" }),
8519
8615
  ]));
8520
8616
  return;
8521
8617
  }
@@ -10882,10 +10978,25 @@ ${calendarAddUrl.toString()}
10882
10978
  var cards = g.items.map(function (x) {
10883
10979
  var again = !!(x.ref && seenRef[x.ref]);
10884
10980
  if (x.ref) seenRef[x.ref] = 1;
10885
- return extSourceCard(x, DS_TYPE[x.type] || { icon: "🔗", label: x.type || "その他" }, { quietHelp: again });
10981
+ var rowCard = extSourceCard(x, DS_TYPE[x.type] || { icon: "🔗", label: x.type || "その他" }, { quietHelp: again });
10982
+ // 同じリポの issues / discussions を、接続確認の結果で揃えるための目印(updateGithubOrgBand)
10983
+ if (rowCard && rowCard.setAttribute && x.ref) rowCard.setAttribute("data-ref", x.ref);
10984
+ return rowCard;
10886
10985
  });
10887
10986
  if (g.state === "warn") {
10888
- out.push(orgNoteBox(githubNotConnectedNodes(g), cards));
10987
+ var warnBox = orgNoteBox(githubNotConnectedNodes(g), cards);
10988
+ // 「接続を確認」が通ったとき帯をその場で畳めるよう、いま読めていないリポの一覧を持たせる
10989
+ //(probeSection → updateGithubOrgBand)。同じリポの issues / discussions は 1 本と数える
10990
+ var unreadRefs = [];
10991
+ g.items.forEach(function (x) {
10992
+ if (x.ref && x.gate && x.gate !== "ok" && unreadRefs.indexOf(x.ref) < 0) unreadRefs.push(x.ref);
10993
+ });
10994
+ warnBox.setAttribute("data-unread-refs", JSON.stringify(unreadRefs));
10995
+ // 見出しの件数は夜間の値のことがある(画面の行数と違う)。初期値を持ち、増減だけ当てる
10996
+ var titleEl = warnBox.querySelector(":scope > .ds-orgnote-title");
10997
+ var m0 = titleEl ? /^⚠️ (\\d+)件のリポジトリを読み取れていません/.exec(titleEl.textContent || "") : null;
10998
+ if (m0) warnBox.setAttribute("data-unread-count", m0[1]);
10999
+ out.push(warnBox);
10889
11000
  return;
10890
11001
  }
10891
11002
  if (g.state === "unsure") {
@@ -10901,6 +11012,64 @@ ${calendarAddUrl.toString()}
10901
11012
  return out;
10902
11013
  }
10903
11014
 
11015
+ /**
11016
+ * 「接続を確認」の結果を、行が属する組織の帯へ反映する。
11017
+ *
11018
+ * 読めた行を帯の「読めていないリポ」から外し、**最後の 1 本だったら帯を畳む**。
11019
+ * まだ読めない行が残るなら帯は残し、見出しの件数だけ減らす。読めなかった結果は
11020
+ * 帯に戻す(読めていた行が後から読めなくなった場合も、帯が嘘をつかない)。
11021
+ * 帯が無い行(組織が繋がっている)では何もしない。
11022
+ */
11023
+ function updateGithubOrgBand(card, ref, ok) {
11024
+ var box = card.closest ? card.closest(".ds-orgnote") : null;
11025
+ if (!box || !ref) return;
11026
+ var raw = box.getAttribute("data-unread-refs");
11027
+ if (raw == null) return;
11028
+ var refs;
11029
+ try { refs = JSON.parse(raw) || []; } catch (e) { return; }
11030
+ var at = refs.indexOf(ref);
11031
+ if (ok && at >= 0) refs.splice(at, 1);
11032
+ else if (!ok && at < 0) refs.push(ref);
11033
+ else return;
11034
+ box.setAttribute("data-unread-refs", JSON.stringify(refs));
11035
+ // 同じリポの兄弟の行(issues / discussions)も、押した行と同じ判定に揃える
11036
+ var gateSrc = card.querySelector(".ds-gate");
11037
+ var sibs = box.querySelectorAll(".ds-card[data-ref]");
11038
+ for (var si = 0; gateSrc && si < sibs.length; si++) {
11039
+ var sib = sibs[si];
11040
+ if (sib === card || sib.getAttribute("data-ref") !== ref) continue;
11041
+ var sg = sib.querySelector(".ds-gate");
11042
+ if (sg) { sg.className = gateSrc.className; sg.textContent = gateSrc.textContent; }
11043
+ var sh = sib.querySelectorAll(".ds-gate-help");
11044
+ for (var hi = 0; hi < sh.length; hi++) sh[hi].hidden = ok;
11045
+ }
11046
+ var title = box.querySelector(":scope > .ds-orgnote-title");
11047
+ var cnt = parseInt(box.getAttribute("data-unread-count"), 10);
11048
+ if (!isNaN(cnt)) {
11049
+ cnt = Math.max(0, cnt + (ok ? -1 : 1));
11050
+ box.setAttribute("data-unread-count", String(cnt));
11051
+ }
11052
+ var hasBand = !!title;
11053
+ if (!hasBand) return;
11054
+ if (refs.length === 0) {
11055
+ // 帯だけを隠す。行(.ds-orgnote-rows)は残す
11056
+ for (var i = 0; i < box.children.length; i++) {
11057
+ var c = box.children[i];
11058
+ if (c.classList.contains("ds-orgnote-rows")) continue;
11059
+ c.hidden = true;
11060
+ }
11061
+ box.classList.add("is-resolved");
11062
+ return;
11063
+ }
11064
+ for (var k = 0; k < box.children.length; k++) {
11065
+ var ch = box.children[k];
11066
+ if (!ch.classList.contains("ds-orgnote-rows")) ch.hidden = false;
11067
+ }
11068
+ box.classList.remove("is-resolved");
11069
+ // 見出しが件数を持つ形(「N件のリポジトリを読み取れていません」)だけ数え直す
11070
+ if (!isNaN(cnt)) title.textContent = "⚠️ " + Math.max(1, cnt) + "件のリポジトリを読み取れていません";
11071
+ }
11072
+
10904
11073
  /** 確かめられなかった組織の案内。**導線は出さない** */
10905
11074
  function githubUnsureNodes(g) {
10906
11075
  var o = g.meta || {};
@@ -14141,6 +14310,9 @@ ${calendarAddUrl.toString()}
14141
14310
  // 再読み込みまで)で、「接続を確認 → 閉じる → その場で接続OK」にならない。
14142
14311
  // 据え置きあり=GitHub は叩かない(ストアの行は据え置きの対象外で毎回引く)。
14143
14312
  // 取り込む能力の即答(appendDispatchProbe)は先に同じ 1 行を持っている
14313
+ // 組織の帯も同じ結果で描き直す(読めた最後の 1 本なら帯が消える)。
14314
+ // 帯はページ描画時に 1 度だけ組むので、ここで触らないと再読み込みまで残る
14315
+ if (probeTypeOf(s.type) === "github") updateGithubOrgBand(card, s.ref, j.gate === "ok");
14144
14316
  reloadLiveState();
14145
14317
  } catch (e) {
14146
14318
  msg.className = "ds-toggle-msg is-error";
@@ -17715,7 +17887,8 @@ ${calendarAddUrl.toString()}
17715
17887
  ]));
17716
17888
  });
17717
17889
  sec.appendChild(what);
17718
- sec.appendChild(el("p", { class: "about-note", text:
17890
+ // 記憶 v2 の案件には確認の印もボタンも無いので、この説明は出さない(出すと「どこにも無い印」を探させる)
17891
+ if (!SITE.memoryV2) sec.appendChild(el("p", { class: "about-note", text:
17719
17892
  "自動で追加されたものには「AI生成・未確認」と表示されます。内容を確認して、そのままでよければ「確認済みにする」を押してください。直したいところがあれば「AIで編集」から修正できます。どちらの場合も、その印は外れます。" }));
17720
17893
 
17721
17894
  sec.appendChild(el("blockquote", { class: "about-quote", text:
@@ -19810,7 +19983,7 @@ ${calendarAddUrl.toString()}
19810
19983
  * 見た目は控えめな文字のボタンのままにして、隣の実体のあるボタンと重さを分ける。
19811
19984
  */
19812
19985
  function attachDeleteToEditRow(rec, editRow) {
19813
- if (!rec || machineOwned(confirmPathOf(rec), rec.rstatus)) return null;
19986
+ if (!rec || unconfirmed(rec)) return null;
19814
19987
  var del = buildDeleteControls(rec);
19815
19988
  if (!del) return null;
19816
19989
  editRow.appendChild(del.btn);
@@ -21558,6 +21731,28 @@ ${calendarAddUrl.toString()}
21558
21731
  }
21559
21732
  })();
21560
21733
 
21734
+ /**
21735
+ * 古いアドレスから来た人への案内(cortex-engine #1075 R6-0f)。旧ビューアの 301 が閉じる日の印を付けて
21736
+ * ここへ送ってくる。**帯は症状・行は一手**(帯は 1 本・押す口はこのページのアドレス 1 つ)。
21737
+ *
21738
+ * 🔴 **印はアドレスから先に消す**(起動の最初の LEGACY_ADDRESS_NOTICE)。残すと、案内を読んだ人がブックマークし直したアドレスにも印が残り、
21739
+ * 閉じた後も帯が出続ける。消すのは印だけ(他のクエリとハッシュは持ち越す)。
21740
+ * 帯は #app の外(前)に置く——#app は画面を描き直すたびに中身を入れ替えるので、中に置くと最初の描画で消える。
21741
+ */
21742
+ (function showLegacyAddressNotice() {
21743
+ var notice = LEGACY_ADDRESS_NOTICE;
21744
+ if (!notice) return;
21745
+ var cleanUrl = notice.cleanUrl;
21746
+ var mainEl = document.getElementById("app");
21747
+ if (!mainEl || !mainEl.parentNode) return;
21748
+ var box = el("div", { class: "stale-note legacy-address-note", role: "status" }, [
21749
+ el("span", { text: "古いアドレスから開きました。古いアドレスは " + notice.label + " で使えなくなります。" }),
21750
+ el("a", { href: cleanUrl, text: "このページのアドレスをブックマークし直してください" }),
21751
+ ]);
21752
+ var wrap = el("div", { class: "container legacy-address-wrap" }, [box]);
21753
+ mainEl.parentNode.insertBefore(wrap, mainEl);
21754
+ })();
21755
+
21561
21756
  // ブラウザの「戻る」「進む」。go() を通らないので、ここで一覧⇄詳細のスクロールだけ別に見る
21562
21757
  // (#1 ブラウザの戻るでも一覧のスクロール位置を保つ)
21563
21758
  window.addEventListener("popstate", function (ev) {
package/dist/story.js CHANGED
@@ -233,16 +233,19 @@ const mdOf = (day) => `${Number(day.slice(5, 7))}/${Number(day.slice(8, 10))}`;
233
233
  * 動きのない日の行(純関数)。`from`〜`to`(新しい方が `to`)を 1 行にする。1 日なら `2026-09-18(金)`、
234
234
  * 続くなら `9/10〜9/18`。文言(「は動きはありませんでした」)は画面が付ける
235
235
  */
236
- function quietStoryRow(from, to) {
236
+ function quietStoryRow(from, to, pending = false) {
237
237
  const heading = from === to ? `${to}(${WEEKDAYS[new Date(utcMs(to)).getUTCDay()]})` : `${mdOf(from)}〜${mdOf(to)}`;
238
- return { date: to, ...(from === to ? {} : { fromDate: from }), heading: heading, bodyHtml: "", quiet: true };
238
+ return { date: to, ...(from === to ? {} : { fromDate: from }), heading: heading, bodyHtml: "", quiet: true, ...(pending ? { pending: true } : {}) };
239
239
  }
240
240
  /**
241
241
  * ページのある日(`pages`)の間と、一番古いページから `today` までの日付の抜けを、
242
242
  * 動きのない日の行で埋めて新しい順に返す。連続する `STORY_COLLAPSE_QUIET_DAYS` 日以上は 1 行に畳む。
243
243
  * 🔴 ページより新しい日(今日まで)も埋める。`pages` が空なら空(タブを出さない)
244
+ *
245
+ * `lastDay`=日の書き手が処理し終えた最後の日(帳簿の最後の日)。これより後の空の日は「動きがなかった」のではなく
246
+ * 「まだ書けていない」日なので `pending` の行にする(動きのない日の行とは畳まない)。分からない(`null`)ときは全部今までどおり
244
247
  */
245
- function withQuietDays(pages, today) {
248
+ function withQuietDays(pages, today, lastDay = null) {
246
249
  if (!pages.length)
247
250
  return [];
248
251
  const have = new Map(pages.map((p) => [p.date, p]));
@@ -251,15 +254,17 @@ function withQuietDays(pages, today) {
251
254
  const newest = dates.reduce((a, d) => (d > a ? d : a));
252
255
  const last = today > newest ? today : newest;
253
256
  const out = [];
254
- let run = []; // 新しい日から並ぶ動きのない日
257
+ let run = []; // 新しい日から並ぶ動きのない日(まだ書けていない日とは混ぜない)
258
+ const pendingOf = (d) => lastDay !== null && d > lastDay;
255
259
  const flush = () => {
256
260
  if (!run.length)
257
261
  return;
262
+ const pending = pendingOf(run[0]);
258
263
  if (run.length >= exports.STORY_COLLAPSE_QUIET_DAYS)
259
- out.push(quietStoryRow(run[run.length - 1], run[0]));
264
+ out.push(quietStoryRow(run[run.length - 1], run[0], pending));
260
265
  else
261
266
  for (const d of run)
262
- out.push(quietStoryRow(d, d));
267
+ out.push(quietStoryRow(d, d, pending));
263
268
  run = [];
264
269
  };
265
270
  for (let ms = utcMs(last); ms >= utcMs(oldest); ms -= 86400000) {
@@ -269,8 +274,11 @@ function withQuietDays(pages, today) {
269
274
  flush();
270
275
  out.push(page);
271
276
  }
272
- else
277
+ else {
278
+ if (run.length && pendingOf(run[0]) !== pendingOf(d))
279
+ flush();
273
280
  run.push(d);
281
+ }
274
282
  }
275
283
  flush();
276
284
  return out;
@@ -376,7 +384,9 @@ ledgerDir) {
376
384
  out.push({ date: f.day, heading: heading, bodyHtml: await renderMarkdown(markdown), ...(editUrl ? { editUrl: editUrl } : {}) });
377
385
  }
378
386
  // 一番古い「動きのある日」から今日まで、日付の抜けを動きのない日の行で埋める。動きのある日が 1 つも無ければ出さない
379
- return out.length ? withQuietDays(out, today) : null;
387
+ // 最後の日=帳簿で on の最後の日(日のファイルの日付ではない。末尾に静かな日が続いても、帳簿には処理した印が残る)
388
+ const lastDay = [...on].reduce((a, d) => (a === null || d > a ? d : a), null);
389
+ return out.length ? withQuietDays(out, today, lastDay) : null;
380
390
  }
381
391
  // ---------------------------------------------------------------------------
382
392
  // 週・月のあらすじ(#901・engine の週・月の書き手が書く `Cortex/Story/Weeks`・`Cortex/Story/Months`)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takagaki/cortex-decisions-viewer",
3
- "version": "0.12.120",
3
+ "version": "0.12.122",
4
4
  "description": "Cortexの意思決定記録(Decisions/*.md)を静的サイトにビルドして閲覧する。各レコードに「Edit on GitHub」リンクを付与する。",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,