@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
|
@@ -0,0 +1,112 @@
|
|
|
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.CONFIRMED_STATUS = void 0;
|
|
7
|
+
exports.decisionKeyOf = decisionKeyOf;
|
|
8
|
+
exports.decisionDateOf = decisionDateOf;
|
|
9
|
+
exports.supersedesIndex = supersedesIndex;
|
|
10
|
+
exports.supersedesOf = supersedesOf;
|
|
11
|
+
exports.retiredIdsOf = retiredIdsOf;
|
|
12
|
+
/**
|
|
13
|
+
* 決定の置き換え(`supersedes`)の逆引き(cortex-engine #699 F3・オーナー決定 A-1'・A-1'')。
|
|
14
|
+
*
|
|
15
|
+
* ## 🔴 これは写しで、正本は engine にある
|
|
16
|
+
*
|
|
17
|
+
* 正本: cortex-engine `scripts/gold/supersedes-index.mjs`(`supersedesIndex`・`decisionKeyOf`・
|
|
18
|
+
* `decisionDateOf`)。**題材で一致を確かめる**: 両リポに同じ `supersedes-index-contract.json`
|
|
19
|
+
* (tools は `test/fixtures/`)を置き、`test/supersedes-index-parity.test.mjs` が全ケースを流す。
|
|
20
|
+
* 規則を変えるときは engine の正本・この写し・題材を同じ日に直す(**tools 側で規則を足さない**)。
|
|
21
|
+
*
|
|
22
|
+
* ## 規則(A-1')
|
|
23
|
+
*
|
|
24
|
+
* 1. 置き換えの正本は**新しい側の `relations[supersedes]` だけ**。古い側の `superseded_by`
|
|
25
|
+
* (engine の夜間の精製が書く写し)は逆引きの材料にしない
|
|
26
|
+
* 2. **確認済み=新しい側の `status` が `active`**。空・`draft`・語彙の外は「未確認」
|
|
27
|
+
* 3. **置き換えられた(もう有効でない・retired)=確認済みの置き換えが 1 本以上**
|
|
28
|
+
* 4. 置き換え先は**全部・新しい順**(決定日の降順 → ID の降順・同じ ID は 1 回)
|
|
29
|
+
* 5. 自分自身を指す・実在しない ID は数えない。古い側の `status` は問わない
|
|
30
|
+
*/
|
|
31
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
32
|
+
/** 確認済みとみなす新しい側の status(これだけ) */
|
|
33
|
+
exports.CONFIRMED_STATUS = "active";
|
|
34
|
+
/**
|
|
35
|
+
* 決定を指す文字列を突き合わせの鍵にする(`decision:` の頭・パス・`.md` を落とす)。
|
|
36
|
+
* engine は `path.basename`(Linux で走る=posix)なので、ここも posix で固定する
|
|
37
|
+
*/
|
|
38
|
+
function decisionKeyOf(ref) {
|
|
39
|
+
return node_path_1.default.posix.basename(String(ref ?? "").trim().replace(/^decision:/, ""), ".md");
|
|
40
|
+
}
|
|
41
|
+
/** `YYYY-MM-DD`。frontmatter の `date`(`20260910` / `2026-09-10`)→ ID の先頭 8 桁 → 無ければ空 */
|
|
42
|
+
function decisionDateOf(date, id) {
|
|
43
|
+
const raw = String(date ?? "").trim();
|
|
44
|
+
const ymd = raw.match(/^(\d{4})-?(\d{2})-?(\d{2})/) || decisionKeyOf(id).match(/^(\d{4})(\d{2})(\d{2})/);
|
|
45
|
+
return ymd ? `${ymd[1]}-${ymd[2]}-${ymd[3]}` : "";
|
|
46
|
+
}
|
|
47
|
+
function normalize(d) {
|
|
48
|
+
if (!d || typeof d !== "object")
|
|
49
|
+
return null;
|
|
50
|
+
const r = d;
|
|
51
|
+
const id = String(r.id ?? "").trim();
|
|
52
|
+
if (!id)
|
|
53
|
+
return null;
|
|
54
|
+
return {
|
|
55
|
+
id,
|
|
56
|
+
key: decisionKeyOf(id),
|
|
57
|
+
date: decisionDateOf(r.date, id),
|
|
58
|
+
status: typeof r.status === "string" ? r.status.trim() : "",
|
|
59
|
+
supersedes: (Array.isArray(r.supersedes) ? r.supersedes : []).map(decisionKeyOf).filter(Boolean),
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/** 新しい順(決定日の降順 → ID の降順)。日付の無いものは最後 */
|
|
63
|
+
function newestFirst(a, b) {
|
|
64
|
+
if (a.date !== b.date) {
|
|
65
|
+
if (!a.date)
|
|
66
|
+
return 1;
|
|
67
|
+
if (!b.date)
|
|
68
|
+
return -1;
|
|
69
|
+
return a.date < b.date ? 1 : -1;
|
|
70
|
+
}
|
|
71
|
+
return a.id < b.id ? 1 : a.id > b.id ? -1 : 0;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 置き換えの逆引き。
|
|
75
|
+
*
|
|
76
|
+
* @returns `Map<古い決定の鍵, SupersedesEntry>`。置き換えが 1 本も無い決定は入らない
|
|
77
|
+
*/
|
|
78
|
+
function supersedesIndex(decisions) {
|
|
79
|
+
const src = decisions instanceof Map ? decisions.values() : decisions || [];
|
|
80
|
+
const list = [...src].map(normalize).filter((d) => d !== null);
|
|
81
|
+
const byKey = new Map();
|
|
82
|
+
for (const d of list)
|
|
83
|
+
if (!byKey.has(d.key))
|
|
84
|
+
byKey.set(d.key, d);
|
|
85
|
+
const out = new Map();
|
|
86
|
+
for (const d of list) {
|
|
87
|
+
for (const target of new Set(d.supersedes)) {
|
|
88
|
+
const old = byKey.get(target);
|
|
89
|
+
if (!old || old.key === d.key)
|
|
90
|
+
continue; // 実在しない・自分自身
|
|
91
|
+
const entry = out.get(old.key) || { id: old.id, by: [], retired: false };
|
|
92
|
+
if (entry.by.some((x) => decisionKeyOf(x.id) === d.key))
|
|
93
|
+
continue;
|
|
94
|
+
const confirmed = d.status === exports.CONFIRMED_STATUS;
|
|
95
|
+
entry.by.push({ id: d.id, date: d.date, status: d.status, confirmed });
|
|
96
|
+
if (confirmed)
|
|
97
|
+
entry.retired = true;
|
|
98
|
+
out.set(old.key, entry);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
for (const entry of out.values())
|
|
102
|
+
entry.by.sort(newestFirst);
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/** その決定の逆引き(無ければ空の形) */
|
|
106
|
+
function supersedesOf(index, id) {
|
|
107
|
+
return index.get(decisionKeyOf(id)) || { id: String(id ?? ""), by: [], retired: false };
|
|
108
|
+
}
|
|
109
|
+
/** 置き換えられた(確認済みの置き換えがある)決定の ID の集合 */
|
|
110
|
+
function retiredIdsOf(index) {
|
|
111
|
+
return new Set([...index.values()].filter((e) => e.retired).map((e) => e.id));
|
|
112
|
+
}
|
package/package.json
CHANGED
package/dist/commit-classify.js
DELETED
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* コミットを **人/機械/不明** の 3 値に分ける(#665 便 12b・#432 裁定 5-4 / 5-7)。
|
|
4
|
-
*
|
|
5
|
-
* ## 🔴 これは写しで、正本は engine にある
|
|
6
|
-
*
|
|
7
|
-
* 正本: cortex-engine `scripts/observe/commit-classify.mjs`。表(入力 → 3 値):
|
|
8
|
-
* cortex-engine `test/observe-commit-classify.test.mjs` の 12 行。
|
|
9
|
-
* **写すのはこの表で、実装を書き写すのではない**——同じ入力を両方に流して突き合わせる
|
|
10
|
-
* (`connection-state-keys.mjs` の写しと同じ流儀)。tools 側の表は
|
|
11
|
-
* `test/fixtures/commit-classify-contract.json` にあり、`commit-classify-parity.test.mjs`
|
|
12
|
-
* が全行を流す。**engine が判定を変えたらこの fixture を差し替える**(テストは書き換えない)。
|
|
13
|
-
*
|
|
14
|
-
* 🔴 **tools 側で規則を足さない。** 「再導出:」のような件名を足したくなっても、足す先は
|
|
15
|
-
* engine の正本(写しに足すと、正本と写しが逆向きにずれて契約テストが意味を失う)。
|
|
16
|
-
*
|
|
17
|
-
* ## なぜ `repo.ts` の `classifyCommit` と別に置くのか
|
|
18
|
-
*
|
|
19
|
-
* `repo.ts` のものは**別の問い**に答える 3 値(human / migration / ai)で、画面の
|
|
20
|
-
* 「AI による最近の変更」が使っている。あれを揃えるのは #646 の便で、この便では触らない
|
|
21
|
-
* ——揃えると 44 案件の既存の画面(履歴の節)が同時に変わる。ここは**新しい帯の条件だけ**が
|
|
22
|
-
* 使う判定で、**既定は「不明」**(証拠が無ければ倒さない)。
|
|
23
|
-
*
|
|
24
|
-
* ## 裁定(2026-09-17・#432 のコメント 5-4)
|
|
25
|
-
*
|
|
26
|
-
* > Claude Code 経由の push も「人が書いた」に数える(責任の所在で決める。人の名義で本人が
|
|
27
|
-
* > 結果を見て push している)。件数の閾値は設けない。「機械」に数えるのは**夜間処理・
|
|
28
|
-
* > マイグレーション・一括取り込みだけ**。
|
|
29
|
-
*/
|
|
30
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
-
exports.DELETE_SUBJECT_RE = exports.CONFIRM_SUBJECT_RE = exports.MIGRATION_REASON = void 0;
|
|
32
|
-
exports.looksLikeMigrationSubject = looksLikeMigrationSubject;
|
|
33
|
-
exports.operatedByKind = operatedByKind;
|
|
34
|
-
exports.classifyGoldCommit = classifyGoldCommit;
|
|
35
|
-
exports.isMachineRewrite = isMachineRewrite;
|
|
36
|
-
/**
|
|
37
|
-
* 機械の名義(**この 3 経路だけ**が機械)。`why` は `reason` にそのまま出る。
|
|
38
|
-
*
|
|
39
|
-
* 🔴 **`reason` の綴りは帯の条件が読む**(マイグレーションだけは帯を立てない・#665)。
|
|
40
|
-
* 綴りを変えるなら正本と fixture と `MIGRATION_REASON` を同時に見ること。
|
|
41
|
-
*/
|
|
42
|
-
const MACHINE_SUBJECTS = [
|
|
43
|
-
{ re: /^自動追記/, why: "夜間処理" },
|
|
44
|
-
{ re: /^自動生成/, why: "夜間処理" },
|
|
45
|
-
{ re: /^週次整理/, why: "夜間処理(週次)" },
|
|
46
|
-
{ re: /^(?:マイグレーション|migrate:|chore\(migrate\))/i, why: "マイグレーション" },
|
|
47
|
-
{ re: /^一括取り込み/, why: "一括取り込み" },
|
|
48
|
-
];
|
|
49
|
-
/** マイグレーションの `reason`。**帯を立てない側に倒す鍵の 1 枚目**(#665 決定表 4d) */
|
|
50
|
-
exports.MIGRATION_REASON = "マイグレーション";
|
|
51
|
-
/**
|
|
52
|
-
* 🔴 **「引っ越し」の綴りの 2 枚目**(`repo.ts:230` の写し・設計レビュー 🔴1)。
|
|
53
|
-
*
|
|
54
|
-
* 正本の `MACHINE_SUBJECTS` のマイグレーションの行は件名の**頭**を `マイグレーション` /
|
|
55
|
-
* `migrate:` / `chore(migrate)` で見るが、**艦隊の実際の件名は
|
|
56
|
-
* `エンジンマイグレーションを適用(schema_version更新)`** で、頭が「エンジン」なので
|
|
57
|
-
* 当たらない(実測: レコードを触った 127 件のうち 93 件が「機械の名義」の枝に落ち、
|
|
58
|
-
* engine の `isMigration()` では 0 件になる=`changed_by` から除かれていない)。
|
|
59
|
-
*
|
|
60
|
-
* この 1 枚が無いと、**引っ越しの翌朝に確認済みのレコード全件へ帯が出る**——実データの
|
|
61
|
-
* コミットの 53.6% が引っ越しで、中身は 1 文字も変わっていない。
|
|
62
|
-
*
|
|
63
|
-
* ⚠️ これは**正本に足すべき規則**(申し送り済み)。正本が拾うようになってもこの枚は
|
|
64
|
-
* 害にならない(両方が「引っ越し」と言うだけ)ので、**消すのは正本が実件名を拾ったあと**。
|
|
65
|
-
* 綴りは画面の「AI による最近の変更」が使う `repo.ts` の判定とそろえてある——ずれると
|
|
66
|
-
* 「履歴からは隠れているのに帯は立つ」という食い違った 1 ページができる(🔴7)。
|
|
67
|
-
*/
|
|
68
|
-
const MIGRATION_SUBJECT_RE = /^(エンジン)?マイグレーション|^スキーマ移行|^schema[_-]?version|^(apply\s+)?[a-z ]*migration/i;
|
|
69
|
-
/** 件名が「データ構造の引っ越し」の規約か(`repo.ts` の綴り) */
|
|
70
|
-
function looksLikeMigrationSubject(subject) {
|
|
71
|
-
return MIGRATION_SUBJECT_RE.test(String(subject || ""));
|
|
72
|
-
}
|
|
73
|
-
/** 機械の名義(名前)。`…[bot]` は GitHub のアプリ名義の綴り */
|
|
74
|
-
const MACHINE_NAME_RE = /(?:^|\s)(?:github-actions|dependabot|renovate)(?:\[bot\])?$|\[bot\]$/i;
|
|
75
|
-
/**
|
|
76
|
-
* 「人の名義とは言えない」メール。`[bot]@users.noreply.github.com` は機械の名義だが、
|
|
77
|
-
* `actions@github.com` のような汎用の名義は「誰が押したか分からない」=**不明**に倒す。
|
|
78
|
-
*/
|
|
79
|
-
const BOT_EMAIL_RE = /\[bot\]@users\.noreply\.github\.com$/i;
|
|
80
|
-
const ANONYMOUS_EMAIL_RE = /^(?:actions@github\.com|noreply@github\.com|no-reply@github\.com)$/i;
|
|
81
|
-
/** 人が Gold を直した印(tools の intake が書く件名。`repo.ts` と同じ綴り) */
|
|
82
|
-
exports.CONFIRM_SUBJECT_RE = /^Gold確認[::]/;
|
|
83
|
-
exports.DELETE_SUBJECT_RE = /^(?:レコード削除|Gold削除)/;
|
|
84
|
-
/** `Operated-By:` trailer の値から**経路だけ**を取る(値そのものは持ち回さない・PII) */
|
|
85
|
-
function operatedByKind(trailerValue) {
|
|
86
|
-
const v = String(trailerValue || "").trim();
|
|
87
|
-
if (!v)
|
|
88
|
-
return "none";
|
|
89
|
-
return /^basic:/.test(v) ? "basic" : "portal";
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* コミット 1 件を 3 値に分ける(正本と同じ順序・同じ規則)。
|
|
93
|
-
*
|
|
94
|
-
* 名前を `classifyCommit` にしない——`repo.ts` に同名で**別の問い**に答える関数があり、
|
|
95
|
-
* 取り違えると画面の履歴の節と帯の条件が静かに入れ替わる。
|
|
96
|
-
*/
|
|
97
|
-
function classifyGoldCommit({ authorName = "", authorEmail = "", committerName: _committerName = "", subject = "", body = "", } = {}) {
|
|
98
|
-
const trailer = (String(body).match(/^Operated-By:\s*(.+)$/m) || [])[1] || "";
|
|
99
|
-
const kind = operatedByKind(trailer);
|
|
100
|
-
// **検証済みか**は名乗り券の有無だけで決める(`basic:` は券が無いときの既定=自己申告)
|
|
101
|
-
const verified = kind === "portal";
|
|
102
|
-
const confirmer = (String(subject).match(exports.CONFIRM_SUBJECT_RE)
|
|
103
|
-
? String(subject).replace(exports.CONFIRM_SUBJECT_RE, "").trim()
|
|
104
|
-
: null) || null;
|
|
105
|
-
const out = (by, reason) => ({
|
|
106
|
-
by,
|
|
107
|
-
verified: by === "human" ? verified : false,
|
|
108
|
-
reason,
|
|
109
|
-
operatedByKind: kind,
|
|
110
|
-
confirmer,
|
|
111
|
-
});
|
|
112
|
-
// ---- 1. 機械(夜間処理・マイグレーション・一括取り込みだけ) ----
|
|
113
|
-
const machineSubject = MACHINE_SUBJECTS.find((m) => m.re.test(String(subject)));
|
|
114
|
-
if (machineSubject)
|
|
115
|
-
return out("machine", machineSubject.why);
|
|
116
|
-
if (MACHINE_NAME_RE.test(String(authorName).trim()))
|
|
117
|
-
return out("machine", "機械の名義");
|
|
118
|
-
if (BOT_EMAIL_RE.test(String(authorEmail).trim()))
|
|
119
|
-
return out("machine", "機械の名義(メール)");
|
|
120
|
-
// ---- 2. 人(**Claude Code 経由の push もここ**・裁定 5-4) ----
|
|
121
|
-
// 🔴 `Co-Authored-By: Claude` / `Generated with Claude Code` を**機械の証拠にしない**。
|
|
122
|
-
// 人の名義で本人が結果を見て push しているので、責任の所在は人にある
|
|
123
|
-
if (String(subject).match(exports.CONFIRM_SUBJECT_RE))
|
|
124
|
-
return out("human", "人が確認した(件名)");
|
|
125
|
-
if (exports.DELETE_SUBJECT_RE.test(String(subject)))
|
|
126
|
-
return out("human", "人が消した(件名)");
|
|
127
|
-
if (kind !== "none")
|
|
128
|
-
return out("human", "画面からの操作(Operated-By)");
|
|
129
|
-
const name = String(authorName).trim();
|
|
130
|
-
const email = String(authorEmail).trim();
|
|
131
|
-
if (name && email && !ANONYMOUS_EMAIL_RE.test(email))
|
|
132
|
-
return out("human", "人の名義の push");
|
|
133
|
-
// ---- 3. 既定は不明(裁定 5-7) ----
|
|
134
|
-
// 名義が空・汎用の名義(`actions@github.com`)は「誰が押したか分からない」
|
|
135
|
-
return out("unknown", "名義から人か機械か決められない");
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* **帯を立てる側の機械の書き直しか**(#665 決定表 4c・4d)。
|
|
139
|
-
*
|
|
140
|
-
* 機械の 3 経路(夜間処理・引っ越し・一括取り込み)のうち**引っ越しだけを外す**。実データの
|
|
141
|
-
* コミットの 53.6% が引っ越しで、中身は 1 文字も変わっていない(`repo.ts:199-207` の実測)
|
|
142
|
-
* ので、これで帯を立てると全レコードに帯が出る。engine も `changed_by` を作るときに
|
|
143
|
-
* `isMigration()` を当てているが(`history.mjs:145`)、**それは正本の綴りしか拾わない**うえに
|
|
144
|
-
* **古い帳簿・作り直し前の帳簿を読む夜がある**ので、写し側でも外す。
|
|
145
|
-
*
|
|
146
|
-
* 🔴 **判定は 2 枚の OR**(設計レビュー 🔴1): 正本の `reason` と `repo.ts` の綴り。
|
|
147
|
-
* **どちらかが「引っ越し」と言えば立てない**(片方だけに頼ると実件名が素通りする)。
|
|
148
|
-
*/
|
|
149
|
-
function isMachineRewrite(c, subject) {
|
|
150
|
-
if (c.by !== "machine")
|
|
151
|
-
return false;
|
|
152
|
-
if (c.reason === exports.MIGRATION_REASON)
|
|
153
|
-
return false;
|
|
154
|
-
if (looksLikeMigrationSubject(subject))
|
|
155
|
-
return false;
|
|
156
|
-
return true;
|
|
157
|
-
}
|