@takagaki/cortex-decisions-viewer 0.12.13 → 0.12.15

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
@@ -38,7 +38,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.readFleetSources = readFleetSources;
40
40
  exports.readGoldProposals = readGoldProposals;
41
- exports.readWeeklyCuration = readWeeklyCuration;
42
41
  exports.buildHomeGrounding = buildHomeGrounding;
43
42
  exports.collectMeetings = collectMeetings;
44
43
  exports.findBacklogDir = findBacklogDir;
@@ -520,276 +519,6 @@ async function readGoldProposals(repoRoot) {
520
519
  }
521
520
  return byPath;
522
521
  }
523
- // ---- 週次整理レポート(.cortex/weekly-curation-report.json)------------------
524
- //
525
- // 週次キュレーション run が「今週AIが何を自動でやったか」と「人に判断してほしい残り」を
526
- // 毎回全置換で書く。**読み方は readGoldProposals と同じ規律**——無い・壊れている・
527
- // version が違うときは表示面なしで正常継続し、フィールドは明示の許可リストで射影する。
528
- //
529
- // 🔴 **素通しにしないこと。** engine 側が単独で版を上げるので、画面が知らないキーが
530
- // 必ず先に入る。素通しにすると、公開範囲フィルタを通していない生の断片(内部限定の
531
- // 引用・敗者レコードの全文など)が知らないうちに顧客の画面へ載りうる。
532
- /** 文字列化。**配列は `, ` で連結する**(synonyms のような配列値が `a,b` に潰れるより読める) */
533
- function wcStr(v) {
534
- if (Array.isArray(v))
535
- return v.map((x) => String(x)).join(", ");
536
- return v != null ? String(v) : "";
537
- }
538
- /** 空文字はキーごと落とす(「無い」と「空」を画面で区別できるようにする) */
539
- function wcOptStr(v) {
540
- const text = wcStr(v);
541
- return text === "" ? undefined : text;
542
- }
543
- /** 数値だけ通す。文字列の "0.78" も落とす(一致度の表示は数値である前提で組んである) */
544
- function wcNum(v) {
545
- return typeof v === "number" && Number.isFinite(v) ? v : undefined;
546
- }
547
- /**
548
- * レコードの指し先(勝者・敗者・クラスタの構成員)。
549
- * **title が無い要素は捨てる**——表示の主語が無いと、画面に出しても何の話か分からない。
550
- * path は無いことがある(統合で消えた敗者にはもう宛先が無い)ので必須にしない。
551
- */
552
- function wcRef(v) {
553
- if (v == null || typeof v !== "object" || Array.isArray(v))
554
- return null;
555
- const row = v;
556
- const title = wcStr(row.title);
557
- if (!title)
558
- return null;
559
- // **短縮記法(`title,`)にしないこと**(readGoldProposals と同じ理由。
560
- // fleet-passthrough のテストが「型で宣言したフィールドを build.ts が受け渡しているか」を
561
- // `名前:` の字面で走査しており、短縮記法にするとその守りの外へ静かに落ちる)
562
- return {
563
- path: wcStr(row.path),
564
- title: title,
565
- body: wcOptStr(row.body),
566
- };
567
- }
568
- /** 共通フィールドの固定リスト。未知の kind を「汎用行」へ倒すときだけ使う */
569
- function wcCommonRow(kind, row) {
570
- return {
571
- kind: kind,
572
- path: wcOptStr(row.path),
573
- title: wcOptStr(row.title),
574
- type: wcOptStr(row.type),
575
- reason: wcOptStr(row.reason),
576
- why: wcOptStr(row.why),
577
- status: wcOptStr(row.status),
578
- score: wcNum(row.score),
579
- commit: wcOptStr(row.commit),
580
- };
581
- }
582
- /** 精製で書き換えた項目。**field の無い要素は捨てる**(どこを直したのか言えない) */
583
- function wcChanges(v) {
584
- if (!Array.isArray(v))
585
- return [];
586
- const out = [];
587
- for (const c of v) {
588
- if (c == null || typeof c !== "object" || Array.isArray(c))
589
- continue;
590
- const row = c;
591
- const field = wcStr(row.field);
592
- if (!field)
593
- continue;
594
- out.push({
595
- field: field,
596
- before: wcStr(row.before),
597
- after: wcStr(row.after),
598
- });
599
- }
600
- return out;
601
- }
602
- /** 根拠。gold-proposals と同じ形(source 必須・quote は公開範囲フィルタで落ちうる) */
603
- function wcEvidence(v) {
604
- if (!Array.isArray(v))
605
- return [];
606
- const out = [];
607
- for (const e of v) {
608
- if (e == null || typeof e !== "object" || Array.isArray(e))
609
- continue;
610
- const row = e;
611
- const source = wcStr(row.source);
612
- if (!source)
613
- continue;
614
- out.push({
615
- source: source,
616
- quote: wcOptStr(row.quote),
617
- });
618
- }
619
- return out;
620
- }
621
- /** 畳んだ表記。文字列だけ通す(オブジェクトが混ざったら [object Object] を画面に出さない) */
622
- function wcFolded(v) {
623
- if (!Array.isArray(v))
624
- return undefined;
625
- const out = v.filter((x) => typeof x === "string" && x !== "");
626
- return out.length ? out : undefined;
627
- }
628
- const WEEKLY_MERGE_KINDS = ["merge-term", "merge-member", "merge-rule"];
629
- const WEEKLY_REFINE_KINDS = ["refine-term", "refine-member"];
630
- const WEEKLY_AUDIT_KINDS = ["tool-discipline-rule", "machine-readable-term", "partial-name-member"];
631
- /** AIが今週やったこと1件。必須が欠けるものは null(呼び側が読み飛ばす) */
632
- function wcAction(v) {
633
- if (v == null || typeof v !== "object" || Array.isArray(v))
634
- return null;
635
- const row = v;
636
- const kind = wcStr(row.kind);
637
- if (!kind)
638
- return null;
639
- if (WEEKLY_MERGE_KINDS.includes(kind)) {
640
- const winner = wcRef(row.winner);
641
- const loser = wcRef(row.loser);
642
- // **どちらのタイトルも要る。** 「『X』を『Y』に統合」の主語述語がそろわないと文にならない
643
- if (!winner || !loser)
644
- return null;
645
- return {
646
- kind: kind,
647
- winner: winner,
648
- loser: loser,
649
- folded: wcFolded(row.folded),
650
- reason: wcOptStr(row.reason),
651
- commit: wcOptStr(row.commit),
652
- };
653
- }
654
- if (WEEKLY_REFINE_KINDS.includes(kind)) {
655
- const title = wcStr(row.title);
656
- const changes = wcChanges(row.changes);
657
- // **どこを直したのか1件も言えないなら出さない。** 「精製しました」だけの行は、
658
- // 見た人が確かめようがなく、確かめられない報告は不信の材料にしかならない
659
- if (!title || !changes.length)
660
- return null;
661
- return {
662
- kind: kind,
663
- path: wcOptStr(row.path),
664
- title: title,
665
- changes: changes,
666
- evidence: wcEvidence(row.evidence),
667
- commit: wcOptStr(row.commit),
668
- };
669
- }
670
- // 未知の kind。**捨てずに汎用行へ倒す**(契約の指示)。engine が先に kind を増やしても
671
- // 「何かをやったこと」自体は画面に残る——黙って消えるより、読めないまま出るほうがよい
672
- return wcCommonRow(kind, row);
673
- }
674
- /** 人に判断してほしい残り1件。必須が欠けるものは null(呼び側が読み飛ばす) */
675
- function wcAttention(v) {
676
- if (v == null || typeof v !== "object" || Array.isArray(v))
677
- return null;
678
- const row = v;
679
- const kind = wcStr(row.kind);
680
- if (!kind)
681
- return null;
682
- if (kind === "duplicate-cluster" || kind === "demoted-merge") {
683
- const members = Array.isArray(row.members)
684
- ? row.members.map(wcRef).filter((m) => m != null)
685
- : [];
686
- // **2件そろわないと「組」にならない。** 1件だけの重複クラスタは判断材料にならず、
687
- // 「AIで適用」の統合プロンプトも対象が1つでは成り立たない
688
- if (members.length < 2)
689
- return null;
690
- return {
691
- kind: kind,
692
- type: wcOptStr(row.type),
693
- // 一致度は**欠けても行を残す**。数値が出せないだけで、重複の疑い自体は伝える価値がある
694
- score: wcNum(row.score),
695
- members: members,
696
- reason: wcOptStr(row.reason),
697
- };
698
- }
699
- if (WEEKLY_AUDIT_KINDS.includes(kind)) {
700
- const title = wcStr(row.title);
701
- if (!title)
702
- return null;
703
- return {
704
- kind: kind,
705
- path: wcOptStr(row.path),
706
- title: title,
707
- why: wcOptStr(row.why),
708
- status: wcOptStr(row.status),
709
- };
710
- }
711
- return wcCommonRow(kind, row);
712
- }
713
- /** 集計。契約の9キーを**数値だけ**通す(それ以外のキー・非数値は落とす) */
714
- function wcStats(v) {
715
- if (v == null || typeof v !== "object" || Array.isArray(v))
716
- return undefined;
717
- const row = v;
718
- const stats = {
719
- mergedTerms: wcNum(row.mergedTerms),
720
- mergedMembers: wcNum(row.mergedMembers),
721
- mergedRules: wcNum(row.mergedRules),
722
- refinedTerms: wcNum(row.refinedTerms),
723
- proposedTerms: wcNum(row.proposedTerms),
724
- refinedMembers: wcNum(row.refinedMembers),
725
- proposedMembers: wcNum(row.proposedMembers),
726
- attentions: wcNum(row.attentions),
727
- deferredByBudget: wcNum(row.deferredByBudget),
728
- };
729
- // **有効な数が1つも無ければキーごと落とす**(secretSpaces と同じ理由。空の器を通すと、
730
- // 画面が「確かめたうえで0件」と読む余地を作る——知らないことは、キーが無いことで表す)
731
- for (const n of Object.values(stats))
732
- if (n != null)
733
- return stats;
734
- return undefined;
735
- }
736
- /**
737
- * 週次整理レポート(`.cortex/weekly-curation-report.json`)を読む。
738
- *
739
- * **無い・壊れているときは null(表示面なし)で正常継続する。** 週次キュレーションを
740
- * 回していない案件のほうが多い時期なので、無いことは既定の状態であって異常ではない。
741
- * これが読めないことでビューアのビルドまで止めるのは釣り合わない。
742
- */
743
- async function readWeeklyCuration(repoRoot) {
744
- let json;
745
- try {
746
- const raw = await node_fs_1.promises.readFile(path.join(repoRoot, ".cortex", "weekly-curation-report.json"), "utf8");
747
- json = JSON.parse(raw);
748
- }
749
- catch (e) {
750
- // 「まだ週次キュレーションを回していない」が既定の状態なので、無いことは警告しない。
751
- // **壊れている(JSON として読めない)ときだけ**残す——静かに0件になると、
752
- // 節が出ない理由が「作られていない」のか「読めていない」のか切り分けられない
753
- if (e?.code !== "ENOENT") {
754
- console.warn(`weekly-curation-report.json を読めませんでした(週次整理は出さずに続けます): ${String(e)}`);
755
- }
756
- return null;
757
- }
758
- if (json == null || typeof json !== "object" || Array.isArray(json)) {
759
- console.warn("weekly-curation-report.json の形式が想定と違います(週次整理は出さずに続けます)");
760
- return null;
761
- }
762
- const obj = json;
763
- // **版が違うものは読まない**(gold-proposals.json と同じ規律)。engine 側が構造を
764
- // 変えたときに、古い読み手が部分的に解釈して中途半端な報告を出すより、出さないほうがよい
765
- if (obj.version !== 1) {
766
- console.warn(`weekly-curation-report.json の version が未対応です(週次整理は出さずに続けます): ${String(obj.version)}`);
767
- return null;
768
- }
769
- const actions = [];
770
- if (Array.isArray(obj.actions)) {
771
- for (const v of obj.actions) {
772
- const act = wcAction(v);
773
- // 必須が欠ける1件のために報告ごと落とさない(エントリ単位の読み飛ばしは正常系なので黙る)
774
- if (act)
775
- actions.push(act);
776
- }
777
- }
778
- const attentions = [];
779
- if (Array.isArray(obj.attentions)) {
780
- for (const v of obj.attentions) {
781
- const att = wcAttention(v);
782
- if (att)
783
- attentions.push(att);
784
- }
785
- }
786
- return {
787
- ranAt: wcStr(obj.ranAt),
788
- actions: actions,
789
- attentions: attentions,
790
- stats: wcStats(obj.stats),
791
- };
792
- }
793
522
  // ---- Home(案件の自己記述)--------------------------------------------------
