@takagaki/cortex-decisions-viewer 0.12.119 → 0.12.120

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/render.js CHANGED
@@ -5057,35 +5057,23 @@ a.pager-btn:hover { text-decoration: none; }
5057
5057
  .home-tools-list:has(+ .home-tools-fold) > .home-tools-item:last-child,
5058
5058
  .home-tools-fold:has(+ .home-tools-list) > .home-tools-list > .home-tools-item:last-child,
5059
5059
  .home-tools-fold:has(+ .home-tools-fold) > .home-tools-list > .home-tools-item:last-child { border-bottom: 1px solid var(--border); }
5060
- /* ---- レコード詳細の「AIによる最近の変更」(リポジトリの git 履歴 由来)----
5061
- カードは詳細ページの提案(.proposals)と同系色にする——どちらも
5062
- 「AIがやったこと・言っていることを読む」もので種類が同じ。
5063
- 出どころは 0.12.15 で週次整理レポートから git 履歴に変えた(レポートは毎回全置換で、
5064
- 翌週の実行で前の履歴が消えたため) */
5065
- .ai-history { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); }
5066
- .ah-summary { cursor: pointer; font-size: 13.5px; font-weight: 700; color: var(--muted); }
5067
- .ah-item {
5068
- background: #f8f9ff; border: 1px solid #c7d2fe; border-radius: 10px;
5069
- padding: 12px 14px; margin: 10px 0 0;
5070
- }
5071
- .ah-item-head { font-size: 14px; font-weight: 700; color: #3730a3; margin-bottom: 6px; line-height: 1.6; }
5072
- .ah-line { font-size: 13px; color: var(--muted); line-height: 1.8; word-break: break-word; }
5073
- .ah-meta { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 10px; font-size: 13px; }
5074
- .ah-commit { font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
5075
- /* 変わった行のかたまり(消えた行/増えた行)。**ファイル全体の前後ではない**ので、
5076
- ラベルを必ず添える(.ah-diff-label) */
5077
- .ah-diff { margin-top: 10px; }
5078
- .ah-diff-label { font-size: 12.5px; font-weight: 700; color: var(--muted); margin-bottom: 4px; }
5079
- /* 何を見せていて何を見せられないのかの断り。節の末尾に置く */
5080
- .ah-source { font-size: 12px; color: var(--muted); line-height: 1.7; margin: 12px 0 0; }
5081
- /* 差分は数千字になりうる。折りたたみの中でも高さを縛って、開いたときにページ全体を押し流さない */
5082
- .ah-diff-text {
5083
- margin: 8px 0 0; padding: 10px 12px; max-height: 320px; overflow: auto;
5084
- background: #fff; border: 1px solid var(--border); border-radius: 8px;
5085
- font-size: 12.5px; line-height: 1.7; white-space: pre-wrap; word-break: break-word;
5086
- }
5060
+ /* ---- 記録ページの「更新履歴」(engine の帳簿由来・#1094 便 35b-1 U2)----
5061
+ 1 行 = 日付・誰・何・きっかけ・GitHub で見る。区切りの「・」は CSS で足す(文字列に混ぜない) */
5062
+ .change-log { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border); }
5063
+ .ch-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; }
5064
+ .ch-head .section-label { margin: 0; }
5065
+ .ch-head a { font-size: 12.5px; }
5066
+ .ch-row { display: flex; flex-wrap: wrap; font-size: 13.5px; line-height: 1.9; padding: 6px 0; border-bottom: 1px solid var(--border); }
5067
+ .ch-row > span + span::before { content: "・"; margin: 0 6px; color: var(--muted); }
5068
+ .ch-day { font-variant-numeric: tabular-nums; color: var(--muted); }
5069
+ .ch-who { font-weight: 700; }
5070
+ .ch-what { word-break: break-word; }
5071
+ .ch-why, .ch-link { color: var(--muted); }
5072
+ .ch-empty { font-size: 13.5px; color: var(--muted); margin: 10px 0 0; }
5073
+ .ch-more { margin: 10px 0 0; font-size: 13px; }
5074
+ .ch-note { font-size: 12px; color: var(--muted); line-height: 1.7; margin: 12px 0 0; }
5087
5075
  /* ---- 関係者詳細の「つながっている ID」(frontmatter の identities 由来・#686)----
5088
- 折りたたみの見た目は「AIによる最近の変更」(.ai-history)と揃える——どちらも
5076
+ 折りたたみの見た目は「更新履歴」(.change-log)の節と同じ並び——どちらも
5089
5077
  **本文の後に足す、機械が書いたことを人が確かめる節**で種類が同じ。
5090
5078
 
5091
5079
  🔴 **表に <table> を使わない。** 画面の部品に table は1つも無く、table の CSS は
@@ -5115,7 +5103,7 @@ a.pager-btn:hover { text-decoration: none; }
5115
5103
  .li-denied-list { margin: 6px 0 0; padding-left: 20px; }
5116
5104
  .li-denied-row { overflow-wrap: anywhere; color: var(--muted); }
5117
5105
  .li-denied-kind { display: inline-block; min-width: 72px; margin-right: 8px; font-weight: 700; }
5118
- /* 違っていたときにどうするかの断り。.ah-source と同じ置き方(節の末尾の小さな段落) */
5106
+ /* 違っていたときにどうするかの断り。.ch-note と同じ置き方(節の末尾の小さな段落) */
5119
5107
  .li-note { font-size: 12px; color: var(--muted); line-height: 1.7; margin: 12px 0 0; }
