@takagaki/cortex-decisions-viewer 0.12.85 → 0.12.87
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 +219 -59
- package/dist/engagement.js +4 -1
- package/dist/external-id.js +2 -2
- package/dist/rederivation.js +77 -109
- package/dist/render.js +101 -13
- package/dist/repo.js +1 -70
- package/dist/supersedes.js +112 -0
- package/package.json +1 -1
- package/dist/commit-classify.js +0 -157
package/dist/build.js
CHANGED
|
@@ -36,16 +36,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.PROJECT_ICON_EXTS = void 0;
|
|
39
|
+
exports.PROJECT_ICON_EXTS = exports.MIRROR_RELS = void 0;
|
|
40
40
|
exports.sanitizeBody = sanitizeBody;
|
|
41
41
|
exports.findProjectIcon = findProjectIcon;
|
|
42
42
|
exports.readFleetSources = readFleetSources;
|
|
43
43
|
exports.attachRederived = attachRederived;
|
|
44
44
|
exports.attachDecisionFollowup = attachDecisionFollowup;
|
|
45
|
+
exports.rawFrontmatterDate = rawFrontmatterDate;
|
|
46
|
+
exports.attachSupersedes = attachSupersedes;
|
|
45
47
|
exports.readGoldProposals = readGoldProposals;
|
|
46
48
|
exports.buildHomeGrounding = buildHomeGrounding;
|
|
47
49
|
exports.collectMeetings = collectMeetings;
|
|
48
50
|
exports.findBacklogDir = findBacklogDir;
|
|
51
|
+
exports.figmaUrlFromRef = figmaUrlFromRef;
|
|
49
52
|
exports.bodyFileSlug = bodyFileSlug;
|
|
50
53
|
exports.bodyStampId = bodyStampId;
|
|
51
54
|
exports.bodyStampComment = bodyStampComment;
|
|
@@ -97,6 +100,7 @@ function takeProposals(filePath) {
|
|
|
97
100
|
}
|
|
98
101
|
const external_id_1 = require("./external-id");
|
|
99
102
|
const home_project_1 = require("./home-project");
|
|
103
|
+
const supersedes_1 = require("./supersedes");
|
|
100
104
|
const render_1 = require("./render");
|
|
101
105
|
/**
|
|
102
106
|
* この画面を作ったパッケージの版。**package.json を唯一の出どころにする。**
|
|
@@ -113,6 +117,11 @@ const PKG_VERSION = (() => {
|
|
|
113
117
|
}
|
|
114
118
|
})();
|
|
115
119
|
const DEFAULT_EXCLUDE = ["README.md", "template.md"];
|
|
120
|
+
/**
|
|
121
|
+
* グラフ・エクスプローラーに描かない関係(#699 F3)。`superseded_by` は engine の夜間の精製が古い側に書く
|
|
122
|
+
* `supersedes` の写しで、描くと同じ置き換えが逆向きにもう 1 本出る。frontmatter のパネルには今までどおり出る
|
|
123
|
+
*/
|
|
124
|
+
exports.MIRROR_RELS = new Set(["superseded_by"]);
|
|
116
125
|
/** frontmatterのrelationsを安全に正規化する */
|
|
117
126
|
function toRelations(value) {
|
|
118
127
|
if (!Array.isArray(value))
|
|
@@ -707,30 +716,35 @@ readBlobs = repo_1.readBlobsAt) {
|
|
|
707
716
|
// 🔴 **「確認済み」は `status` だけで決まる**(#667)。帯も並びも同じ `rstatus` を見る
|
|
708
717
|
// ——ここで別のものを渡すと、画面の札と食い違う(「未確認」の札が付いた記録に帯が出る)
|
|
709
718
|
const statusOf = (r) => r.rstatus;
|
|
710
|
-
// 決定表の行
|
|
711
|
-
//
|
|
719
|
+
// 決定表の行 5〜7 の材料。**帯の候補になったレコードのぶんだけ**今の版を読み、指紋を作る。
|
|
720
|
+
// 帳簿に人が確認した版の指紋が無い候補だけ、`<基準コミット>:<path>` をまとめて 1 回 git から引く。
|
|
712
721
|
// 🔴 **キーが 0 件の区画では git を起こさない**(この関数は区画ごとに呼ばれる)
|
|
713
722
|
const keyOf = new Map();
|
|
714
723
|
// 今の版は**作業ツリーから読む**——画面が描いているのはこのファイルであって HEAD ではない
|
|
715
724
|
const now = new Map();
|
|
725
|
+
const nowSha = new Map();
|
|
716
726
|
for (const r of records) {
|
|
717
727
|
const key = (0, rederivation_1.rederivedContentKey)(pathOf(r), statusOf(r), ctx.ledgers);
|
|
718
728
|
if (!key)
|
|
719
729
|
continue;
|
|
720
730
|
const p = pathOf(r);
|
|
731
|
+
let nowRaw;
|
|
721
732
|
try {
|
|
722
|
-
|
|
733
|
+
nowRaw = await node_fs_1.promises.readFile(path.join(ctx.repoRoot, p), "utf8");
|
|
723
734
|
}
|
|
724
735
|
catch {
|
|
725
|
-
//
|
|
736
|
+
// 読めなければ「読めなかった」側(`rederivedBandOf` が帯を出さずに数える)
|
|
737
|
+
continue;
|
|
726
738
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
if (
|
|
739
|
+
now.set(p, nowRaw);
|
|
740
|
+
const sha = (0, rederivation_1.contentShaOf)(nowRaw);
|
|
741
|
+
nowSha.set(p, sha);
|
|
742
|
+
const conf = ctx.ledgers.confirmation.get(p);
|
|
743
|
+
// 🔴 **今の版が機械の書き直しの版でなければ git を読まない**(帯は出ないと決まっている)。
|
|
744
|
+
// 人が確認した版の指紋が帳簿にあれば、それで決まるので git を読まない(engine #546)
|
|
745
|
+
if (sha !== conf?.rewritten?.contentSha)
|
|
746
|
+
continue;
|
|
747
|
+
if (conf.contentSha) {
|
|
734
748
|
if (ctx.blobs)
|
|
735
749
|
ctx.blobs.skipped++;
|
|
736
750
|
continue;
|
|
@@ -742,28 +756,18 @@ readBlobs = repo_1.readBlobsAt) {
|
|
|
742
756
|
const bases = keyOf.size ? readBlobs(ctx.repoRoot, [...keyOf.values()]) : new Map();
|
|
743
757
|
const verdict = (p, baseCommit) => {
|
|
744
758
|
const nowRaw = now.get(p);
|
|
745
|
-
if (nowRaw == null) {
|
|
746
|
-
ctx.miss.unreadable.add(p);
|
|
747
|
-
return "unknown";
|
|
748
|
-
}
|
|
749
|
-
// 🔴 **帳簿が中身の指紋を持つならそれを先に使う**(engine #546 が書く。一致した記録は
|
|
750
|
-
// 上で git の key から外してある)。
|
|
751
|
-
// 一致したときだけ「変わっていない」と言い、食い違ったら git の二段目へ落とす——
|
|
752
|
-
// engine が別の綴りで指紋を作っていた場合に、全レコードへ誤報が出るのを避ける
|
|
753
|
-
const conf = ctx.ledgers.confirmation.get(p);
|
|
754
|
-
if (conf && conf.contentSha && conf.contentSha === (0, rederivation_1.contentShaOf)(nowRaw))
|
|
755
|
-
return "same";
|
|
756
759
|
const baseRaw = bases.get(`${baseCommit}:${p}`);
|
|
757
|
-
if (baseRaw == null) {
|
|
760
|
+
if (nowRaw == null || baseRaw == null) {
|
|
758
761
|
// 浅い clone の窓の外・確認の後に改名された・基準コミットにこの path が無い。
|
|
759
|
-
//
|
|
762
|
+
// **帯は出さない**(誤報より無言)が、静かに消える経路なのでログの器に数える
|
|
760
763
|
ctx.miss.unreadable.add(p);
|
|
761
764
|
return "unknown";
|
|
762
765
|
}
|
|
763
766
|
return (0, rederivation_1.sameRederivedContent)(baseRaw, nowRaw) ? "same" : "changed";
|
|
764
767
|
};
|
|
768
|
+
const nowShaOf = (p) => nowSha.get(p) ?? null;
|
|
765
769
|
for (const r of records) {
|
|
766
|
-
const band = (0, rederivation_1.rederivedBandOf)(pathOf(r), statusOf(r), ctx.ledgers,
|
|
770
|
+
const band = (0, rederivation_1.rederivedBandOf)(pathOf(r), statusOf(r), ctx.ledgers, nowShaOf, verdict, ctx.miss);
|
|
767
771
|
// **印が立たないレコードにはキーごと付けない**(空の器を通さない)
|
|
768
772
|
if (band)
|
|
769
773
|
r.rederived = band;
|
|
@@ -795,6 +799,78 @@ function attachDecisionFollowup(decisions, recordsDir, repoRoot, ledger) {
|
|
|
795
799
|
}
|
|
796
800
|
return [...ledger.keys()].filter((k) => !matched.has(k.normalize("NFC")));
|
|
797
801
|
}
|
|
802
|
+
/**
|
|
803
|
+
* 決定に置き換えの逆引き(置き換え先・置き換えた決定)を載せる(cortex-engine #699 F3・A-1')。
|
|
804
|
+
*
|
|
805
|
+
* 🔴 **逆引きは `supersedes.ts`(engine `scripts/gold/supersedes-index.mjs` の写し)だけで組む**。
|
|
806
|
+
* 材料は新しい側の `relations[supersedes]` だけで、古い側の `superseded_by`(夜間の精製が書く写し)は
|
|
807
|
+
* 読まない。確認済み=新しい側の `status` が `active`。
|
|
808
|
+
*
|
|
809
|
+
* 置き換えが無い決定には**キーごと付けない**(今までと同じ SiteData)
|
|
810
|
+
*/
|
|
811
|
+
/**
|
|
812
|
+
* frontmatter に**書かれたままの** `date` の値(トップレベルの `date:` の行・引用符と行末のコメントを外す)。
|
|
813
|
+
* 無い・frontmatter が無ければ undefined。置き換えの逆引きの決定日だけに使う(engine と同じ日付にするため)
|
|
814
|
+
*/
|
|
815
|
+
function rawFrontmatterDate(raw) {
|
|
816
|
+
const m = String(raw ?? "").replace(/^\uFEFF/, "").match(/^---[ \t]*\r?\n([\s\S]*?)\r?\n---[ \t]*(?:\r?\n|$)/);
|
|
817
|
+
if (!m)
|
|
818
|
+
return undefined;
|
|
819
|
+
const line = m[1].match(/^date:[ \t]*(.*)$/m);
|
|
820
|
+
if (!line)
|
|
821
|
+
return undefined;
|
|
822
|
+
let v = line[1].replace(/\r$/, "").trim();
|
|
823
|
+
const q = v.match(/^(["'])(.*)\1(?:\s+#.*)?$/);
|
|
824
|
+
if (q)
|
|
825
|
+
return q[2];
|
|
826
|
+
v = v.replace(/\s+#.*$/, "").trim();
|
|
827
|
+
return v;
|
|
828
|
+
}
|
|
829
|
+
function attachSupersedes(decisions, rawDateOf = (d) => d.fm.date) {
|
|
830
|
+
const input = decisions.map((d) => ({
|
|
831
|
+
id: d.id,
|
|
832
|
+
// 🔴 **frontmatter に書かれたままの date の文字列**(`rawFrontmatterDate`)。gray-matter は時刻・時差つきの
|
|
833
|
+
// date を Date に読み、sanitizeFm は UTC の日付にするので、`2026-07-01T01:00:00+09:00` が 6/30 にずれる。
|
|
834
|
+
// engine は日付を Date にしない読み方(CORE_SCHEMA)で書かれた文字列の先頭 8 桁を使う。
|
|
835
|
+
// 無ければ写しが ID の先頭 8 桁から取る
|
|
836
|
+
date: rawDateOf(d),
|
|
837
|
+
status: d.fm.status,
|
|
838
|
+
supersedes: d.relations.filter((r) => r.rel === "supersedes").map((r) => r.target),
|
|
839
|
+
}));
|
|
840
|
+
const index = (0, supersedes_1.supersedesIndex)(input);
|
|
841
|
+
const idOfKey = new Map();
|
|
842
|
+
for (const d of decisions) {
|
|
843
|
+
const k = (0, supersedes_1.decisionKeyOf)(d.id);
|
|
844
|
+
if (!idOfKey.has(k))
|
|
845
|
+
idOfKey.set(k, d.id);
|
|
846
|
+
}
|
|
847
|
+
for (const d of decisions) {
|
|
848
|
+
const key = (0, supersedes_1.decisionKeyOf)(d.id);
|
|
849
|
+
const entry = index.get(key);
|
|
850
|
+
if (entry && entry.by.length) {
|
|
851
|
+
d.supersededBy = {
|
|
852
|
+
by: entry.by.map((b) => ({ id: b.id, date: b.date, confirmed: b.confirmed })),
|
|
853
|
+
retired: entry.retired,
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
const replaced = [];
|
|
857
|
+
for (const r of d.relations) {
|
|
858
|
+
if (r.rel !== "supersedes")
|
|
859
|
+
continue;
|
|
860
|
+
const k = (0, supersedes_1.decisionKeyOf)(r.target);
|
|
861
|
+
// engine の規則と同じく、自分自身・実在しない決定は数えない
|
|
862
|
+
if (!k || k === key)
|
|
863
|
+
continue;
|
|
864
|
+
const id = idOfKey.get(k);
|
|
865
|
+
if (id === undefined)
|
|
866
|
+
continue;
|
|
867
|
+
if (!replaced.includes(id))
|
|
868
|
+
replaced.push(id);
|
|
869
|
+
}
|
|
870
|
+
if (replaced.length)
|
|
871
|
+
d.supersedesIds = replaced;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
798
874
|
/**
|
|
799
875
|
* AIからの更新提案(`.cortex/gold-proposals.json`)を読む。
|
|
800
876
|
*
|
|
@@ -1373,16 +1449,28 @@ async function buildTargetUrlMap(repoRoot, repoBaseUrl, branch) {
|
|
|
1373
1449
|
// - 参照ID: `design:xxx:103:1836`
|
|
1374
1450
|
// - [Figmaで開く](https://www.figma.com/design/...)
|
|
1375
1451
|
// 以前は `data.type === "design"` を見ていたので、この解決は**一度も効いていなかった**。
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1452
|
+
//
|
|
1453
|
+
// #727 K10 で、コメントだけを取り込む案件(栓 `design_comments_only`)は inventory を持たず、
|
|
1454
|
+
// `デザイン/指摘/<ファイル>/README.md`(`design:{fileKey}` と「Figmaで開く」)と
|
|
1455
|
+
// 月ごとの `YYYY-MM.md`(スレッドごとの `figma-comment:` と「Figmaでこの指摘を開く」)になる。
|
|
1456
|
+
// 移行期は両方ありうるので**両方歩く**。inventory を先に歩き、同じ ID は先勝ち
|
|
1457
|
+
// (=inventory があれば今までと同じ URL。1バイトも変えない)。
|
|
1458
|
+
// 表に無い `design:` / `figma-comment:` は `figmaUrlFromRef` がキーから組む(解決器の側)。
|
|
1459
|
+
for (const sub of ["inventory", "指摘"]) {
|
|
1460
|
+
for (const file of await walkMd(path.join(repoRoot, designDir, sub))) {
|
|
1461
|
+
try {
|
|
1462
|
+
const body = await node_fs_1.promises.readFile(file, "utf8");
|
|
1463
|
+
const id = body.match(/^- 参照ID: `(design:[^`]+)`\s*$/m);
|
|
1464
|
+
const url = body.match(/^- \[Figmaで開く\]\((https?:[^)]+)\)\s*$/m);
|
|
1465
|
+
if (id && url && !map.has(id[1]))
|
|
1466
|
+
map.set(id[1], url[1]);
|
|
1467
|
+
for (const [cid, curl] of figmaCommentLinks(body))
|
|
1468
|
+
if (!map.has(cid))
|
|
1469
|
+
map.set(cid, curl);
|
|
1470
|
+
}
|
|
1471
|
+
catch {
|
|
1472
|
+
// 読めないファイルはスキップ
|
|
1473
|
+
}
|
|
1386
1474
|
}
|
|
1387
1475
|
}
|
|
1388
1476
|
// minute:{定例名}:{YYYYMMDD} → 議事録ファイルのGitHubリンク
|
|
@@ -1405,6 +1493,70 @@ async function buildTargetUrlMap(repoRoot, repoBaseUrl, branch) {
|
|
|
1405
1493
|
}
|
|
1406
1494
|
return map;
|
|
1407
1495
|
}
|
|
1496
|
+
/**
|
|
1497
|
+
* 指摘のスレッドから `figma-comment:{fileKey}:{commentId}` → 「Figmaでこの指摘を開く」の URL を拾う。
|
|
1498
|
+
*
|
|
1499
|
+
* engine の `render_thread` の形(`### 未解決: …` の見出し・`- 参照ID:`・`- [Figmaでこの指摘を開く](…)`)。
|
|
1500
|
+
* **対は同じスレッドの中でだけ組む。** 次の `### ` 見出しを越えて拾うと、リンクの欠けた
|
|
1501
|
+
* スレッドに隣のスレッドの URL を付けてしまう(押すと別の指摘へ飛ぶ——エラーの出ない壊れ方)。
|
|
1502
|
+
*/
|
|
1503
|
+
function figmaCommentLinks(body) {
|
|
1504
|
+
const out = [];
|
|
1505
|
+
let pending = null;
|
|
1506
|
+
for (const line of body.split("\n")) {
|
|
1507
|
+
if (/^#{1,3}\s/.test(line)) {
|
|
1508
|
+
pending = null;
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
const id = line.match(/^- 参照ID: `(figma-comment:[^`]+)`\s*$/);
|
|
1512
|
+
if (id) {
|
|
1513
|
+
pending = id[1];
|
|
1514
|
+
continue;
|
|
1515
|
+
}
|
|
1516
|
+
const url = line.match(/^- \[Figmaでこの指摘を開く\]\((https?:[^)]+)\)\s*$/);
|
|
1517
|
+
if (url && pending) {
|
|
1518
|
+
out.push([pending, url[1]]);
|
|
1519
|
+
pending = null;
|
|
1520
|
+
}
|
|
1521
|
+
}
|
|
1522
|
+
return out;
|
|
1523
|
+
}
|
|
1524
|
+
/** Figma のファイルキー(engine `sync_designs.py` の `REAL_FIGMA_KEY` と同じ形) */
|
|
1525
|
+
const FIGMA_KEY = /^[A-Za-z0-9_-]{8,}$/;
|
|
1526
|
+
/** Figma のノード ID(`103:1836`。URL では `103-1836` と書く) */
|
|
1527
|
+
const FIGMA_NODE = /^\d+:\d+$/;
|
|
1528
|
+
/**
|
|
1529
|
+
* `design:` / `figma-comment:` の参照から、**キーとノードだけで** Figma の URL を組む(#727 K10)。
|
|
1530
|
+
*
|
|
1531
|
+
* コメントだけを取り込む案件には画面ごとの inventory が無いので、Gold の
|
|
1532
|
+
* `design:{fileKey}:{nodeId}` を表から引けない。画面の本体は Figma にあるので、キーとノードが
|
|
1533
|
+
* 揃っていれば URL は決まる(スラグは Figma が読まないので付けない)。
|
|
1534
|
+
* - `design:{key}:{node}` → `https://www.figma.com/design/{key}?node-id={node の : を -}`
|
|
1535
|
+
* - `design:{key}` → `https://www.figma.com/design/{key}`
|
|
1536
|
+
* - `figma-comment:{key}:{id}` → `https://www.figma.com/design/{key}#{id}`
|
|
1537
|
+
* (フラグメントはサーバーに送られないので、外れてもファイルには着く。engine の `comment_link` と同じ倒し方)
|
|
1538
|
+
*
|
|
1539
|
+
* 🔴 **形の合わないものは組まない(null)。** キー・ノードに任意の文字列を通すと、
|
|
1540
|
+
* `design:../../x` や `design:abc:1:2?x=` から**押せそうで 404** の URL ができる。
|
|
1541
|
+
* 壊れたリンクは、無いより悪い。
|
|
1542
|
+
*/
|
|
1543
|
+
function figmaUrlFromRef(ref) {
|
|
1544
|
+
const d = ref.match(/^design:([^:]+)(?::(.+))?$/);
|
|
1545
|
+
if (d) {
|
|
1546
|
+
const [, key, node] = d;
|
|
1547
|
+
if (!FIGMA_KEY.test(key))
|
|
1548
|
+
return null;
|
|
1549
|
+
if (node === undefined)
|
|
1550
|
+
return `https://www.figma.com/design/${key}`;
|
|
1551
|
+
if (!FIGMA_NODE.test(node))
|
|
1552
|
+
return null;
|
|
1553
|
+
return `https://www.figma.com/design/${key}?node-id=${node.replace(":", "-")}`;
|
|
1554
|
+
}
|
|
1555
|
+
const c = ref.match(/^figma-comment:([^:]+):(\d+)$/);
|
|
1556
|
+
if (c && FIGMA_KEY.test(c[1]))
|
|
1557
|
+
return `https://www.figma.com/design/${c[1]}#${c[2]}`;
|
|
1558
|
+
return null;
|
|
1559
|
+
}
|
|
1408
1560
|
/** 画像・添付など、グラフのノードにしないリンクの拡張子 */
|
|
1409
1561
|
const NON_DOC_LINK = /\.(png|jpe?g|gif|svg|webp|ico|zip|mp4|mov|heic)([?#]|$)/i;
|
|
1410
1562
|
/** 本文markdownからhttp(s)リンクを抽出する(重複除去・画像等は除外) */
|
|
@@ -1509,7 +1661,8 @@ async function scanContent(repoRoot, goldDirName, repoBaseUrl, branch) {
|
|
|
1509
1661
|
}
|
|
1510
1662
|
}
|
|
1511
1663
|
for (const r of toRelations(data.relations))
|
|
1512
|
-
|
|
1664
|
+
if (!exports.MIRROR_RELS.has(r.rel))
|
|
1665
|
+
edges.push({ target: r.target, rel: r.rel });
|
|
1513
1666
|
}
|
|
1514
1667
|
else {
|
|
1515
1668
|
const keyMatch = raw.slice(0, 4000).match(/^- 課題キー: (\S+)$/m);
|
|
@@ -1586,9 +1739,13 @@ function buildGraph(decisions, home, sections, content, targetUrls, repoBaseUrl,
|
|
|
1586
1739
|
return resolved;
|
|
1587
1740
|
let group = "phantom";
|
|
1588
1741
|
let label = target;
|
|
1589
|
-
if (target.startsWith("minute:") ||
|
|
1742
|
+
if (target.startsWith("minute:") ||
|
|
1743
|
+
target.startsWith("material:") ||
|
|
1744
|
+
target.startsWith("design:") ||
|
|
1745
|
+
target.startsWith("figma-comment:")) {
|
|
1590
1746
|
group = "silver";
|
|
1591
|
-
|
|
1747
|
+
// 表に無い design: / figma-comment: はキーから組む(詳細画面の resolve と同じ順)
|
|
1748
|
+
const url = targetUrls.get(target) ?? figmaUrlFromRef(target);
|
|
1592
1749
|
if (url)
|
|
1593
1750
|
return addNode({ id: target, label, group, url });
|
|
1594
1751
|
}
|
|
@@ -1618,6 +1775,9 @@ function buildGraph(decisions, home, sections, content, targetUrls, repoBaseUrl,
|
|
|
1618
1775
|
};
|
|
1619
1776
|
const addRelationEdges = (fromId, relations) => {
|
|
1620
1777
|
for (const r of relations) {
|
|
1778
|
+
// 🔴 `superseded_by` は描かない(#699 F3)。新しい側の `supersedes` の写しで、描くと同じ置き換えが 2 本になる
|
|
1779
|
+
if (exports.MIRROR_RELS.has(r.rel))
|
|
1780
|
+
continue;
|
|
1621
1781
|
edges.push({ from: fromId, to: classifyTarget(r.target), rel: r.rel });
|
|
1622
1782
|
}
|
|
1623
1783
|
};
|
|
@@ -2043,8 +2203,8 @@ async function build(opts) {
|
|
|
2043
2203
|
// 同一プロセスで2回ビルドしても前回の値が残らないよう、毎回入れ替える
|
|
2044
2204
|
PROPOSALS_BY_PATH = await readGoldProposals(repoRoot);
|
|
2045
2205
|
PROPOSAL_PATHS_MATCHED = new Set();
|
|
2046
|
-
// 再導出の 3 帳簿(#665 便 12b
|
|
2047
|
-
//
|
|
2206
|
+
// 再導出の 3 帳簿(#665 便 12b)。**帳簿が無い案件では帯も並び替えも作らない**。
|
|
2207
|
+
// 人か機械かの見分けは engine が帳簿(`rewritten`)に書くので、画面は git の素性を読まない(#799)
|
|
2048
2208
|
const ledgers = await (0, rederivation_1.readRederivationLedgers)(repoRoot);
|
|
2049
2209
|
const rederive = ledgers.confirmation.size || ledgers.activity.size
|
|
2050
2210
|
? {
|
|
@@ -2052,10 +2212,9 @@ async function build(opts) {
|
|
|
2052
2212
|
// 🔴 **`repoRoot` で走らせる**(`collectAiChangeHistory` の `process.cwd()` に倣わない)。
|
|
2053
2213
|
// あちらは「リポジトリルートで実行すること」が前提の既存の制約だが、こちらは repoRoot を
|
|
2054
2214
|
// 既に知っている(帳簿をそこから読んでいる)。Decisions ディレクトリから CLI を叩いた
|
|
2055
|
-
//
|
|
2215
|
+
// ときも今の版と基準版が引け、帯が黙って消えない
|
|
2056
2216
|
repoRoot: repoRoot,
|
|
2057
|
-
|
|
2058
|
-
miss: { unresolved: new Set(), unreadable: new Set() },
|
|
2217
|
+
miss: { unreadable: new Set() },
|
|
2059
2218
|
blobs: { read: 0, skipped: 0 },
|
|
2060
2219
|
}
|
|
2061
2220
|
: null;
|
|
@@ -2063,11 +2222,18 @@ async function build(opts) {
|
|
|
2063
2222
|
// **読めない・使えないなら `null`** で、関係者の並びは今までと 1 バイトも同じ
|
|
2064
2223
|
const engagement = await (0, engagement_1.readEngagementLedger)(repoRoot);
|
|
2065
2224
|
const decisions = [];
|
|
2225
|
+
const rawDates = new Map();
|
|
2066
2226
|
for (const fileName of mdFiles) {
|
|
2067
2227
|
const raw = await node_fs_1.promises.readFile(path.join(dirAbs, fileName), "utf8");
|
|
2068
2228
|
const rec = await parseDecision(raw, fileName, repoRelDir, repo.baseUrl, repo.branch, refs);
|
|
2069
2229
|
decisions.push(rec);
|
|
2230
|
+
// 置き換えの逆引きの決定日(書かれたままの文字列・SiteData には載せない)
|
|
2231
|
+
const rd = rawFrontmatterDate(raw);
|
|
2232
|
+
if (rd !== undefined)
|
|
2233
|
+
rawDates.set(rec, rd);
|
|
2070
2234
|
}
|
|
2235
|
+
// 置き換えの逆引き(#699 F3)。**置き換えの無い案件では 1 件も付かない**
|
|
2236
|
+
attachSupersedes(decisions, (d) => rawDates.get(d) ?? d.fm.date);
|
|
2071
2237
|
// 決定の「その後」(#705)。**帳簿を持たない案件では 1 件も付かず、絞り込みの印も立たない**
|
|
2072
2238
|
// (`decisionFollowup` はキーごと無い=今までと 1 バイトも同じ画面)
|
|
2073
2239
|
const followupLedger = await (0, rederivation_1.readDecisionFollowup)(repoRoot);
|
|
@@ -2152,30 +2318,20 @@ async function build(opts) {
|
|
|
2152
2318
|
// **突き合わなかった提案の件数をビルドログに出す。** path が1文字違うだけで
|
|
2153
2319
|
// 画面から静かに消えるので、出さないと「提案が出ない」の原因に辿り着けない。
|
|
2154
2320
|
// 表示からは落とす(どのレコードの話か分からない提案は、出しても行動につながらない)
|
|
2155
|
-
// 🔴 **帯が静かに消える経路を 1
|
|
2156
|
-
//
|
|
2157
|
-
// 画面には出さない(利用者が動ける症状ではない)が、ログに無いと「帯が出ない」の原因を
|
|
2158
|
-
// 「夜間がまだ書いていない」と切り分けられない
|
|
2159
|
-
if (rederive && rederive.miss.unresolved.size) {
|
|
2160
|
-
const shas = [...rederive.miss.unresolved];
|
|
2161
|
-
console.warn(`gold-confirmation.json: 変更のコミット ${shas.length} 件を git の履歴で引けませんでした` +
|
|
2162
|
-
`(浅い clone か履歴の窓の外。人か機械か見分けられないので帯は出しません): ` +
|
|
2163
|
-
shas.slice(0, 3).map((s) => s.slice(0, 8)).join(", ") + (shas.length > 3 ? " ほか" : ""));
|
|
2164
|
-
}
|
|
2165
|
-
// 🔴 **印が静かに消えるもう 1 つの経路を 1 行残す。** 人が確認した版の中身を git から
|
|
2166
|
-
// 引けないと「説明文が変わったか」を言えないので、印は出さない(誤報より無言)。
|
|
2321
|
+
// 🔴 **帯が静かに消える経路を 1 行残す。** 今の版か、人が確認した版の中身を
|
|
2322
|
+
// 引けないと「説明文が変わったか」を言えないので、帯は出さない(誤報より無言)。
|
|
2167
2323
|
// 出さないと「印が出ない」の原因を「中身が変わっていない」と切り分けられない
|
|
2168
2324
|
if (rederive && rederive.miss.unreadable.size) {
|
|
2169
2325
|
const paths = [...rederive.miss.unreadable];
|
|
2170
|
-
console.warn(`gold-confirmation.json:
|
|
2171
|
-
`(浅い clone
|
|
2326
|
+
console.warn(`gold-confirmation.json: 人が確認した版か今の版の中身を ${paths.length} 件引けませんでした` +
|
|
2327
|
+
`(浅い clone か、確認の後に改名・削除された。変わったかを言えないので帯は出しません): ` +
|
|
2172
2328
|
paths.slice(0, 3).join(", ") + (paths.length > 3 ? " ほか" : ""));
|
|
2173
2329
|
}
|
|
2174
2330
|
// 🔴 **指紋(engine #546)が効いているかを 1 行残す。** 一致した記録は git を読まないので、
|
|
2175
2331
|
// 「読んだ件数」が候補の数より減っていれば効いている。警告ではないので console.log
|
|
2176
2332
|
if (rederive?.blobs && rederive.blobs.read + rederive.blobs.skipped > 0) {
|
|
2177
2333
|
console.log(`gold-confirmation.json: 人が確認した版の中身を git から ${rederive.blobs.read} 件読みました` +
|
|
2178
|
-
|
|
2334
|
+
`(中身の指紋で決めた ${rederive.blobs.skipped} 件は読んでいません)`);
|
|
2179
2335
|
}
|
|
2180
2336
|
const unmatched = [...PROPOSALS_BY_PATH.keys()].filter((k) => !PROPOSAL_PATHS_MATCHED.has(k));
|
|
2181
2337
|
if (unmatched.length) {
|
|
@@ -2399,6 +2555,10 @@ function createRefResolver(opts) {
|
|
|
2399
2555
|
const known = targetUrls.get(trimmed);
|
|
2400
2556
|
if (known)
|
|
2401
2557
|
return { label: trimmed, url: known };
|
|
2558
|
+
// inventory の無い案件(#727 K10)の design: / figma-comment: は、キーとノードから組む
|
|
2559
|
+
const figma = figmaUrlFromRef(trimmed);
|
|
2560
|
+
if (figma)
|
|
2561
|
+
return { label: trimmed, url: figma };
|
|
2402
2562
|
// 実在するファイルだけ blob URL にする
|
|
2403
2563
|
if (repoBaseUrl && exists(trimmed)) {
|
|
2404
2564
|
return { label: trimmed, url: `${repoBaseUrl}/blob/${branch}/${encodePath(trimmed)}` };
|
package/dist/engagement.js
CHANGED
|
@@ -50,7 +50,7 @@ exports.ENGAGEMENT_LEDGER_VERSION = 1;
|
|
|
50
50
|
/** 半減期(日)。engine `weights.mjs` の `HALF_LIFE_DAYS`(正本 6-B「半減期 90 日固定」) */
|
|
51
51
|
exports.HALF_LIFE_DAYS = 90;
|
|
52
52
|
/**
|
|
53
|
-
* 行為 1 件の重み。engine `weights.mjs` の `WEIGHTS` の写し(
|
|
53
|
+
* 行為 1 件の重み。engine `weights.mjs` の `WEIGHTS` の写し(24 値・**表に無い `kind` は 0**)。
|
|
54
54
|
*
|
|
55
55
|
* 🔴 契約の表(fixture)は `message.posted` しか使っていないので、**ここの写し間違いは表では
|
|
56
56
|
* 捕まらない**。重みの番人は `test/member-engagement-parity.test.mjs` の `[重み]` が持つ。
|
|
@@ -80,6 +80,9 @@ exports.WEIGHTS = Object.freeze({
|
|
|
80
80
|
"record.deleted": 0,
|
|
81
81
|
// 本文を取り込んだ(engine #695)。人の行為ではないので 0(話した人は `utterance.spoken` で数える)
|
|
82
82
|
"body.captured": 0,
|
|
83
|
+
// 人の宣言の指紋(cortex-engine #699 F1)。宣言の取り込みが書く行で、関与には数えない(0)。
|
|
84
|
+
// 場は宣言専用(`decl:<owner/repo>`)で帳簿にもともと入らないが、網羅のため写す
|
|
85
|
+
"decl.recorded": 0,
|
|
83
86
|
});
|
|
84
87
|
const DAY_MS = 86400000;
|
|
85
88
|
const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
package/dist/external-id.js
CHANGED
|
@@ -106,8 +106,8 @@ function splitTypedId(v) {
|
|
|
106
106
|
* 両方がこれを引く。
|
|
107
107
|
*
|
|
108
108
|
* 🔴 画面の中では `identities` と呼ばない(`linkedIds` / `.linked-ids` / `.li-*`)。
|
|
109
|
-
* `identities`
|
|
110
|
-
|
|
109
|
+
* `identities` という語は、このリポジトリでは `GoldCommitIdentity`
|
|
110
|
+
*(帯のコミットの筆者・#799 で撤去)の意味で使っていた——同じ語を 2 つの意味で使わない。
|
|
111
111
|
*/
|
|
112
112
|
exports.FM_LINKED_IDS = "identities";
|
|
113
113
|
/**
|