794
523
  //
795
524
  // Home.md は「案件そのものの説明」に絞る再構成の途中にある(engine 側で進行中)。
@@ -1893,8 +1622,11 @@ async function build(opts) {
1893
1622
  // すでにある会議(投入モーダルの「会議名」の選択肢)。intake の有無に関わらず載せる——
1894
1623
  // 出し分けは表示側の仕事で、ここで絞ると「投入の口を後から付けたら選択肢が空」になる
1895
1624
  const meetings = await collectMeetings(repoRoot);
1896
- // 週次整理レポート。無ければ null で、操作タブの節を出さない
1897
- const weeklyCuration = await readWeeklyCuration(repoRoot);
1625
+ // AIによるレコードの変更履歴を git から取る。**レコード数に関わらず git は2回だけ呼ぶ**。
1626
+ // git が無い・浅い clone・該当0件なら空で、履歴の節が出ないだけ(他の表示は壊れない)。
1627
+ // 走るのは latestGeneratedPaths と同じ **リポジトリルート**(process.cwd())——
1628
+ // Decisions ディレクトリで実行すると pathspec に一致せず常に空になる
1629
+ const aiHistory = (0, repo_1.collectAiChangeHistory)(process.cwd());
1898
1630
  const site = {
1899
1631
  title: opts.title,
1900
1632
  generatedAt: opts.generatedAt,
@@ -1939,7 +1671,8 @@ async function build(opts) {
1939
1671
  backlogTarget: fleet.backlogTarget,
1940
1672
  // null(無い・壊れ・version違い)は undefined へ落とす——SiteData の
1941
1673
  // 「知らないことはキーが無いことで表す」に合わせる(空の器を通さない)
1942
- weeklyCuration: weeklyCuration ?? undefined,
1674
+ // 空の器は通さない(0件なら undefined = キーごと出さない)
1675
+ aiHistory: Object.keys(aiHistory).length ? aiHistory : undefined,
1943
1676
  };
1944
1677
  // サムネイル(デザイン画面等)をサイトに同梱し、ノードのパスをサイト内相対に書き換える
1945
1678
  await node_fs_1.promises.mkdir(outAbs, { recursive: true });