5120
5108
  /* ---- レコードの削除(?op=delete-record)----
5121
5109
  置き場は2つ(2026-08-28 のオーナー裁定)。**未確認の記録は警告帯の中**で
@@ -6508,7 +6496,7 @@ ${calendarAddUrl.toString()}
6508
6496
  var HAS_FLEET = !!((SITE.internalSources || []).length || (SITE.externalSources || []).length ||
6509
6497
  (SITE.pipelines || []).length || SITE.capabilities || (SITE.secretKinds || []).length);
6510
6498
  // **週次整理は候補に数えない(2026-08-28 に節ごと撤去)。** レポートの中身は
6511
- // 関係するレコードのページ(「AIによる最近の変更」)へ移したので、この受け皿には
6499
+ // 関係するレコードのページ(今の「更新履歴」の前身)へ移したので、この受け皿には
6512
6500
  // もう週次整理の面が無い。数えたままにすると、fleet も intake も無い案件で
6513
6501
  // **中身の無い操作タブ**が立つ(#283 以前の挙動へ戻す)
6514
6502
  if (SITE.intake || HAS_FLEET) {
@@ -6824,6 +6812,7 @@ ${calendarAddUrl.toString()}
6824
6812
  // 状態の絞り込み。**未確認と置き換え済みを同じセレクトに並べる**(導線を増やさない)
6825
6813
  function statusLabel(v) {
6826
6814
  if (v === RETIRED_FILTER) return "置き換え済み";
6815
+ if (v === RETRACTED_FILTER) return "取り下げ";
6827
6816
  return v === "draft" ? "未確認のみ" : v;
6828
6817
  }
6829
6818
  /** 一覧に出す選択肢。**該当が1件も無い状態は選択肢に出さない**(押しても0件の項目を並べない) */
@@ -6832,6 +6821,8 @@ ${calendarAddUrl.toString()}
6832
6821
  if (hasUnverified(items)) opts.push("draft");
6833
6822
  // 置き換え済み(#699 F3)。決定にしか付かない印なので、決定の一覧でだけ選択肢になる
6834
6823
  if ((items || []).some(isRetired)) opts.push(RETIRED_FILTER);
6824
+ // 取り下げ(便 4-2d)。ルールにしか付かない印なので、ルールの一覧でだけ選択肢になる
6825
+ if ((items || []).some(isRetracted)) opts.push(RETRACTED_FILTER);
6835
6826
  return opts;
6836
6827
  }
6837
6828
  /** 絞り込みの判定。frontmatter の status との素の比較は「未確認のみ」と置き換え済み以外のとき */
@@ -6839,6 +6830,7 @@ ${calendarAddUrl.toString()}
6839
6830
  if (!listState.status) return true;
6840
6831
  // 決定以外の一覧(タブを移っても絞り込みは残る)では絞らない——全件が消えないように
6841
6832
  if (listState.status === RETIRED_FILTER) return rec && rec._kind === "decision" ? isRetired(rec) : true;
