@takagaki/cortex-decisions-viewer 0.12.119 → 0.12.121

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) {
@@ -6662,7 +6650,7 @@ ${calendarAddUrl.toString()}
6662
6650
  }
6663
6651
  // 未確認(status: draft = AI生成・人間未確認)の印。Gold層の4型(決定事項・用語集・メンバー・ルール)共通で、active・status無しには何も出さない
6664
6652
  function draftBadge(rec) {
6665
- return rec && machineOwned(confirmPathOf(rec), rec.rstatus) ? el("span", { class: "badge draft", text: "AI生成・未確認" }) : null;
6653
+ return rec && unconfirmed(rec) ? el("span", { class: "badge draft", text: "AI生成・未確認" }) : null;
6666
6654
  }
6667
6655
  // 直近のGold昇格で作られたレコードの印。レコードの date は決定日なので、日付順に並べても
6668
6656
  // 「今回の実行で何が増えたか」は分からない。ビルド時に git から判定した結果(isLatest)を出す
@@ -6679,7 +6667,7 @@ ${calendarAddUrl.toString()}
6679
6667
  // 同じ場面の同じ判断なので、口を離さない。**この帯は draft にしか出ない**ので、
6680
6668
  // draft でない記録の削除の口は操作列の末尾(attachDeleteToEditRow)に置く。
6681
6669
  function draftNote(rec) {
6682
- if (!rec || !machineOwned(confirmPathOf(rec), rec.rstatus)) return null;
6670
+ if (!rec || !unconfirmed(rec)) return null;
6683
6671
  var path = promotableRecordPath(rec);
6684
6672
  var note = el("div", { class: "draft-note" }, [
6685
6673
  el("span", { text: "⚠️" }),
@@ -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,8 +6830,9 @@ ${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
- if (listState.status === "draft") return machineOwned(confirmPathOf(rec), rec.rstatus);
6835
+ if (listState.status === "draft") return unconfirmed(rec);
6844
6836
  return rec.rstatus === listState.status;
6845
6837
  }
6846
6838
  /* -------------------------------------------------------------------------
@@ -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 だけ */
@@ -7066,7 +7090,7 @@ ${calendarAddUrl.toString()}
7066
7090
 
7067
7091
  // 未確認が1件も無い一覧では絞り込みを出さない
7068
7092
  function hasUnverified(items) {
7069
- return (items || []).some(function (r) { return machineOwned(confirmPathOf(r), r.rstatus); });
7093
+ return (items || []).some(function (r) { return unconfirmed(r); });
7070
7094
  }
7071
7095
 
7072
7096
  // ---------- AI導線(Gold層への人間の書き込み口。claude.ai/code へのディープリンク) ----------
@@ -7176,6 +7200,14 @@ ${calendarAddUrl.toString()}
7176
7200
  if (rec && rec._kind === "generic" && rec._section) return "Cortex/" + rec._section + "/";
7177
7201
  return null;
7178
7202
  }
7203
+ /**
7204
+ * 「AI生成・未確認」(機械の下書き)か。札・警告帯・[確認済みにする]・「未確認のみ」・削除の置き場は**全部これに聞く**。
7205
+ * 🔴 **記憶 v2 の案件(人の手の表の写しがある=SITE.memoryV2・cortex-engine #1094 q-10)では常に偽**。
7206
+ * 画面で人が押すのは記録の削除と名寄せの確定だけで、確認の印は出さない(削除は帯の外の操作列に出る)
7207
+ */
7208
+ function unconfirmed(rec) {
7209
+ return !SITE.memoryV2 && !!rec && machineOwned(confirmPathOf(rec), rec.rstatus);
7210
+ }
7179
7211
  /**
7180
7212
  * 更新提案を AI に適用させるプロンプトの締め(status の扱い)。**3 つに分ける**(cortex-engine #1094 段 4):
7181
7213
  * - 下書き(draft)…… 人が確かめて直したので active に上げさせる
@@ -7184,7 +7216,9 @@ ${calendarAddUrl.toString()}
7184
7216
  * - それ以外(人の値)…… 据え置き(draft に戻させない)
7185
7217
  */
7186
7218
  function statusKeepLine(rec) {
7187
- if (machineOwned(confirmPathOf(rec), rec.rstatus)) {
7219
+ // 記憶 v2 の案件: status は確認の印ではない。どの値でも触らせない
7220
+ if (SITE.memoryV2) return "この記録の status の欄は変えないでください(足さない・書き換えない)。";
7221
+ if (unconfirmed(rec)) {
7188
7222
  return "あなた(人間)が内容を確認して編集したので、status が draft なら active に上げてください。";
7189
7223
  }
7190
7224
  if (!rec.rstatus) return "この記録には status の欄がありません。status の欄は足さないでください。";
@@ -17691,7 +17725,8 @@ ${calendarAddUrl.toString()}
17691
17725
  ]));
17692
17726
  });