6833
+ if (listState.status === RETRACTED_FILTER) return rec && rec._kind === "generic" && rec._section === "Rules" ? isRetracted(rec) : true;
6842
6834
  // 「未確認のみ」は口に聞く(札と同じ答え)。それ以外の値は従来どおりの素の比較
6843
6835
  if (listState.status === "draft") return machineOwned(confirmPathOf(rec), rec.rstatus);
6844
6836
  return rec.rstatus === listState.status;
@@ -6897,6 +6889,38 @@ ${calendarAddUrl.toString()}
6897
6889
  if (!isRetired(rec)) return null;
6898
6890
  return el("span", { class: "badge retired", text: "置き換え済み" });
6899
6891
  }
6892
+ /**
6893
+ * ルールの取り下げ(cortex-engine #1094 便 4-2d)。ビルドが relations[retracted_by] から rec.retractedBy
6894
+ * (取り下げを決めた決定・無ければキーごと無い)を載せてある。**1 本以上あれば取り下げ済み**。
6895
+ * 札・薄く出す・詳細の節は、決定の「置き換えられた」と同じ部品を使う(本文は消さない)
6896
+ */
6897
+ var RETRACTED_FILTER = "retracted";
6898
+ function isRetracted(rec) {
6899
+ return !!(rec && rec._kind === "generic" && rec.retractedBy && rec.retractedBy.length);
6900
+ }
6901
+ function retractedBadge(rec) {
6902
+ if (!isRetracted(rec)) return null;
6903
+ return el("span", { class: "badge retired", text: "取り下げ" });
6904
+ }
6905
+ /** 取り下げ済みのルールを薄く出す(一覧のカード) */
6906
+ function retractedFadedClass(rec) {
6907
+ return isRetracted(rec) ? " is-faded" : "";
6908
+ }
6909
+ /** 詳細の節「取り下げを決めた決定」。取り下げの無い記録では null(要素を作らない) */
6910
+ function retractSection(r) {
6911
+ if (!isRetracted(r)) return null;
6912
+ var box = el("div", { class: "supersede-section" });
6913
+ box.appendChild(el("div", { class: "section-label", text: "取り下げを決めた決定" }));
6914
+ var ul = el("ul", { class: "supersede-list supersede-by" });
6915
+ r.retractedBy.forEach(function (b) {
6916
+ var row = supersedeRow(b.id, b.date, false);
6917
+ // 決定が実在しない(まだ同期されていない等)ときも、行を空にせず ID を出す
6918
+ if (!row.firstChild) row.appendChild(el("span", { class: "supersede-id mono", text: b.id }));
6919
+ ul.appendChild(row);
6920
+ });
6921
+ box.appendChild(ul);
6922
+ return box;
6923
+ }
6900
6924
  /** 候補の帯(症状)。行の札は「候補」とだけ言い、理由はこの帯 1 行に寄せる(繰り返さない) */
6901
6925
  var SUPERSEDE_CANDIDATE_NOTE = "前の決定は人が直しているため、置き換えは候補です";
6902
6926
  /** 決定 1 件の行。SITE にあれば題名で詳細へのリンク、無ければ ID だけ */
@@ -19192,6 +19216,7 @@ ${calendarAddUrl.toString()}
19192
19216
  pg.slice.forEach(function (r) {
19193
19217
  var meta = el("div", { class: "card-meta" }, [
19194
19218
  draftBadge(r),
19219
+ retractedBadge(r),
19195
19220
  latestBadge(r),
19196
19221
  r.date ? el("span", { class: "mono", text: r.date }) : null,
19197
19222
  ]);
@@ -19200,7 +19225,7 @@ ${calendarAddUrl.toString()}
19200
19225
  var traceR = el("a", { class: "mc-trace", href: "?tab=graph", text: "🕸 つながり" });
19201
19226
  traceR.addEventListener("click", function (ev) { ev.preventDefault(); ev.stopPropagation(); openExplorerFor(r); });
19202
19227
  meta.appendChild(traceR);
19203
- var card = el("div", { class: "card", title: r.id, "data-record-id": r.id }, [
19228
+ var card = el("div", { class: "card" + retractedFadedClass(r), title: r.id, "data-record-id": r.id }, [
19204
19229
  meta,
19205
19230
  cardTitleLink(r, r.deleted ? " is-deleted" : ""),
19206
19231
  r.summary ? el("p", { class: "card-summary", text: r.summary }) : null,
@@ -19216,96 +19241,103 @@ ${calendarAddUrl.toString()}
19216
19241
  }
19217
19242
 
19218
19243
  /* ---------------------------------------------------------------------------
19219
- * AIによる最近の変更(リポジトリの git 履歴 由来)
19244
+ * 更新履歴(記録ページ・engine の帳簿 gold-change-log.json 由来・#1094 便 35b-1 U2)
19220
19245
  * ---------------------------------------------------------------------------
19221
- * 夜間のパイプラインは、記録を勝手に起票し、draft の記録を勝手に書き換える。
19222
- * **黙って書き換えられた記録を作らないための面**がこれ——AIが何をしたかを、
19223
- * 変えられた記録自身のページに畳んで置く。
19224
- *
19225
- * 🔴 **出どころを週次整理レポートから git 履歴に変えた(0.12.15)。**
19226
- * レポート(.cortex/weekly-curation-report.json)は毎回全置換で、翌週の実行で
19227
- * 差し替わると**前の履歴が画面から消えた**——オーナーが実際に踏んだ(前日に見た変更が
19228
- * 翌日には無くなっていた)。1週間で消える履歴は、この節の目的を果たさない。
19229
- * git のコミットは消えないので、ビルド時にそこから作る(repo.ts の collectAiChangeHistory)。
19230
- *
19231
- * **取れないものは取れないと言う。** git には「なぜそう直したか」の根拠(引用)が
19232
- * 残らないので、出せるのは**変わった行・コミットメッセージ・日時**まで。
19233
- * 節の末尾でそれを1行断る(根拠があるように見せない)。
19234
- *
19235
- * **人のコミットは混ぜない。** 受け口の Lambda は人の操作の代理でコミットするので
19236
- *(「Gold確認: …」「レコード削除: …」)、bot 名義だけで拾うと人の操作がAIの仕業として
19237
- * 出る。切り分けは repo.ts の isAiCommit(ビルド時)。
19238
- *
19239
- * 🔴 **値はすべて textContent 経由で入れる**(el の text オプション)。
19240
- * 差分の中身・コミットメッセージは**リポジトリの生テキスト**で、記録の本文がそのまま
19241
- * 入る。innerHTML に渡すと記録の中身が画面のコードになる。
19242
- */
19243
-
19244
- /** そのレコードに対するAIの変更(新しい順・ビルド側で最大5件に絞ってある) */
19245
- function aiHistoryEntries(rec) {
19246
- var map = SITE.aiHistory;
19247
- if (!map) return [];
19248
- var p = repoPathOf(rec);
19249
- if (!p) return [];
19250
- return map[p] || [];
19251
- }
19252
- /**
19253
- * 見出しに出す日付(YYYY/M/D)。**Date に通さない**——閲覧者のタイムゾーンで日付がずれると、
19254
- * コミット自身が持っている日付と食い違う。読めない値は素のまま出す
19255
- */
19256
- function ahDate(iso) {
19257
- var m = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(String(iso));
19258
- if (!m) return String(iso);
19259
- return m[1] + "/" + parseInt(m[2], 10) + "/" + parseInt(m[3], 10);
19260
- }
19261
- // コミットへのリンク。repoBaseUrl が無ければ短縮SHAだけ出す(どの変更かは残る)
19262
- function ahCommitEl(sha) {
19263
- if (!sha) return null;
19264
- var short = sha.length > 8 ? sha.slice(0, 8) : sha;
19265
- if (!SITE.repoBaseUrl) return el("span", { class: "ah-commit", text: "コミット " + short });
19266
- return el("a", {
19267
- class: "ah-commit", href: SITE.repoBaseUrl + "/commit/" + encodeURIComponent(sha),
19268
- target: "_blank", rel: "noopener", text: "コミット " + short,
19269
- });
19270
- }
19271
- /**
19272
- * 変わった行のかたまり。**「消えた行/増えた行」と書く。**
19273
- * 出しているのは差分(-U0)であってファイル全体の前後ではないので、
19274
- * 「変更前」だけだと**記録の全文が載っていると誤読される**
19275
- */
19276
- function ahDiffEl(label, text) {
19277
- return el("div", { class: "ah-diff" }, [
19278
- el("div", { class: "ah-diff-label", text: label }),
19279
- el("pre", { class: "ah-diff-text", text: text }),
19246
+ * 記録ごとに「いつ・誰が・何を・何をきっかけに」を 1 行で出す。材料はビルドが帳簿から組んだ
19247
+ * SITE.changeLog(change-log.ts)で、画面は git も帳簿も読まない。
19248
+ *
19249
+ * 🔴 **画面に人が押す操作(戻す・差分)は置かない。** ボタンは「もっと見る」だけ(表示の開閉)。
19250
+ * 🔴 **値はすべて textContent(el の text)で入れる。** what・label は帳簿の生の文字列。
19251
+ * 🔴 **リンクは http(s) だけ。** ビルド側の検査を通らずに SITE が渡されても javascript: を href に入れない。
19252
+ * 🔴 **取れないものは出さない・推測しない。** きっかけが空の行は「きっかけ」の部分ごと出さない。
19253
+ * 🔴 **縮退は 1 つの形。** 帳簿が無い・壊れ・版違い・その記録の行が無い → 同じ節に空の文を出す(今の欄には戻さない)。
19254
+ * 出す記録は 4 区画の記録だけ(Home・ストーリー・README・雛形には出さない)。
19255
+ */
19256
+ var CH_RECORD_PATH = /^Cortex\\/(Decisions|Glossary|Members|Rules)\\/records\\/[^/]+\\.md$/;
19257
+ var CH_FIRST = 5;
19258
+ var CH_MAX = 50;
19259
+ var CH_EMPTY = "更新履歴はまだありません";
19260
+ function chHttp(url) { return /^https?:\\/\\//i.test(String(url)); }
19261
+ function chWho(row) {
19262
+ if (row.who === "night") return "自動更新";
19263
+ if (row.who === "member" && row.member) return row.member;
19264
+ return "更新者不明";
19265
+ }
19266
+ function chOutLink(cls, href, text) {
19267
+ return el("a", { class: cls, href: href, target: "_blank", rel: "noopener", text: text });
19268
+ }
19269
+ /** 1 行。各部分は span(区切りの「・」は CSS)。きっかけが無ければその部分ごと作らない */
19270
+ function chRowEl(row) {
19271
+ var line = el("div", { class: "ch-row" }, [
19272
+ el("span", { class: "ch-day", text: row.day }),
19273
+ el("span", { class: "ch-who", text: chWho(row) }),
19274
+ el("span", { class: "ch-what", text: row.what }),
19280
19275
  ]);
19281
- }
19282
- /**
19283
- * AIがこの記録にしたこと1件。
19284
- */
19285
- function ahEntryEl(e) {
19286
- var what = e.kind === "add" ? "AIがこの記録を作成しました" : "AIがこの記録を書き換えました";
19287
- var box = el("div", { class: "ah-item" }, [
19288
- el("div", { class: "ah-item-head", text: ahDate(e.at) + " " + what }),
19289
- ]);
19290
- if (e.subject) box.appendChild(el("div", { class: "ah-line", text: e.subject }));
19291
- if (e.kind !== "add") {
19292
- if (e.before) box.appendChild(ahDiffEl("変更前(消えた行)", e.before));
19293
- if (e.after) box.appendChild(ahDiffEl("変更後(増えた行)", e.after));
19294
- // **空欄で済ませない。** ビルドは「中身が変わっていないと言い切れる変更」
19295
- //(権限だけの変更・純粋な移動)はエントリにしないので、ここへ来るのは
19296
- // **中身は変わったのに差分を出せなかった**もの(記録を binary 扱いにしている案件・
19297
- // 履歴の窓の外)だけ。落とすと本物の変更が黙って消えるので、断りを出して残す
19298
- if (!e.before && !e.after) {
19299
- box.appendChild(el("div", { class: "ah-line", text: "変わった行をここに出せませんでした。コミットを開くと中身を確認できます。" }));
19300
- }
19301
- if (e.truncated) {
19302
- box.appendChild(el("div", { class: "ah-line", text: "※ 長いので途中までを出しています。全部はコミットで見られます。" }));
19303
- }
19276
+ var whys = row.why || [];
19277
+ if (whys.length) {
19278
+ var cause = el("span", { class: "ch-why" }, [document.createTextNode("きっかけ: ")]);
19279
+ whys.forEach(function (w, i) {
19280
+ if (i) cause.appendChild(document.createTextNode("、"));
19281
+ cause.appendChild(chHttp(w.url) ? chOutLink("ch-cause", w.url, w.label) : el("span", { text: w.label }));
19282
+ });
19283
+ line.appendChild(cause);
19284
+ }
19285
+ if (SITE.repoBaseUrl && row.commit) {
19286
+ line.appendChild(el("span", { class: "ch-link" }, [
19287
+ chOutLink("ch-commit", SITE.repoBaseUrl + "/commit/" + encodeURIComponent(row.commit), "GitHub で見る"),
19288
+ ]));
19289
+ }
19290
+ return line;
19291
+ }
19292
+ function changeLogRowsOf(path) {
19293
+ var view = SITE.changeLog;
19294
+ if (!view || !view.rows) return [];
19295
+ var key = path.normalize("NFC");
19296
+ return Object.prototype.hasOwnProperty.call(view.rows, key) ? view.rows[key] : [];
19297
+ }
19298
+ /** 記録ページの末尾の節。4 区画の記録でなければ null(節ごと出さない) */
19299
+ function changeLogSection(rec) {
19300
+ var path = repoPathOf(rec);
19301
+ if (!path || !CH_RECORD_PATH.test(path.normalize("NFC"))) return null;
19302
+ // 区画の規約・雛形はレコードではない
19303
+ var baseName = path.slice(path.lastIndexOf("/") + 1).toLowerCase();
19304
+ if (baseName === "readme.md" || baseName === "template.md") return null;
19305
+ var rows = changeLogRowsOf(path);
19306
+ var days = SITE.changeLog && SITE.changeLog.windowDays;
19307
+ var head = el("div", { class: "ch-head" }, [el("span", { class: "section-label", text: "更新履歴" })]);
19308
+ // 記録全体の履歴(主)。編集リンクの /edit/ を /commits/ に替える(ブランチ名・パスの符号化を作り直さない)
19309
+ if (rec.editUrl && rec.editUrl.indexOf("/edit/") >= 0) {
19310
+ head.appendChild(chOutLink("ch-all", rec.editUrl.replace("/edit/", "/commits/"), "GitHub の履歴"));
19311
+ }
19312
+ var box = el("section", { class: "change-log", "aria-label": "更新履歴" }, [head]);
19313
+ if (!rows.length) {
19314
+ // 読めた帳簿で行が無い記録は「窓の外・落とした変更だけ」もありうるので、窓を言う。読めないときは素の 1 文
19315
+ box.appendChild(el("p", { class: "ch-empty", text: days ? "直近 " + days + " 日の更新履歴はありません。これより前は GitHub の履歴で見られます。" : CH_EMPTY }));
19316
+ return box;
19317
+ }
19318
+ var list = el("div", { class: "ch-list" });
19319
+ rows.slice(0, CH_FIRST).forEach(function (row) { list.appendChild(chRowEl(row)); });
19320
+ box.appendChild(list);
19321
+ if (rows.length > CH_FIRST) {
19322
+ var rest = null;
19323
+ var more = el("button", { type: "button", class: "ch-more pager-btn", "aria-expanded": "false", text: "もっと見る(" + (rows.length - CH_FIRST) + " 件)" });
19324
+ // 文言は固定(開閉は aria-expanded が伝える)
19325
+ more.addEventListener("click", function () {
19326
+ var open = more.getAttribute("aria-expanded") !== "true";
19327
+ if (open && !rest) {
19328
+ rest = el("div", { class: "ch-rest" });
19329
+ rows.slice(CH_FIRST).forEach(function (row) { rest.appendChild(chRowEl(row)); });
19330
+ list.appendChild(rest);
19331
+ }
19332
+ if (rest) rest.hidden = !open;
19333
+ more.setAttribute("aria-expanded", open ? "true" : "false");
19334
+ });
19335
+ box.appendChild(more);
19336
+ }
19337
+ // 窓の上限(50 件)まで届いているときだけ、これより前の逃げ道を言う
19338
+ if (rows.length >= CH_MAX) {
19339
+ box.appendChild(el("p", { class: "ch-note", text: "新しいほうから最大 " + CH_MAX + " 件まで出しています。これより前は GitHub の履歴で見られます。" }));
19304
19340
  }
19305
- var meta = el("div", { class: "ah-meta" });
19306
- var commit = ahCommitEl(e.commit);
19307
- if (commit) meta.appendChild(commit);
19308
- if (meta.children.length) box.appendChild(meta);
19309
19341
  return box;
19310
19342
  }
19311
19343
  // ---------- 関係者詳細の「つながっている ID」(#686・#704 で値・場・リンク・否定済み) ----------
@@ -19595,30 +19627,11 @@ ${calendarAddUrl.toString()}
19595
19627
  box.appendChild(ul);
19596
19628
  det.appendChild(box);
19597
19629
  }
19598
- // 違っていたときにどうするか。.ah-source と同じ置き方(節の末尾の小さな段落)
19630
+ // 違っていたときにどうするか。.ch-note と同じ置き方(節の末尾の小さな段落)
19599
19631
  det.appendChild(el("p", { class: "li-note", text: LI_NOTE }));
19600
19632
  return det;
19601
19633
  }
19602
19634
 
19603
- /**
19604
- * 「AIによる最近の変更」の折りたたみ。該当が0件なら null(節ごと出さない)。
19605
- *
19606
- * 画面に人が押す操作(戻す・差分)は置かない(#1094 便 e4)。
19607
- */
19608
- function buildAiHistory(rec) {
19609
- var list = aiHistoryEntries(rec);
19610
- if (!list.length) return null;
19611
- var summary = el("summary", { class: "ah-summary", text: "AIによる最近の変更(" + list.length + "件)" });
19612
- var det = el("details", { class: "ai-history" }, [summary]);
19613
- list.forEach(function (e) { det.appendChild(ahEntryEl(e)); });
19614
- // 🔴 何を見せていて、何を見せられないのかを最後に断る
19615
- det.appendChild(el("p", {
19616
- class: "ah-source",
19617
- text: "この一覧はリポジトリの変更履歴(コミット)から作っています。AIがなぜそう直したかの根拠は履歴に残らないため、変わった行とコミットだけを出しています。ファイルの移動やデータ構造の入れ替えなど、記録の中身が変わっていない変更は出していません。件数が多いときは新しいほうから5件までです。",
19618
- }));
19619
- return det;
19620
- }
19621
-
19622
19635
  // ---------- レコードの削除(?op=delete-record) ----------
19623
19636
  //
19624
19637
  // AIが起票した記録を「要らない」と判断したとき、**消すだけでは翌晩また現れる**
@@ -19805,14 +19818,14 @@ ${calendarAddUrl.toString()}
19805
19818
  }
19806
19819
  /** 詳細ページの末尾(本文・参照の後)に、つながっている ID・その後の記録・AIの変更履歴・frontmatter を並べる */
19807
19820
  function appendRecordFooter(rec) {
19808
- // 「つながっている ID」は本文の後・「AIによる最近の変更」の前(関係者の記録だけ・#686)
19821
+ // 「つながっている ID」は本文の後・「更新履歴」の前(関係者の記録だけ・#686)
19809
19822
  var linked = buildLinkedIds(rec);
19810
19823
  if (linked) app.appendChild(linked);
19811
19824
  // 決定の「その後の記録」(担当者の答えの 1 行)も同じ席——**本文と参照を読んでから、その後を見る順**。
19812
19825
  // 決定以外の記録・答えが無い決定は rec.followupHuman を持たないので null(要素を作らない)
19813
19826
  var fu = followupSection(rec);
19814
19827
  if (fu) app.appendChild(fu);
19815
- var history = buildAiHistory(rec);
19828
+ var history = changeLogSection(rec);
19816
19829
  if (history) app.appendChild(history);
19817
19830
  var fmp = renderFmPanel(rec.fm);
19818
19831
  if (fmp) app.appendChild(fmp);
@@ -20047,6 +20060,7 @@ ${calendarAddUrl.toString()}
20047
20060
 
20048
20061
  var metaR = el("div", { class: "detail-meta", title: r.id, "data-record-id": r.id }, [
20049
20062
  draftBadge(r),
20063
+ retractedBadge(r),
20050
20064
  latestBadge(r),
20051
20065
  r.date ? el("span", { class: "mono", text: r.date }) : null,
20052
20066
  r.summary ? el("span", { text: r.summary }) : null,
@@ -20066,6 +20080,10 @@ ${calendarAddUrl.toString()}
20066
20080
  app.appendChild(genBody);
20067
20081
  loadBody(r, genBody);
20068
20082
 
20083
+ // 取り下げ(便 4-2d)。本文の後・「その後の記録」の前。取り下げの無い記録では何も作らない
20084
+ var retr = retractSection(r);
20085
+ if (retr) app.appendChild(retr);
20086
+
20069
20087
  appendRecordFooter(r);
20070
20088
  }
20071
20089
 
@@ -20099,6 +20117,7 @@ ${calendarAddUrl.toString()}
20099
20117
  derived_from: { color: "#0d9488", label: "生成元", kind: "gold" },
20100
20118
  relates_to: { color: "#94a3b8", label: "関連", kind: "gold" },
20101
20119
  supersedes: { color: "#dc2626", label: "置換", kind: "gold" },
20120
+ retracted_by: { color: "#dc2626", label: "取り下げ", kind: "gold", onlyWhenPresent: true },
20102
20121
  reference: { color: "#d1d5db", label: "参照", kind: "evidence" },
20103
20122
  source: { color: "#d1d5db", label: "出典", kind: "evidence" },
20104
20123
  link: { color: "#e2e8f0", label: "リンク", kind: "evidence" },
@@ -20723,7 +20742,7 @@ ${calendarAddUrl.toString()}
20723
20742
 
20724
20743
  var EDGE_DARK = {
20725
20744
  based_on: "rgba(125,165,255,0.85)", derived_from: "rgba(94,234,212,0.75)",
20726
- relates_to: "rgba(148,163,184,0.6)", supersedes: "rgba(248,113,113,0.9)",
20745
+ relates_to: "rgba(148,163,184,0.6)", supersedes: "rgba(248,113,113,0.9)", retracted_by: "rgba(248,113,113,0.9)",
20727
20746
  reference: "rgba(180,196,230,0.32)", source: "rgba(180,196,230,0.32)", link: "rgba(180,196,230,0.22)",
20728
20747
  about: "rgba(196,181,253,0.22)",
20729
20748
  decided_by: "rgba(244,114,182,0.75)",
@@ -21059,7 +21078,7 @@ ${calendarAddUrl.toString()}
21059
21078
  // レコード情報(frontmatter)のパネルが英語の語のまま出さないためだけ(凡例の REL_LABEL には足さない)
21060
21079
  var REL_LABEL = {
21061
21080
  based_on: "根拠", derived_from: "由来", relates_to: "関連",
21062
- supersedes: "置換", superseded_by: "置換先", reference: "参照", source: "出典", link: "リンク",
21081
+ supersedes: "置換", superseded_by: "置換先", retracted_by: "取り下げ", reference: "参照", source: "出典", link: "リンク",
21063
21082
  about: "話題",
21064
21083
  decided_by: "決めた人",
21065
21084
  };
@@ -21074,7 +21093,7 @@ ${calendarAddUrl.toString()}
21074
21093
  (adjDir[e.from] = adjDir[e.from] || []).push({ id: e.to, rel: e.rel, dir: "out" });
21075
21094
  (adjDir[e.to] = adjDir[e.to] || []).push({ id: e.from, rel: e.rel, dir: "in" });
21076
21095
  });
21077
- var REL_ORDER = ["supersedes", "based_on", "derived_from", "relates_to", "decided_by", "source", "reference", "link", "about"];
21096
+ var REL_ORDER = ["supersedes", "retracted_by", "based_on", "derived_from", "relates_to", "decided_by", "source", "reference", "link", "about"];
21078
21097
 
21079
21098
  function recordText(rec) {
21080
21099
  if (!rec) return "";