17693
17727
  sec.appendChild(what);
17694
- sec.appendChild(el("p", { class: "about-note", text:
17728
+ // 記憶 v2 の案件には確認の印もボタンも無いので、この説明は出さない(出すと「どこにも無い印」を探させる)
17729
+ if (!SITE.memoryV2) sec.appendChild(el("p", { class: "about-note", text:
17695
17730
  "自動で追加されたものには「AI生成・未確認」と表示されます。内容を確認して、そのままでよければ「確認済みにする」を押してください。直したいところがあれば「AIで編集」から修正できます。どちらの場合も、その印は外れます。" }));
17696
17731
 
17697
17732
  sec.appendChild(el("blockquote", { class: "about-quote", text:
@@ -19192,6 +19227,7 @@ ${calendarAddUrl.toString()}
19192
19227
  pg.slice.forEach(function (r) {
19193
19228
  var meta = el("div", { class: "card-meta" }, [
19194
19229
  draftBadge(r),
19230
+ retractedBadge(r),
19195
19231
  latestBadge(r),
19196
19232
  r.date ? el("span", { class: "mono", text: r.date }) : null,
19197
19233
  ]);
@@ -19200,7 +19236,7 @@ ${calendarAddUrl.toString()}
19200
19236
  var traceR = el("a", { class: "mc-trace", href: "?tab=graph", text: "🕸 つながり" });
19201
19237
  traceR.addEventListener("click", function (ev) { ev.preventDefault(); ev.stopPropagation(); openExplorerFor(r); });
19202
19238
  meta.appendChild(traceR);
19203
- var card = el("div", { class: "card", title: r.id, "data-record-id": r.id }, [
19239
+ var card = el("div", { class: "card" + retractedFadedClass(r), title: r.id, "data-record-id": r.id }, [
19204
19240
  meta,
19205
19241
  cardTitleLink(r, r.deleted ? " is-deleted" : ""),
19206
19242
  r.summary ? el("p", { class: "card-summary", text: r.summary }) : null,
@@ -19216,96 +19252,103 @@ ${calendarAddUrl.toString()}
19216
19252
  }
19217
19253
 
19218
19254
  /* ---------------------------------------------------------------------------
19219
- * AIによる最近の変更(リポジトリの git 履歴 由来)
19255
+ * 更新履歴(記録ページ・engine の帳簿 gold-change-log.json 由来・#1094 便 35b-1 U2)
19220
19256
  * ---------------------------------------------------------------------------
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 }),
19280
- ]);
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 }),
19257
+ * 記録ごとに「いつ・誰が・何を・何をきっかけに」を 1 行で出す。材料はビルドが帳簿から組んだ
19258
+ * SITE.changeLog(change-log.ts)で、画面は git も帳簿も読まない。
19259
+ *
19260
+ * 🔴 **画面に人が押す操作(戻す・差分)は置かない。** ボタンは「もっと見る」だけ(表示の開閉)。
19261
+ * 🔴 **値はすべて textContent(el の text)で入れる。** what・label は帳簿の生の文字列。
19262
+ * 🔴 **リンクは http(s) だけ。** ビルド側の検査を通らずに SITE が渡されても javascript: を href に入れない。
19263
+ * 🔴 **取れないものは出さない・推測しない。** きっかけが空の行は「きっかけ」の部分ごと出さない。
19264
+ * 🔴 **縮退は 1 つの形。** 帳簿が無い・壊れ・版違い・その記録の行が無い → 同じ節に空の文を出す(今の欄には戻さない)。
19265
+ * 出す記録は 4 区画の記録だけ(Home・ストーリー・README・雛形には出さない)。
19266
+ */
19267
+ var CH_RECORD_PATH = /^Cortex\\/(Decisions|Glossary|Members|Rules)\\/records\\/[^/]+\\.md$/;
19268
+ var CH_FIRST = 5;
19269
+ var CH_MAX = 50;
19270
+ var CH_EMPTY = "更新履歴はまだありません";
19271
+ function chHttp(url) { return /^https?:\\/\\//i.test(String(url)); }
19272
+ function chWho(row) {
19273
+ if (row.who === "night") return "自動更新";
19274
+ if (row.who === "member" && row.member) return row.member;
19275
+ return "更新者不明";
19276
+ }
19277
+ function chOutLink(cls, href, text) {
19278
+ return el("a", { class: cls, href: href, target: "_blank", rel: "noopener", text: text });
19279
+ }
19280
+ /** 1 行。各部分は span(区切りの「・」は CSS)。きっかけが無ければその部分ごと作らない */
19281
+ function chRowEl(row) {
19282
+ var line = el("div", { class: "ch-row" }, [
19283
+ el("span", { class: "ch-day", text: row.day }),
19284
+ el("span", { class: "ch-who", text: chWho(row) }),
19285
+ el("span", { class: "ch-what", text: row.what }),
19289
19286
  ]);
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
- }
19287
+ var whys = row.why || [];
19288
+ if (whys.length) {
19289
+ var cause = el("span", { class: "ch-why" }, [document.createTextNode("きっかけ: ")]);
19290
+ whys.forEach(function (w, i) {
19291
+ if (i) cause.appendChild(document.createTextNode("、"));
19292
+ cause.appendChild(chHttp(w.url) ? chOutLink("ch-cause", w.url, w.label) : el("span", { text: w.label }));
19293
+ });
19294
+ line.appendChild(cause);
19295
+ }
19296
+ if (SITE.repoBaseUrl && row.commit) {
19297
+ line.appendChild(el("span", { class: "ch-link" }, [
19298
+ chOutLink("ch-commit", SITE.repoBaseUrl + "/commit/" + encodeURIComponent(row.commit), "GitHub で見る"),
19299
+ ]));
19300
+ }
19301
+ return line;
19302
+ }
19303
+ function changeLogRowsOf(path) {
19304
+ var view = SITE.changeLog;
19305
+ if (!view || !view.rows) return [];
19306
+ var key = path.normalize("NFC");
19307
+ return Object.prototype.hasOwnProperty.call(view.rows, key) ? view.rows[key] : [];
19308
+ }
19309
+ /** 記録ページの末尾の節。4 区画の記録でなければ null(節ごと出さない) */
19310
+ function changeLogSection(rec) {
19311
+ var path = repoPathOf(rec);
19312
+ if (!path || !CH_RECORD_PATH.test(path.normalize("NFC"))) return null;
19313
+ // 区画の規約・雛形はレコードではない
19314
+ var baseName = path.slice(path.lastIndexOf("/") + 1).toLowerCase();
19315
+ if (baseName === "readme.md" || baseName === "template.md") return null;
19316
+ var rows = changeLogRowsOf(path);
19317
+ var days = SITE.changeLog && SITE.changeLog.windowDays;
19318
+ var head = el("div", { class: "ch-head" }, [el("span", { class: "section-label", text: "更新履歴" })]);
19319
+ // 記録全体の履歴(主)。編集リンクの /edit/ を /commits/ に替える(ブランチ名・パスの符号化を作り直さない)
19320
+ if (rec.editUrl && rec.editUrl.indexOf("/edit/") >= 0) {
19321
+ head.appendChild(chOutLink("ch-all", rec.editUrl.replace("/edit/", "/commits/"), "GitHub の履歴"));
19322
+ }
19323
+ var box = el("section", { class: "change-log", "aria-label": "更新履歴" }, [head]);
19324
+ if (!rows.length) {
19325
+ // 読めた帳簿で行が無い記録は「窓の外・落とした変更だけ」もありうるので、窓を言う。読めないときは素の 1 文
19326
+ box.appendChild(el("p", { class: "ch-empty", text: days ? "直近 " + days + " 日の更新履歴はありません。これより前は GitHub の履歴で見られます。" : CH_EMPTY }));
19327
+ return box;
19328
+ }
19329
+ var list = el("div", { class: "ch-list" });
19330
+ rows.slice(0, CH_FIRST).forEach(function (row) { list.appendChild(chRowEl(row)); });
19331
+ box.appendChild(list);
19332
+ if (rows.length > CH_FIRST) {
19333
+ var rest = null;
19334
+ var more = el("button", { type: "button", class: "ch-more pager-btn", "aria-expanded": "false", text: "もっと見る(" + (rows.length - CH_FIRST) + " 件)" });
19335
+ // 文言は固定(開閉は aria-expanded が伝える)
19336
+ more.addEventListener("click", function () {
19337
+ var open = more.getAttribute("aria-expanded") !== "true";
19338
+ if (open && !rest) {
19339
+ rest = el("div", { class: "ch-rest" });
19340
+ rows.slice(CH_FIRST).forEach(function (row) { rest.appendChild(chRowEl(row)); });
19341
+ list.appendChild(rest);
19342
+ }
19343
+ if (rest) rest.hidden = !open;
19344
+ more.setAttribute("aria-expanded", open ? "true" : "false");
19345
+ });
19346
+ box.appendChild(more);
19347
+ }
19348
+ // 窓の上限(50 件)まで届いているときだけ、これより前の逃げ道を言う
19349
+ if (rows.length >= CH_MAX) {
19350
+ box.appendChild(el("p", { class: "ch-note", text: "新しいほうから最大 " + CH_MAX + " 件まで出しています。これより前は GitHub の履歴で見られます。" }));
19304
19351
  }
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
19352
  return box;
19310
19353
  }
19311
19354
  // ---------- 関係者詳細の「つながっている ID」(#686・#704 で値・場・リンク・否定済み) ----------
@@ -19595,30 +19638,11 @@ ${calendarAddUrl.toString()}
19595
19638
  box.appendChild(ul);
19596
19639
  det.appendChild(box);
19597
19640
  }
19598
- // 違っていたときにどうするか。.ah-source と同じ置き方(節の末尾の小さな段落)
19641
+ // 違っていたときにどうするか。.ch-note と同じ置き方(節の末尾の小さな段落)
19599
19642
  det.appendChild(el("p", { class: "li-note", text: LI_NOTE }));
19600
19643
  return det;
19601
19644
  }
19602
19645
 
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
19646
  // ---------- レコードの削除(?op=delete-record) ----------
19623
19647
  //
19624
19648
  // AIが起票した記録を「要らない」と判断したとき、**消すだけでは翌晩また現れる**
@@ -19797,7 +19821,7 @@ ${calendarAddUrl.toString()}
19797
19821
  * 見た目は控えめな文字のボタンのままにして、隣の実体のあるボタンと重さを分ける。
19798
19822
  */
19799
19823
  function attachDeleteToEditRow(rec, editRow) {
19800
- if (!rec || machineOwned(confirmPathOf(rec), rec.rstatus)) return null;
19824
+ if (!rec || unconfirmed(rec)) return null;
19801
19825
  var del = buildDeleteControls(rec);
19802
19826
  if (!del) return null;
19803
19827
  editRow.appendChild(del.btn);
@@ -19805,14 +19829,14 @@ ${calendarAddUrl.toString()}
19805
19829
  }
19806
19830
  /** 詳細ページの末尾(本文・参照の後)に、つながっている ID・その後の記録・AIの変更履歴・frontmatter を並べる */
19807
19831
  function appendRecordFooter(rec) {
19808
- // 「つながっている ID」は本文の後・「AIによる最近の変更」の前(関係者の記録だけ・#686)
19832
+ // 「つながっている ID」は本文の後・「更新履歴」の前(関係者の記録だけ・#686)
19809
19833
  var linked = buildLinkedIds(rec);
19810
19834
  if (linked) app.appendChild(linked);
19811
19835
  // 決定の「その後の記録」(担当者の答えの 1 行)も同じ席——**本文と参照を読んでから、その後を見る順**。
19812
19836
  // 決定以外の記録・答えが無い決定は rec.followupHuman を持たないので null(要素を作らない)
19813
19837
  var fu = followupSection(rec);
19814
19838
  if (fu) app.appendChild(fu);
19815
- var history = buildAiHistory(rec);
19839
+ var history = changeLogSection(rec);
19816
19840
  if (history) app.appendChild(history);
19817
19841
  var fmp = renderFmPanel(rec.fm);
19818
19842
  if (fmp) app.appendChild(fmp);
@@ -20047,6 +20071,7 @@ ${calendarAddUrl.toString()}
20047
20071
 
20048
20072
  var metaR = el("div", { class: "detail-meta", title: r.id, "data-record-id": r.id }, [
20049
20073
  draftBadge(r),
20074
+ retractedBadge(r),
20050
20075
  latestBadge(r),
20051
20076
  r.date ? el("span", { class: "mono", text: r.date }) : null,
20052
20077
  r.summary ? el("span", { text: r.summary }) : null,
@@ -20066,6 +20091,10 @@ ${calendarAddUrl.toString()}
20066
20091
  app.appendChild(genBody);
20067
20092
  loadBody(r, genBody);
20068
20093
 
20094
+ // 取り下げ(便 4-2d)。本文の後・「その後の記録」の前。取り下げの無い記録では何も作らない
20095
+ var retr = retractSection(r);
20096
+ if (retr) app.appendChild(retr);
20097
+
20069
20098
  appendRecordFooter(r);
20070
20099
  }
20071
20100
 
@@ -20099,6 +20128,7 @@ ${calendarAddUrl.toString()}
20099
20128
  derived_from: { color: "#0d9488", label: "生成元", kind: "gold" },
20100
20129
  relates_to: { color: "#94a3b8", label: "関連", kind: "gold" },
20101
20130
  supersedes: { color: "#dc2626", label: "置換", kind: "gold" },
20131
+ retracted_by: { color: "#dc2626", label: "取り下げ", kind: "gold", onlyWhenPresent: true },
20102
20132
  reference: { color: "#d1d5db", label: "参照", kind: "evidence" },
20103
20133
  source: { color: "#d1d5db", label: "出典", kind: "evidence" },
20104
20134
  link: { color: "#e2e8f0", label: "リンク", kind: "evidence" },
@@ -20723,7 +20753,7 @@ ${calendarAddUrl.toString()}
20723
20753
 
20724
20754
  var EDGE_DARK = {
20725
20755
  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)",
20756
+ relates_to: "rgba(148,163,184,0.6)", supersedes: "rgba(248,113,113,0.9)", retracted_by: "rgba(248,113,113,0.9)",
20727
20757
  reference: "rgba(180,196,230,0.32)", source: "rgba(180,196,230,0.32)", link: "rgba(180,196,230,0.22)",
20728
20758
  about: "rgba(196,181,253,0.22)",
20729
20759
  decided_by: "rgba(244,114,182,0.75)",
@@ -21059,7 +21089,7 @@ ${calendarAddUrl.toString()}
21059
21089
  // レコード情報(frontmatter)のパネルが英語の語のまま出さないためだけ(凡例の REL_LABEL には足さない)
21060
21090
  var REL_LABEL = {
21061
21091
  based_on: "根拠", derived_from: "由来", relates_to: "関連",
21062
- supersedes: "置換", superseded_by: "置換先", reference: "参照", source: "出典", link: "リンク",
21092
+ supersedes: "置換", superseded_by: "置換先", retracted_by: "取り下げ", reference: "参照", source: "出典", link: "リンク",
21063
21093
  about: "話題",
21064
21094
  decided_by: "決めた人",
21065
21095
  };
@@ -21074,7 +21104,7 @@ ${calendarAddUrl.toString()}
21074
21104
  (adjDir[e.from] = adjDir[e.from] || []).push({ id: e.to, rel: e.rel, dir: "out" });
21075
21105
  (adjDir[e.to] = adjDir[e.to] || []).push({ id: e.from, rel: e.rel, dir: "in" });
21076
21106
  });
21077
- var REL_ORDER = ["supersedes", "based_on", "derived_from", "relates_to", "decided_by", "source", "reference", "link", "about"];
21107
+ var REL_ORDER = ["supersedes", "retracted_by", "based_on", "derived_from", "relates_to", "decided_by", "source", "reference", "link", "about"];
21078
21108
 
21079
21109
  function recordText(rec) {
21080
21110
  if (!rec) return "";