@takagaki/cortex-decisions-viewer 0.12.67 → 0.12.69
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 +664 -25
- package/package.json +1 -1
package/dist/render.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = void 0;
|
|
3
|
+
exports.STATE_CONFIRMING = exports.PROBE_WAIT_TIMEOUT_TEXT = exports.UNSETTLED_LIMITS = exports.INGEST_LIMITS = exports.ALREADY_REGISTERED = exports.LIVE_CONFIRM_LIMITS = exports.CLAUDE_CODE_LINK = void 0;
|
|
4
4
|
exports.renderSite = renderSite;
|
|
5
5
|
exports.renderSiteWithAssets = renderSiteWithAssets;
|
|
6
6
|
exports.viewerAssetSources = viewerAssetSources;
|
|
@@ -14,7 +14,12 @@ exports.regRowOfToolItem = regRowOfToolItem;
|
|
|
14
14
|
exports.toolsFoldSummary = toolsFoldSummary;
|
|
15
15
|
exports.isAlreadyRegistered = isAlreadyRegistered;
|
|
16
16
|
exports.sanitizeRegisteredNow = sanitizeRegisteredNow;
|
|
17
|
+
exports.sanitizeIngestCounts = sanitizeIngestCounts;
|
|
18
|
+
exports.sanitizeIngestFailures = sanitizeIngestFailures;
|
|
17
19
|
exports.sanitizeStateNow = sanitizeStateNow;
|
|
20
|
+
exports.devExcludedSkip = devExcludedSkip;
|
|
21
|
+
exports.devExcludedRefs = devExcludedRefs;
|
|
22
|
+
exports.devExcludedLabel = devExcludedLabel;
|
|
18
23
|
exports.overlayStateOnFleet = overlayStateOnFleet;
|
|
19
24
|
exports.chatRowsToAdd = chatRowsToAdd;
|
|
20
25
|
exports.gchatRowsToAdd = gchatRowsToAdd;
|
|
@@ -38,6 +43,8 @@ exports.secretRegisteredAnywhere = secretRegisteredAnywhere;
|
|
|
38
43
|
exports.sanitizeCapabilitiesNow = sanitizeCapabilitiesNow;
|
|
39
44
|
exports.sanitizeGithubOwnersNow = sanitizeGithubOwnersNow;
|
|
40
45
|
exports.splitNeededParts = splitNeededParts;
|
|
46
|
+
exports.ingestFacts = ingestFacts;
|
|
47
|
+
exports.ingestSucceeded = ingestSucceeded;
|
|
41
48
|
exports.ingestedAtOf = ingestedAtOf;
|
|
42
49
|
exports.ingestedRowOf = ingestedRowOf;
|
|
43
50
|
exports.runLogUrlOf = runLogUrlOf;
|
|
@@ -57,6 +64,8 @@ exports.calendarWaiting = calendarWaiting;
|
|
|
57
64
|
exports.calendarTemplateUrl = calendarTemplateUrl;
|
|
58
65
|
exports.calendarAddUrl = calendarAddUrl;
|
|
59
66
|
exports.deriveCapTile = deriveCapTile;
|
|
67
|
+
exports.ingestFailureRows = ingestFailureRows;
|
|
68
|
+
exports.ingestFailureText = ingestFailureText;
|
|
60
69
|
exports.derivePendingTile = derivePendingTile;
|
|
61
70
|
exports.liveRowVerdict = liveRowVerdict;
|
|
62
71
|
exports.liveRegistryOf = liveRegistryOf;
|
|
@@ -548,8 +557,9 @@ const TOOL_KIND_ICONS = {
|
|
|
548
557
|
"google-meet": "🎥",
|
|
549
558
|
};
|
|
550
559
|
/**
|
|
551
|
-
* 状態の表示語と色。**
|
|
552
|
-
*
|
|
560
|
+
* 状態の表示語と色。**5 語+「登録状況を確認中です」しか出さない**(読み取り中/
|
|
561
|
+
* 接続OK(読み取りの対象外)/承認待ち/停止中/未確認、そして登録簿にしか無く
|
|
562
|
+
* まだ測っていない行の「確認中」・#581 / #640)。
|
|
553
563
|
*
|
|
554
564
|
* 色は既存の `ds-gate-*` を流用する——同じ画面で「緑=読めている・橙=人の操作待ち・
|
|
555
565
|
* 灰=分からない」の意味を 2 通りに増やさない。
|
|
@@ -562,6 +572,13 @@ const TOOL_KIND_ICONS = {
|
|
|
562
572
|
function toolStateLabel(state) {
|
|
563
573
|
if (state === "reading")
|
|
564
574
|
return { text: "読み取り中", cls: "ds-gate-ok" };
|
|
575
|
+
// 届いてはいるが、Gold の材料にしていない(#640・engine #399)。**「読み取り中」と
|
|
576
|
+
// 言わない**——読みに行かないと決めた接続先なので、言えば画面が嘘をつく。
|
|
577
|
+
// **警告色にもしない**(`ds-gate-ok` のまま)——直すものが無い中立の事実で、橙にすると
|
|
578
|
+
// 直しに行かせる。状態名を `"excluded"` にしないのは、`liveToolItems` の中で
|
|
579
|
+
// `match.excluded`(登録簿の devExcluded =「停止中」)が別の意味で使われているため
|
|
580
|
+
if (state === "ok_not_ingested")
|
|
581
|
+
return { text: "接続OK(読み取りの対象外)", cls: "ds-gate-ok" };
|
|
565
582
|
if (state === "waiting")
|
|
566
583
|
return { text: "承認待ち", cls: "ds-gate-warn" };
|
|
567
584
|
// 止めているものは警告色にしない(直す必要が無いので、橙にすると直しに行かせてしまう)
|
|
@@ -650,11 +667,13 @@ function regRowOfToolItem(item) {
|
|
|
650
667
|
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
651
668
|
*/
|
|
652
669
|
function toolsFoldSummary(states) {
|
|
653
|
-
//
|
|
654
|
-
|
|
655
|
-
|
|
670
|
+
// 表示の順は状態の語と同じ(読み取り中→接続OK(読み取りの対象外)→承認待ち→停止中→
|
|
671
|
+
// 未確認→登録状況を確認中です)。🔴 **新しい語をここへ足し忘れると、下の `order.indexOf`
|
|
672
|
+
// が `unknown` へ倒すので、畳んだ 1 行だけが「未確認」と言う**(開くと違う語が出る・#640)
|
|
673
|
+
var order = ["reading", "ok_not_ingested", "waiting", "stopped", "unknown", "pending"];
|
|
674
|
+
var count = { reading: 0, ok_not_ingested: 0, waiting: 0, stopped: 0, unknown: 0, pending: 0 };
|
|
656
675
|
(states || []).forEach(function (s) {
|
|
657
|
-
// ⚠
|
|
676
|
+
// ⚠ 5 語のどれかに当たるかは **order で見る**(素のオブジェクトの添字だと
|
|
658
677
|
// "constructor" のような語が継承プロパティに当たって、知らない語が素通りする)
|
|
659
678
|
var k = String(s == null ? "" : s);
|
|
660
679
|
count[order.indexOf(k) < 0 ? "unknown" : k] += 1;
|
|
@@ -1042,6 +1061,89 @@ const RUN_URL_WRITERS = ["ingest", "gold", "nightly"];
|
|
|
1042
1061
|
* (「Webhook」と断定すると嘘になる)
|
|
1043
1062
|
*/
|
|
1044
1063
|
const RUN_SOURCE_WORDS = { central: "中央の処理", button: "その場で確認", auto: "保存時に確認" };
|
|
1064
|
+
/**
|
|
1065
|
+
* 部分失敗の内訳の欄(#634)。サーバ側 `connection-state-keys.mjs` の
|
|
1066
|
+
* `INGEST_FAILURE_KINDS` / `INGEST_FAILURES_MAX` / `INGEST_TEXT_MAX` / `INGEST_COUNT_KEYS` の写し。
|
|
1067
|
+
*
|
|
1068
|
+
* 🔴 **`STATE_ROW_FIELDS` は文字列の欄しか通さない**(`sanitizeStateNow` の `typeof fv === "string"`)。
|
|
1069
|
+
* `counts`(オブジェクト)と `failures`(配列)は**そこを通らない**ので、下の 2 つの篩を
|
|
1070
|
+
* 別に通す。通し忘れると欄は黙って消え、見出しの件数だけが出なくなる——**行は届いているのに
|
|
1071
|
+
* 画面が何も言わない**形なので、実機でも「engine がまだ書いていないのだろう」と読めてしまう。
|
|
1072
|
+
*
|
|
1073
|
+
* ⚠ **定数を export しない**(`exports.X` に化けて、流し込んだ先で未定義になる)。
|
|
1074
|
+
* 突き合わせは `viewer/test/state-vocab-parity.test.mjs`(サーバの正本と同じ値か)。
|
|
1075
|
+
*/
|
|
1076
|
+
const INGEST_FAILURE_KINDS = ["transient", "permanent"];
|
|
1077
|
+
const INGEST_FAILURES_MAX = 20;
|
|
1078
|
+
const INGEST_TEXT_MAX = 200;
|
|
1079
|
+
const INGEST_COUNT_KEYS = ["ok", "failed", "skipped"];
|
|
1080
|
+
const INGEST_COUNT_MAX = 1000000;
|
|
1081
|
+
/**
|
|
1082
|
+
* タイルに並べる失敗の行の本数。**超えたぶんは「ほか N 件」に畳む。**
|
|
1083
|
+
* 20 行並べるとタイルが理由文で埋まる(同じ形の実害が 0.4.71 にある)。
|
|
1084
|
+
*/
|
|
1085
|
+
const INGEST_TILE_ROWS = 3;
|
|
1086
|
+
/** 語と上限を**テストから固定するための入れ物**(`STATE_CONFIRMING` と同じ作法) */
|
|
1087
|
+
exports.INGEST_LIMITS = {
|
|
1088
|
+
kinds: INGEST_FAILURE_KINDS,
|
|
1089
|
+
failuresMax: INGEST_FAILURES_MAX,
|
|
1090
|
+
textMax: INGEST_TEXT_MAX,
|
|
1091
|
+
countKeys: INGEST_COUNT_KEYS,
|
|
1092
|
+
countMax: INGEST_COUNT_MAX,
|
|
1093
|
+
tileRows: INGEST_TILE_ROWS,
|
|
1094
|
+
};
|
|
1095
|
+
/**
|
|
1096
|
+
* `counts` を篩う(#634)。**落として残りを出す**(サーバ側 `normalizeIngestCounts` と同じ規則)。
|
|
1097
|
+
*
|
|
1098
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
1099
|
+
* `INGEST_COUNT_KEYS` / `INGEST_COUNT_MAX` は CLIENT_JS 側に `var` で流し込んである)。
|
|
1100
|
+
*/
|
|
1101
|
+
function sanitizeIngestCounts(v) {
|
|
1102
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
1103
|
+
return null;
|
|
1104
|
+
var out = {};
|
|
1105
|
+
var any = false;
|
|
1106
|
+
for (var i = 0; i < INGEST_COUNT_KEYS.length; i++) {
|
|
1107
|
+
var k = INGEST_COUNT_KEYS[i];
|
|
1108
|
+
var n = v[k];
|
|
1109
|
+
// **整数・非負・上限つきだけ。** 画面の見出しに出る数なので、`-1` や `1e30` を通さない
|
|
1110
|
+
if (typeof n === "number" && isFinite(n) && Math.floor(n) === n && n >= 0 && n <= INGEST_COUNT_MAX) {
|
|
1111
|
+
out[k] = n;
|
|
1112
|
+
any = true;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return any ? out : null;
|
|
1116
|
+
}
|
|
1117
|
+
/**
|
|
1118
|
+
* `failures` を篩う(#634)。**落として残りを出す**(サーバ側 `normalizeIngestFailures` と同じ規則)。
|
|
1119
|
+
*
|
|
1120
|
+
* `kind` が語彙外の値は `permanent` に倒す。**`transient` に倒さない**——画面は `transient` にだけ
|
|
1121
|
+
* 「次回の取り込みで再試行します」を添えるので、知らない語をそちらへ倒すと、直さないと消えない
|
|
1122
|
+
* 失敗に「待てば直る」と言うことになる。
|
|
1123
|
+
*
|
|
1124
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
1125
|
+
* `INGEST_FAILURE_KINDS` / `INGEST_FAILURES_MAX` / `INGEST_TEXT_MAX` は `var` で流し込んである)。
|
|
1126
|
+
*/
|
|
1127
|
+
function sanitizeIngestFailures(v) {
|
|
1128
|
+
if (!Array.isArray(v))
|
|
1129
|
+
return null;
|
|
1130
|
+
var out = [];
|
|
1131
|
+
for (var i = 0; i < v.length; i++) {
|
|
1132
|
+
if (out.length >= INGEST_FAILURES_MAX)
|
|
1133
|
+
break;
|
|
1134
|
+
var f = v[i];
|
|
1135
|
+
if (!f || typeof f !== "object" || Array.isArray(f))
|
|
1136
|
+
continue;
|
|
1137
|
+
if (typeof f.name !== "string" || !f.name)
|
|
1138
|
+
continue;
|
|
1139
|
+
out.push({
|
|
1140
|
+
name: f.name.slice(0, INGEST_TEXT_MAX),
|
|
1141
|
+
reason: typeof f.reason === "string" ? f.reason.slice(0, INGEST_TEXT_MAX) : "",
|
|
1142
|
+
kind: INGEST_FAILURE_KINDS.indexOf(f.kind) >= 0 ? f.kind : "permanent",
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
return out.length ? out : null;
|
|
1146
|
+
}
|
|
1045
1147
|
/** stateNow はライブ専用キーで build.ts の網にかからない。画面側でも許可リストを通す */
|
|
1046
1148
|
function sanitizeStateNow(v) {
|
|
1047
1149
|
if (!Array.isArray(v))
|
|
@@ -1066,10 +1168,162 @@ function sanitizeStateNow(v) {
|
|
|
1066
1168
|
// `runUrl` は run ページの形だけ(#560)。形が違えば**欄だけ落として行は残す**
|
|
1067
1169
|
if (item.runUrl && !RUN_URL_RE.test(item.runUrl))
|
|
1068
1170
|
delete item.runUrl;
|
|
1171
|
+
// 部分失敗の内訳(#634)。**上の走査は文字列の欄しか通さない**ので、オブジェクトと配列の
|
|
1172
|
+
// この 2 欄はここで別に篩う。🔴 ここを落とすと欄は黙って消え、**行は届いているのに
|
|
1173
|
+
// 見出しの件数と失敗の行だけが出ない**——「engine がまだ書いていない」と読めてしまう形になる。
|
|
1174
|
+
// 秘密の除去はサーバ側(`queryConnectionState`)が済ませてある(`detail` と同じ扱い。
|
|
1175
|
+
// 画面側に秘密のパターンの 2 枚目を持たない・設計レビュー🔴2)
|
|
1176
|
+
if (item.question === "ingested") {
|
|
1177
|
+
const c = sanitizeIngestCounts(r.counts);
|
|
1178
|
+
if (c)
|
|
1179
|
+
item.counts = c;
|
|
1180
|
+
const f = sanitizeIngestFailures(r.failures);
|
|
1181
|
+
if (f)
|
|
1182
|
+
item.failures = f;
|
|
1183
|
+
}
|
|
1069
1184
|
out.push(item);
|
|
1070
1185
|
}
|
|
1071
1186
|
return out.length ? out : null;
|
|
1072
1187
|
}
|
|
1188
|
+
/**
|
|
1189
|
+
* その行が「接続はできるが、Gold の材料にしていない」**中立**の事実か(#640・engine #399)。
|
|
1190
|
+
*
|
|
1191
|
+
* 夜間の Gold 昇格は、性質が「作業物」で取得ごと省いたソースにも到達性を 1 回書く
|
|
1192
|
+
* (engine #399・PR #405)。同じ ref に 2 行が並ぶ:
|
|
1193
|
+
*
|
|
1194
|
+
* ```
|
|
1195
|
+
* SK: dev##<owner>/<repo>#reachable status: ok by: gold
|
|
1196
|
+
* SK: dev##<owner>/<repo>#ingested status: skipped by: gold
|
|
1197
|
+
* detail: 性質が作業物のため Gold の材料にしません
|
|
1198
|
+
* ```
|
|
1199
|
+
*
|
|
1200
|
+
* 🔴 **「鍵が無くて取り込みを始めていない」`skipped` と混ぜない(#225)。** どちらも同じ語だが、
|
|
1201
|
+
* 利用者が取るべき行動が正反対で、こちらは**何もしなくてよい**——警告(橙)に落とすと、
|
|
1202
|
+
* GitHub を登録した全案件が「直すものがある」と言われる(engine #399 実装レビュー🟡3)。
|
|
1203
|
+
* 見分ける材料は書き手(`by: "gold"`)で、理由文は engine が `by` の綴りを変えた日の保険。
|
|
1204
|
+
*
|
|
1205
|
+
* 🔴 **cap=`dev` で門を掛けるのが、「開発以外は 1 バイトも変わらない」の実体。**
|
|
1206
|
+
* 課題管理・デザインの `skipped`(=鍵未登録)はこの関数に一度も当たらない。他の能力にも
|
|
1207
|
+
* 「読まないと決めた」が生まれたら、そのときに cap を足す(先回りして広げない——
|
|
1208
|
+
* 広げた先の `skipped` が鍵未登録かどうかを、ここでは確かめられない)。
|
|
1209
|
+
*
|
|
1210
|
+
* ⚠ **チャットには同じ穴が残っている(この便では直さない・実装レビュー🟡8)。** engine の
|
|
1211
|
+
* 門(`external-sources.sh`)は GitHub 専用ではなく、`nature: 作業物` を宣言した Slack
|
|
1212
|
+
* チャンネル / Google Chat スペースにも `chat##…#ingested: skipped` を書く。ここは
|
|
1213
|
+
* `cap !== "dev"` で弾くので、その接続先の Home は #640 以前と**まったく同じ嘘**
|
|
1214
|
+
* (「読み取り中」)を言う。広げるときは、チャットの `skipped` に鍵未登録の書き手が
|
|
1215
|
+
* いないことを先に確かめる。
|
|
1216
|
+
*
|
|
1217
|
+
* ⚠ **`goldState: "excluded"`(行の「読み取り停止中」)とも別物。** あちらは人が
|
|
1218
|
+
* `dev-sources.json` の exclude に書いて止めたもので、こちらはソースの性質で門が省いたもの。
|
|
1219
|
+
*
|
|
1220
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
1221
|
+
* 正規表現も持たない——文字列の一致だけで足りる)。
|
|
1222
|
+
*/
|
|
1223
|
+
function devExcludedSkip(row) {
|
|
1224
|
+
if (!row || row.cap !== "dev" || row.question !== "ingested")
|
|
1225
|
+
return false;
|
|
1226
|
+
if (row.status !== "skipped")
|
|
1227
|
+
return false;
|
|
1228
|
+
// 🔴 **書き手と理由の AND(片方だけで中立と決めない)。** engine の `neutral` の印は
|
|
1229
|
+
// ストアの属性にならない(`connection-state-writer.mjs` の `historyArgs` は履歴を飛ばす
|
|
1230
|
+
// だけ)ので、画面が中立と言える材料はこの 2 つしかない。`by: "gold"` だけで倒すと、
|
|
1231
|
+
// 夜間が**本物の** `skipped` を dev に書き始めた日に、⚠️ にしたい行が黙って
|
|
1232
|
+
// 「接続OK」に化ける(倒れる向きが問題を隠す側になる・設計レビュー🔴3)。
|
|
1233
|
+
// 理由文の印は engine の 2 文(`性質が作業物のため…` / `ソースの性質により…`)に共通で、
|
|
1234
|
+
// 正本は `infra/lambda/shared/adapters.mjs` の `INGEST_NEUTRAL_DETAIL_MARK`
|
|
1235
|
+
// (写しのずれは `state-vocab-parity.test.mjs` が見張る)
|
|
1236
|
+
if (row.by !== "gold")
|
|
1237
|
+
return false;
|
|
1238
|
+
var d = typeof row.detail === "string" ? row.detail : "";
|
|
1239
|
+
return d.indexOf("材料にしません") >= 0;
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* 「いま材料にしていない」開発の接続先(#640)。**鍵は ref の生値**(`overlayStateOnFleet` の
|
|
1243
|
+
* `dev` の鍵と同じ畳み方——揃えないと、行に札が出ないのに Home だけ出る食い違いになる)。
|
|
1244
|
+
*
|
|
1245
|
+
* 🔴 **同じ接続先の「届くか」のほうが新しければ、その中立は古い事実として捨てる。**
|
|
1246
|
+
* 接続状態の行に TTL は無く、engine が中立の行を書くのは**門で省いた晩だけ**
|
|
1247
|
+
* (`connection-state-write-gold.mjs` の `gatedIngestedRow`)。登録簿で性質を「会話」に
|
|
1248
|
+
* 直すと、engine は翌晩から**行を書かないだけ**で、`ingested: ok` に上書きはしない。
|
|
1249
|
+
* 鮮度を見ないと、**言われたとおりに直した人の画面が永久に変わらない**——
|
|
1250
|
+
* 「登録簿で性質を『会話』にしてください」と出し続ける(設計レビュー🔴2)。
|
|
1251
|
+
*
|
|
1252
|
+
* 🔴 **比べる相手は「その晩の Gold 昇格が回った時刻」(`by: "gold"` の行の最新の `at`)。**
|
|
1253
|
+
* 同じ ref の「届くか」と比べてはいけない——接続状態の SK は `cap#workspace#ref#question`
|
|
1254
|
+
* で **`reachable` は 1 本しかなく**、「接続を確認」(`by: "button"`)がその 1 本を
|
|
1255
|
+
* 上書きする。比べると次の 2 つが**どちらも**起きる(実装レビュー🔴1):
|
|
1256
|
+
*
|
|
1257
|
+
* - ボタンの行も比べる … 押した瞬間に `at` が今になり、**押しただけで札が消える**
|
|
1258
|
+
* (押した人には「確認したら読まれるようになった」と読める)
|
|
1259
|
+
* - 夜間の行だけ比べる … 押すと夜間の行が**消える**ので比べる相手を失い、
|
|
1260
|
+
* 性質を直して消えたはずの札が**押しただけで復活する**
|
|
1261
|
+
*
|
|
1262
|
+
* 夜間の Gold 昇格は 1 回の run の全部の行に**同じ `at`** を書く(engine の
|
|
1263
|
+
* `connection-state-write-gold.mjs` は `at` を run で 1 つだけ作る)。だから
|
|
1264
|
+
* **書き手が `gold` の行の最新の `at`**=その案件で夜間が最後に回った時刻で、これは
|
|
1265
|
+
* ボタンが 1 本上書きしても残る(他の接続先・他の問いの行が持っている)。
|
|
1266
|
+
* 中立の行自身も `by: "gold"` なので、**厳密に新しいときだけ**捨てれば同じ晩の組は残る。
|
|
1267
|
+
*
|
|
1268
|
+
* ⚠ **残る穴**: その案件の `by: "gold"` の行が**この 1 本だけ**(接続先が 1 つで、
|
|
1269
|
+
* その `reachable` をボタンが上書きした)ときは比べる相手が無く、古い札が次の夜まで残る。
|
|
1270
|
+
* 塞ぐのは engine 側(門を通らなくなった先の `ingested` の行を消す/`ok` を書く)で、
|
|
1271
|
+
* 決定ログ `20260916-640.md` の「覆し方」に書いてある。
|
|
1272
|
+
*
|
|
1273
|
+
* 時刻はミリ秒ありの ISO に正規化済み(サーバの `normalizeAt`)なので辞書順で比べられる。
|
|
1274
|
+
*
|
|
1275
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
1276
|
+
*/
|
|
1277
|
+
function devExcludedRefs(rows) {
|
|
1278
|
+
var out = {};
|
|
1279
|
+
// その案件で夜間の Gold 昇格が最後に回った時刻。**能力も問いも問わず集める**
|
|
1280
|
+
// ——1 回の run は全部の行に同じ `at` を書くので、ボタンが 1 本上書きしても
|
|
1281
|
+
// 他の行が同じ時刻を持っている(上の項)
|
|
1282
|
+
var lastGoldAt = "";
|
|
1283
|
+
var list = Array.isArray(rows) ? rows : [];
|
|
1284
|
+
var i, r, k;
|
|
1285
|
+
for (i = 0; i < list.length; i++) {
|
|
1286
|
+
r = list[i];
|
|
1287
|
+
if (!r)
|
|
1288
|
+
continue;
|
|
1289
|
+
if (r.by === "gold" && String(r.at) > lastGoldAt)
|
|
1290
|
+
lastGoldAt = String(r.at);
|
|
1291
|
+
if (r.cap !== "dev")
|
|
1292
|
+
continue;
|
|
1293
|
+
k = String(r.ref == null ? "" : r.ref);
|
|
1294
|
+
if (!k)
|
|
1295
|
+
continue;
|
|
1296
|
+
if (devExcludedSkip(r) && (!out[k] || String(r.at) > String(out[k].at)))
|
|
1297
|
+
out[k] = r;
|
|
1298
|
+
}
|
|
1299
|
+
for (k in out) {
|
|
1300
|
+
if (!Object.prototype.hasOwnProperty.call(out, k))
|
|
1301
|
+
continue;
|
|
1302
|
+
// **厳密に新しいときだけ捨てる。** 同じ晩の組(`at` が同値)を落とさないため
|
|
1303
|
+
if (lastGoldAt > String(out[k].at))
|
|
1304
|
+
delete out[k];
|
|
1305
|
+
}
|
|
1306
|
+
return out;
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* 中立の行の理由文から、画面に出す短語を作る(#640)。
|
|
1310
|
+
*
|
|
1311
|
+
* 🔴 **engine が名指しを避けた性質を、画面が断定しない。** engine は性質が語彙
|
|
1312
|
+
* (会話/作業物/参照資料)に載っているときだけ名指しし、載っていなければ
|
|
1313
|
+
* 「ソースの性質により Gold の材料にしません」と書く(`gatedIngestedRow`)。
|
|
1314
|
+
* viewer に性質の語彙の写しは持たない——持つと 4 枚目の写しになり、engine が
|
|
1315
|
+
* 門に掛ける性質を増やした日に黙ってずれる(engine 設計レビュー🟡6 と同じ罠)。
|
|
1316
|
+
* **理由文が名指ししている語をそのまま括弧に入れる**のが、写しを増やさない唯一の形。
|
|
1317
|
+
*
|
|
1318
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
1319
|
+
*/
|
|
1320
|
+
function devExcludedLabel(detail) {
|
|
1321
|
+
var d = typeof detail === "string" ? detail : "";
|
|
1322
|
+
var at = d.indexOf("性質が");
|
|
1323
|
+
var to = d.indexOf("のため");
|
|
1324
|
+
var nature = at === 0 && to > 3 ? d.slice(3, to) : "";
|
|
1325
|
+
return nature ? "読み取りの対象外(" + nature + ")" : "読み取りの対象外";
|
|
1326
|
+
}
|
|
1073
1327
|
/**
|
|
1074
1328
|
* ストアの「届くか」を行に重ねる。**まとめ表の時刻とは比べない。**
|
|
1075
1329
|
* fleet-status.json は chat / dev の gate・needed を焼き込まない(観測の一本化・工程1。
|
|
@@ -1110,12 +1364,23 @@ function sanitizeStateNow(v) {
|
|
|
1110
1364
|
* 画面に届かない(fleet-status.json には焼き込まれないので、ライブが届かない案件では出ない
|
|
1111
1365
|
* ——それは意図。古いスナップショットの理由を出すより、出さないほうが嘘にならない)。
|
|
1112
1366
|
* needed と同じく、上書きするときは空でも必ず載せる
|
|
1367
|
+
*
|
|
1368
|
+
* **開発の行だけ、`ingested` の中立の行も載せる(#640)。** 「接続できる」と「Gold の
|
|
1369
|
+
* 材料にしている」は別の状態で、`reachable: ok` だけを載せると、読みに行かないと決めた
|
|
1370
|
+
* リポジトリが画面で「読み取り中」になる(engine #399 の申し送り)。載せる欄は
|
|
1371
|
+
* `ingestExcluded`(併記の札を出すか)と `ingestExcludedDetail`(語の括弧に入れる性質)。
|
|
1372
|
+
* 🔴 **`goldState: "excluded"`(人が `dev-sources.json` の exclude で止めた)と別物。**
|
|
1113
1373
|
*/
|
|
1114
1374
|
function overlayStateOnFleet(view, rows) {
|
|
1115
1375
|
const v = view || {};
|
|
1116
1376
|
if (!rows || !rows.length || !Array.isArray(v.externalSources))
|
|
1117
1377
|
return v;
|
|
1118
1378
|
const byKey = {};
|
|
1379
|
+
// 「接続できる」と「Gold の材料にしている」は別の状態(#640)。**鮮度の門は
|
|
1380
|
+
// `devExcludedRefs` 1 本**(タイル・Home と同じ規則を 2 枚にしない)。拾う対象は
|
|
1381
|
+
// この下が捨てている行(`question !== "reachable"`)だけなので、チャット・
|
|
1382
|
+
// Google Chat の経路は 1 バイトも変わらない
|
|
1383
|
+
const excludedByRef = devExcludedRefs(rows);
|
|
1119
1384
|
for (const r of rows) {
|
|
1120
1385
|
if (r.question !== "reachable")
|
|
1121
1386
|
continue;
|
|
@@ -1131,16 +1396,28 @@ function overlayStateOnFleet(view, rows) {
|
|
|
1131
1396
|
if (!s || !s.ref)
|
|
1132
1397
|
return s;
|
|
1133
1398
|
let hit = null;
|
|
1399
|
+
let ex = null;
|
|
1134
1400
|
if (s.type === "slack")
|
|
1135
1401
|
hit = byKey["chat\t" + String(s.workspace || "") + "\t" + String(s.ref)];
|
|
1136
1402
|
else if (s.type === "google-chat")
|
|
1137
1403
|
hit = byKey["google-chat\t" + String(s.ref)];
|
|
1138
|
-
else if (String(s.type || "").indexOf("github") === 0)
|
|
1404
|
+
else if (String(s.type || "").indexOf("github") === 0) {
|
|
1139
1405
|
hit = byKey["dev\t" + String(s.ref)];
|
|
1140
|
-
|
|
1141
|
-
|
|
1406
|
+
// ⚠ 鍵は ref の生値(1 つ上の `dev` の鍵と同じ畳み方)。**`liveRowVerdict` だけは
|
|
1407
|
+
// 両側を小文字に畳む**ので、大小が揺れた repo では Home にだけ札が出る食い違いが
|
|
1408
|
+
// 作れる(#566 の宿題と同根・この便では直さず申し送り)
|
|
1409
|
+
ex = excludedByRef[String(s.ref)] || null;
|
|
1410
|
+
}
|
|
1411
|
+
// **到達性の行が無い夜でも、併記だけは出す。** engine は 2 行を同じ晩に書くが、
|
|
1412
|
+
// 片方しか届かない窓を「何も言わない」に倒すと、画面が知っている事実を捨てる
|
|
1413
|
+
if (!hit) {
|
|
1414
|
+
if (!ex)
|
|
1415
|
+
return s;
|
|
1416
|
+
changed = true;
|
|
1417
|
+
return Object.assign({}, s, { ingestExcluded: true, ingestExcludedDetail: String(ex.detail || "") });
|
|
1418
|
+
}
|
|
1142
1419
|
changed = true;
|
|
1143
|
-
return Object.assign({}, s, {
|
|
1420
|
+
return Object.assign({}, s, ex ? { ingestExcluded: true, ingestExcludedDetail: String(ex.detail || "") } : {}, {
|
|
1144
1421
|
gate: hit.status,
|
|
1145
1422
|
gateAt: hit.at,
|
|
1146
1423
|
gateBy: hit.by,
|
|
@@ -2062,6 +2339,90 @@ function splitNeededParts(text) {
|
|
|
2062
2339
|
out.push({ text: s.slice(at), url: false });
|
|
2063
2340
|
return out;
|
|
2064
2341
|
}
|
|
2342
|
+
/**
|
|
2343
|
+
* 取り込みの行 1 本から「何件取り込めて、何件失敗したか」を読む(#634)。**判定はこの 1 本だけ。**
|
|
2344
|
+
*
|
|
2345
|
+
* 🔴 **タイル(`deriveCapTile`)・Home の「使用ツール」(`liveRowVerdict`)・行の札(`ingestedEl`)・
|
|
2346
|
+
* 「最終同期」(`ingestedAtOf`)が全部ここを通る。** 分けると、同じ案件でタイルは「接続OK」・
|
|
2347
|
+
* Home は「承認待ち」・札は「失敗」になる(`ops-ui-controls-policy.md` R10「タイルと行が違うことを
|
|
2348
|
+
* 言ってはいけない」)。2026-09-16 の nicigas-ncp が踏んだのはまさにこの形の 1 枚目。
|
|
2349
|
+
*
|
|
2350
|
+
* ## 2 つの読み方
|
|
2351
|
+
*
|
|
2352
|
+
* 1. **新形式**(便 2 以降)… `counts` から読む。`status` は `ok`(部分失敗は「取り込めた」側)
|
|
2353
|
+
* 2. **旧形式の橋**(便 1 の暫定)… `status: "failed"` で `counts` が無い行の `detail` から
|
|
2354
|
+
* 件数を読む。engine の書き手は `251件取り込み・1件失敗・38件は取り込めない形式…`
|
|
2355
|
+
* (`scripts/connection-state-write-materials.mjs` の `ingestedDetail`)、中央 Apps Script は
|
|
2356
|
+
* `3件取り込み(カレンダー 2・合図 1)・1件失敗`(`apps-script/src/Main.gs`)と書く。
|
|
2357
|
+
* **cap を問わず全部の `ingested` の行に効く**(会議もこの便で部分失敗になる・設計レビュー🔴4)
|
|
2358
|
+
*
|
|
2359
|
+
* ⚠ **橋は便 2 で消してよい。** engine と中央の書き手が `counts` を書き始めたら、下の
|
|
2360
|
+
* 「旧形式の橋」の分岐ごと削除できる(新形式の行は 1 つ目の分岐で答えが出る)。
|
|
2361
|
+
* 消すときは番人テストの旧形式の題材も一緒に消す。
|
|
2362
|
+
*
|
|
2363
|
+
* 🔴 **正規表現は関数の中に literal で置く(外の const にしない)。** モジュール直下の const は
|
|
2364
|
+
* tsc が `exports.X` に書き換え、`.toString()` で客先へ渡ったこの関数がブラウザで未定義を参照する
|
|
2365
|
+
* ——ライブ取得が丸ごと落ちて画面が静的表示に戻る(`isCalendarId` の項と同じ穴・設計レビュー🔴1)。
|
|
2366
|
+
*
|
|
2367
|
+
* @returns 読めたときだけ。`partial` は「1 件以上取り込めて、1 件以上失敗した」
|
|
2368
|
+
*
|
|
2369
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
2370
|
+
*/
|
|
2371
|
+
function ingestFacts(row) {
|
|
2372
|
+
if (!row || row.question !== "ingested")
|
|
2373
|
+
return null;
|
|
2374
|
+
// 🔴 **`skipped`(鍵が無くて取り込みを始めていない)の行は部分失敗にしない。** 件数が
|
|
2375
|
+
// 付いていても、この行が言いたいのは「◯が未登録です」で、部分失敗として飲み込むと
|
|
2376
|
+
// **鍵を入れるべき案件に「接続OK」と出る**(取るべき行動が違う・#225 が分けたかったもの)
|
|
2377
|
+
var c = row.status === "skipped" ? null : row.counts;
|
|
2378
|
+
// 🔴 **`ok` と `failed` のどちらも読めない `counts` は「新形式」と見なさない。** 契約は
|
|
2379
|
+
// 3 つの鍵を個別に任意にしているので、`{ skipped: 38 }` だけの行がありうる。そこで即
|
|
2380
|
+
// 「0 件取り込み・0 件失敗」と答えると、**同じ行の `detail` にある件数が読まれないまま
|
|
2381
|
+
// 「同期が失敗しています」に戻る**——便 2 が半分だけ移った窓(counts は書き始めたが
|
|
2382
|
+
// status はまだ `failed`)で起きる。読めないなら下の橋へ落とす
|
|
2383
|
+
if (c && typeof c === "object" && !Array.isArray(c) && (typeof c.ok === "number" || typeof c.failed === "number")) {
|
|
2384
|
+
var cok = typeof c.ok === "number" ? c.ok : 0;
|
|
2385
|
+
var cfail = typeof c.failed === "number" ? c.failed : 0;
|
|
2386
|
+
var cskip = typeof c.skipped === "number" ? c.skipped : 0;
|
|
2387
|
+
return { ok: cok, failed: cfail, skipped: cskip, partial: cok > 0 && cfail > 0, legacy: false };
|
|
2388
|
+
}
|
|
2389
|
+
// ---- 旧形式の橋(便 2 で消してよい・上の項) ----
|
|
2390
|
+
if (row.status !== "failed")
|
|
2391
|
+
return null;
|
|
2392
|
+
var d = typeof row.detail === "string" ? row.detail : "";
|
|
2393
|
+
var mo = /([0-9][0-9,]*)\s*件取り込み/.exec(d);
|
|
2394
|
+
var mf = /([0-9][0-9,]*)\s*件失敗/.exec(d);
|
|
2395
|
+
// **件数を持たない失敗はここへ来ない。** 「フォルダを開けないため取り込めませんでした」
|
|
2396
|
+
// 「取り込みが途中で終わりました」は件数の話ではないので、失敗のまま残るのが正しい
|
|
2397
|
+
if (!mo || !mf)
|
|
2398
|
+
return null;
|
|
2399
|
+
var lok = parseInt(String(mo[1]).replace(/,/g, ""), 10);
|
|
2400
|
+
var lfail = parseInt(String(mf[1]).replace(/,/g, ""), 10);
|
|
2401
|
+
if (!(lok >= 0) || !(lfail >= 0))
|
|
2402
|
+
return null;
|
|
2403
|
+
var ms = /([0-9][0-9,]*)\s*件は取り込めない/.exec(d);
|
|
2404
|
+
var lskip = ms ? parseInt(String(ms[1]).replace(/,/g, ""), 10) : 0;
|
|
2405
|
+
// `0件取り込み・1件失敗`(フォルダを開けなかった回)は partial にならない=失敗のまま
|
|
2406
|
+
return { ok: lok, failed: lfail, skipped: lskip >= 0 ? lskip : 0, partial: lok > 0 && lfail > 0, legacy: true };
|
|
2407
|
+
}
|
|
2408
|
+
/**
|
|
2409
|
+
* その行が「取り込めた」と言えるか(#634)。`ok` か、部分失敗(旧形式を含む)。
|
|
2410
|
+
*
|
|
2411
|
+
* **「最終同期」の材料**(`ingestedAtOf` / `ingestedRowOf`)。251 件取り込んだ回は
|
|
2412
|
+
* **実際に取り込めている**ので、その回の時刻を「最終同期」として出すのが R4
|
|
2413
|
+
* (「最終同期」は取り込めた時刻だけ)に沿う。旧形式では `status` が `failed` なので、
|
|
2414
|
+
* ここを通さないと**前回の成功時刻が出続ける**(設計レビュー🟡11)。
|
|
2415
|
+
*
|
|
2416
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
2417
|
+
*/
|
|
2418
|
+
function ingestSucceeded(row) {
|
|
2419
|
+
if (!row)
|
|
2420
|
+
return false;
|
|
2421
|
+
if (row.status === "ok")
|
|
2422
|
+
return true;
|
|
2423
|
+
var f = ingestFacts(row);
|
|
2424
|
+
return !!(f && f.partial);
|
|
2425
|
+
}
|
|
2065
2426
|
/**
|
|
2066
2427
|
* `stateNow` から、その能力の「最後に取り込めた時刻」を引く(#227 の時刻の一貫化)。
|
|
2067
2428
|
*
|
|
@@ -2087,8 +2448,14 @@ function ingestedAtOf(rows, capId, refs) {
|
|
|
2087
2448
|
continue;
|
|
2088
2449
|
if (refs && !refs[stateRefKey(capId, r.ref, r.workspace)])
|
|
2089
2450
|
continue;
|
|
2090
|
-
|
|
2091
|
-
|
|
2451
|
+
// **部分失敗の回も「取り込めた」**(#634・`ingestSucceeded`)。251 件は実際にこの回で
|
|
2452
|
+
// 取り込めているので、前回の成功時刻を出し続けるほうが嘘になる。
|
|
2453
|
+
// 履歴(`lastOkAt`)と今回の時刻の**新しいほう**を採る——同じ 1 行の中の 2 つの時刻で、
|
|
2454
|
+
// どちらもミリ秒ありの ISO に正規化済み(サーバの `normalizeAt`)なので辞書順で比べられる
|
|
2455
|
+
const hist = typeof r.lastOkAt === "string" ? r.lastOkAt : "";
|
|
2456
|
+
const okAt = ingestSucceeded(r) && typeof r.at === "string" ? r.at : "";
|
|
2457
|
+
const at = okAt > hist ? okAt : hist;
|
|
2458
|
+
if (at > best)
|
|
2092
2459
|
best = at;
|
|
2093
2460
|
}
|
|
2094
2461
|
return best;
|
|
@@ -2111,8 +2478,12 @@ function ingestedRowOf(rows, capId, refs) {
|
|
|
2111
2478
|
continue;
|
|
2112
2479
|
if (refs && !refs[stateRefKey(capId, r.ref, r.workspace)])
|
|
2113
2480
|
continue;
|
|
2114
|
-
|
|
2115
|
-
|
|
2481
|
+
// 選び方は `ingestedAtOf` と**同じ 1 本**(分かれると「最終同期の時刻」と
|
|
2482
|
+
// 「そのリンク先の run」が別の行のものになる)
|
|
2483
|
+
const hist = typeof r.lastOkAt === "string" ? r.lastOkAt : "";
|
|
2484
|
+
const okAt = ingestSucceeded(r) && typeof r.at === "string" ? r.at : "";
|
|
2485
|
+
const at = okAt > hist ? okAt : hist;
|
|
2486
|
+
if (at > best) {
|
|
2116
2487
|
best = at;
|
|
2117
2488
|
bestRow = r;
|
|
2118
2489
|
}
|
|
@@ -2146,9 +2517,13 @@ function runLogUrlOf(row) {
|
|
|
2146
2517
|
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
2147
2518
|
*/
|
|
2148
2519
|
function lastSyncRunUrlOf(row) {
|
|
2149
|
-
|
|
2520
|
+
// **部分失敗の回も「取り込めた」(#634)。** ここを `status === "ok"` のままにすると、
|
|
2521
|
+
// 「最終同期」の時刻は部分失敗の回のものになったのに、その時刻を書いた run へ行けない
|
|
2522
|
+
// ——R9(その時刻を書いた run にだけリンクを出す)が、まさにこの便の対象の回で満たせない
|
|
2523
|
+
if (!ingestSucceeded(row))
|
|
2150
2524
|
return "";
|
|
2151
|
-
|
|
2525
|
+
// 「最終同期」として出した時刻がこの行の `at` でないなら、リンク先の run は別の回のもの
|
|
2526
|
+
if (row.lastOkAt && row.lastOkAt > row.at)
|
|
2152
2527
|
return "";
|
|
2153
2528
|
return runLogUrlOf(row);
|
|
2154
2529
|
}
|
|
@@ -2307,7 +2682,12 @@ const TILE_CAPS = {
|
|
|
2307
2682
|
// `cap: "google-chat"` の行は夜間の Gold 昇格が書く(#153 PR-E で結線済み)ので、
|
|
2308
2683
|
// 夜間が一度走れば規則2/3(⚠️ / 接続OK)まで進む。走る前は規則4(実績なし→中立)
|
|
2309
2684
|
"チャット-google-chat": { id: "google-chat", question: "reachable", noFact: "登録済み(接続は未確認です)", keyLabel: "", refKeyed: true },
|
|
2310
|
-
|
|
2685
|
+
// 🔴 **`alsoBad: ["ingested"]` は `devExcludedSkip` の篩と一対で足した(#640)。** 夜間が
|
|
2686
|
+
// 「接続はできるが Gold の材料にしていない」を `ingested: skipped` で書くようになった
|
|
2687
|
+
// (engine #399)ので、`reachable` だけを見ていると画面が「読み取り中」と嘘をつく。
|
|
2688
|
+
// ただし `BAD_STATE_STATUSES.ingested` は `skipped` を**悪い**と数えるので、篩を外すと
|
|
2689
|
+
// **GitHub を登録した全案件のタイルが ⚠️「取り込みを飛ばしました」**になる
|
|
2690
|
+
"開発": { id: "dev", question: "reachable", noFact: "登録済み(接続は未確認です)", keyLabel: "", alsoBad: ["ingested"], refKeyed: true },
|
|
2311
2691
|
};
|
|
2312
2692
|
/**
|
|
2313
2693
|
* 登録したばかりで、まだ実測の行が1件も届いていない接続先があるときの語(#523 決定6)。
|
|
@@ -2701,7 +3081,15 @@ function deriveCapTile(cap, ctx) {
|
|
|
2701
3081
|
var skipped = 0;
|
|
2702
3082
|
var noToken = 0;
|
|
2703
3083
|
var unreachable = 0;
|
|
3084
|
+
// 「接続はできるが Gold の材料にしていない」接続先(#640)。**警告には数えない。**
|
|
3085
|
+
// 鮮度の門(古い中立を捨てる)は `devExcludedRefs` が持つ——ここに書き写すと、
|
|
3086
|
+
// 行・Home・タイルで「いつ古いと見なすか」が割れる
|
|
3087
|
+
var excludedRefs = meta.id === "dev" ? devExcludedRefs(rows) : {};
|
|
3088
|
+
var excluded = 0;
|
|
2704
3089
|
var okSeen = false;
|
|
3090
|
+
// 部分失敗の内訳の合計(#634)。**能力の全接続先を足す**——タイルは能力ぜんたいを 1 語で言う
|
|
3091
|
+
var sumOk = 0;
|
|
3092
|
+
var sumFailed = 0;
|
|
2705
3093
|
for (var i = 0; i < rows.length; i++) {
|
|
2706
3094
|
var r = rows[i];
|
|
2707
3095
|
if (!r || r.cap !== meta.id)
|
|
@@ -2714,12 +3102,44 @@ function deriveCapTile(cap, ctx) {
|
|
|
2714
3102
|
// 出続ける(engine #314 の症状の候補3)。**篩い方の正本は stateRefKey 1箇所**
|
|
2715
3103
|
if (refs && meta.refKeyed && !refs[stateRefKey(meta.id, r.ref, r.workspace)])
|
|
2716
3104
|
continue;
|
|
3105
|
+
// 🔴 **中立の事実を、悪い語の表に掛ける前に抜く(#640)。** `BAD_STATE_STATUSES.ingested`
|
|
3106
|
+
// は `skipped` を悪いと数える(鍵が無くて始めていない、の意味)ので、ここを外すと
|
|
3107
|
+
// **GitHub を登録した全案件のタイルが ⚠️ になる**(engine #399 実装レビュー🟡3)。
|
|
3108
|
+
// 数えるのは**鮮度の門を通った分だけ**——性質を「会話」に直した接続先の古い行は
|
|
3109
|
+
// ここで黙って落とす(⚠️ にもしない。直すものが無い過去の事実なので)。
|
|
3110
|
+
// 件数は規則3 の見出しへ回す。「読まないと決めた」を黙って消すと、画面が
|
|
3111
|
+
// 「全部読んでいます」と読める(#523「常に真実を表示する」)
|
|
3112
|
+
if (devExcludedSkip(r))
|
|
3113
|
+
continue;
|
|
2717
3114
|
// **一度でも成功していれば実績あり。** `lastOkAt` を見るのは、いま失敗している行でも
|
|
2718
3115
|
// 「繋がったことがある」のは事実だから(規則2が先に立つので表示は⚠️のまま)。
|
|
2719
3116
|
// **実績は `question`(その能力が「実際に動いた」と言える問い)の側だけで数える**
|
|
2720
3117
|
// ——Drive に届いていても取り込みが0件なら「まだ取り込みがありません」が事実
|
|
2721
3118
|
if (r.question === meta.question && (r.status === "ok" || r.lastOkAt))
|
|
2722
3119
|
okSeen = true;
|
|
3120
|
+
// 🔴 **部分失敗は「失敗」と数えない(#634・オーナー決定 2026-09-16)。** 251 件成功・
|
|
3121
|
+
// 1 件失敗の行を `failed` に数えると、規則2 が能力ぜんたいを「同期が失敗しています」に
|
|
3122
|
+
// 丸める——99.6% 取り込めているのに「共有資料が壊れている」と読める画面になる。
|
|
3123
|
+
// **取り込めた実績はあるので `okSeen` は立てる**(旧形式の行は `status: "failed"` なので、
|
|
3124
|
+
// ここで立てないと規則4 の「まだ取り込みがありません」に落ちる)。
|
|
3125
|
+
// 件数は規則3 の見出しへ回す。判定の実体は `ingestFacts` 1 本(タイル・Home・行の札で共有)
|
|
3126
|
+
var facts = r.question === meta.question ? ingestFacts(r) : null;
|
|
3127
|
+
if (facts) {
|
|
3128
|
+
sumOk += facts.ok;
|
|
3129
|
+
sumFailed += facts.failed;
|
|
3130
|
+
}
|
|
3131
|
+
if (facts && facts.partial) {
|
|
3132
|
+
okSeen = true;
|
|
3133
|
+
continue;
|
|
3134
|
+
}
|
|
3135
|
+
// 🔴 **`ok` と名乗っていても 1 件も取り込めていなければ失敗として数える(#634)。**
|
|
3136
|
+
// `failed` を「1 件も取り込めなかった」に限定した約束の裏返しで、書き手が取り違えると
|
|
3137
|
+
// 見出しが「接続OK(0 件取り込み・3 件失敗)」という**自分で矛盾した文**になる。
|
|
3138
|
+
// 契約では弾かない(行ごと落とすと失敗そのものが画面から消える)ので、ここで倒す
|
|
3139
|
+
if (facts && facts.ok === 0 && facts.failed > 0) {
|
|
3140
|
+
failed++;
|
|
3141
|
+
continue;
|
|
3142
|
+
}
|
|
2723
3143
|
var bad = BAD_STATE_STATUSES[r.question] || [];
|
|
2724
3144
|
if (bad.indexOf(r.status) < 0)
|
|
2725
3145
|
continue;
|
|
@@ -2732,6 +3152,16 @@ function deriveCapTile(cap, ctx) {
|
|
|
2732
3152
|
else
|
|
2733
3153
|
unreachable++;
|
|
2734
3154
|
}
|
|
3155
|
+
// 「材料にしていない」は**接続先ごとに 1 件**(#640)。行を数えると、同じ ref の古い行が
|
|
3156
|
+
// 残っている案件で件数が水増しされる。孤児の篩(#523 決定2)はここにも掛ける
|
|
3157
|
+
// ——外した repo の中立の行が「読み取りの対象外が 3 件」に数え続けられないように
|
|
3158
|
+
for (var ek in excludedRefs) {
|
|
3159
|
+
if (!Object.prototype.hasOwnProperty.call(excludedRefs, ek))
|
|
3160
|
+
continue;
|
|
3161
|
+
if (refs && meta.refKeyed && !refs[stateRefKey(meta.id, ek)])
|
|
3162
|
+
continue;
|
|
3163
|
+
excluded++;
|
|
3164
|
+
}
|
|
2735
3165
|
// ---- 規則2: 実測が悪い → 警告 ----
|
|
2736
3166
|
//
|
|
2737
3167
|
// **失敗を先に言う。** 「鍵が無くて飛ばした」より「鍵はあるのに取れなかった」ほうが
|
|
@@ -2753,6 +3183,19 @@ function deriveCapTile(cap, ctx) {
|
|
|
2753
3183
|
return { rule: 2, state: "warn", text: unreachable + "件が読めていません", keyMissing: false };
|
|
2754
3184
|
}
|
|
2755
3185
|
// ---- 規則3: 実績あり → 接続OK ----
|
|
3186
|
+
//
|
|
3187
|
+
// **部分失敗は件数で言う(#634)。** 「接続OK」の一言で丸めると 1 件の失敗が画面から消え、
|
|
3188
|
+
// 「同期が失敗しています」で丸めると 251 件の成功が消える。**帯は症状だけ**
|
|
3189
|
+
// (`ui-wording-symptom-then-one-action`)——何を直すかはタイルの中の失敗の行が言う
|
|
3190
|
+
if (okSeen && sumFailed > 0) {
|
|
3191
|
+
return { rule: 3, state: "ok", text: "接続OK(" + sumOk + " 件取り込み・" + sumFailed + " 件失敗)", keyMissing: false };
|
|
3192
|
+
}
|
|
3193
|
+
// **読みに行かないと決めた接続先は、件数で言う(#640)。** 「接続OK」の一言で丸めると
|
|
3194
|
+
// 「決定・用語の材料になっている」と読めるし、⚠️ に落とすと直すものが無いのに直しに
|
|
3195
|
+
// 行かせる。**帯は症状だけ**——性質を変える手順は行の補足が 1 行だけ持つ
|
|
3196
|
+
if (okSeen && excluded > 0) {
|
|
3197
|
+
return { rule: 3, state: "ok", text: "接続OK(読み取りの対象外が " + excluded + " 件)", keyMissing: false };
|
|
3198
|
+
}
|
|
2756
3199
|
if (okSeen)
|
|
2757
3200
|
return { rule: 3, state: "ok", text: "接続OK", keyMissing: false };
|
|
2758
3201
|
// ---- 規則4: 実績なし → 中立 ----
|
|
@@ -2765,6 +3208,56 @@ function deriveCapTile(cap, ctx) {
|
|
|
2765
3208
|
return { rule: 4, state: "idle", text: STATE_CONFIRMING_TEXT, keyMissing: false };
|
|
2766
3209
|
return { rule: 4, state: "idle", text: meta.noFact, keyMissing: false };
|
|
2767
3210
|
}
|
|
3211
|
+
/**
|
|
3212
|
+
* その能力で取り込めなかったファイルの一覧(#634)。**タイルの中に 1 行ずつ出す材料。**
|
|
3213
|
+
*
|
|
3214
|
+
* 選び方は `ingestedAtOf` と同じ篩(`cap` / `question === "ingested"` / 登録簿の `refs`)。
|
|
3215
|
+
* 孤児行(外した接続先の残骸)の失敗を並べない——外したフォルダのファイル名が
|
|
3216
|
+
* いつまでも画面に残る(#523 決定2 と同じ理由)。
|
|
3217
|
+
*
|
|
3218
|
+
* 旧形式の行には一覧が無い(`detail` は件数しか持たない)ので 0 件になる。**それが橋の限界**で、
|
|
3219
|
+
* 便 1 では帯の件数だけが出る。便 2 で書き手が `failures` を書き始めると行が並ぶ。
|
|
3220
|
+
*
|
|
3221
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
3222
|
+
* `stateRefKey` / `INGEST_FAILURES_MAX` はこれより前に流し込んである)。
|
|
3223
|
+
*/
|
|
3224
|
+
function ingestFailureRows(rows, capId, refs) {
|
|
3225
|
+
var out = [];
|
|
3226
|
+
if (!Array.isArray(rows) || !capId)
|
|
3227
|
+
return out;
|
|
3228
|
+
for (var i = 0; i < rows.length; i++) {
|
|
3229
|
+
var r = rows[i];
|
|
3230
|
+
if (!r || r.cap !== capId || r.question !== "ingested")
|
|
3231
|
+
continue;
|
|
3232
|
+
if (refs && !refs[stateRefKey(capId, r.ref, r.workspace)])
|
|
3233
|
+
continue;
|
|
3234
|
+
if (!Array.isArray(r.failures))
|
|
3235
|
+
continue;
|
|
3236
|
+
for (var j = 0; j < r.failures.length; j++) {
|
|
3237
|
+
if (out.length >= INGEST_FAILURES_MAX)
|
|
3238
|
+
return out;
|
|
3239
|
+
var f = r.failures[j];
|
|
3240
|
+
if (f && f.name)
|
|
3241
|
+
out.push(f);
|
|
3242
|
+
}
|
|
3243
|
+
}
|
|
3244
|
+
return out;
|
|
3245
|
+
}
|
|
3246
|
+
/**
|
|
3247
|
+
* 取り込めなかったファイル 1 件の 1 行(#634)。**行は一手**(`ui-wording-symptom-then-one-action`)。
|
|
3248
|
+
*
|
|
3249
|
+
* 一手を添えるのは `transient`(相手側の一時エラー)だけ。**`permanent` には足さない**
|
|
3250
|
+
* ——恒久エラーの直し方は理由ごとに違う(権限を直す/形式を変える)ので、一律の案内を
|
|
3251
|
+
* ここで作ると嘘になる。何を直すかは書き手が `reason` に書く。
|
|
3252
|
+
*
|
|
3253
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
3254
|
+
*/
|
|
3255
|
+
function ingestFailureText(f) {
|
|
3256
|
+
var name = String((f && f.name) || "");
|
|
3257
|
+
var reason = String((f && f.reason) || "");
|
|
3258
|
+
var line = reason ? name + " — " + reason : name;
|
|
3259
|
+
return f && f.kind === "transient" ? line + "(次回の取り込みで再試行します)" : line;
|
|
3260
|
+
}
|
|
2768
3261
|
/**
|
|
2769
3262
|
* 「まとめ表が知らない行がある」能力のタイルに、行と同じ答えを出せるか(#576)。
|
|
2770
3263
|
*
|
|
@@ -2921,6 +3414,13 @@ function liveRowVerdict(regKey, reg, rows, question) {
|
|
|
2921
3414
|
return null;
|
|
2922
3415
|
if (hit.status === "ok")
|
|
2923
3416
|
return { status: "ok", row: hit };
|
|
3417
|
+
// 🔴 **部分失敗は「承認待ち」と言わない(#634)。** タイル(`deriveCapTile`)が
|
|
3418
|
+
// 「接続OK(251 件取り込み・1 件失敗)」と言っている横で、Home の同じ接続先が
|
|
3419
|
+
// 「承認待ち」になると、同じ画面の 2 か所が別のことを言う(R10)。判定は `ingestFacts` 1 本。
|
|
3420
|
+
// **`row` は必ず添える**(呼び手が `v.row.at` を読む・設計レビュー🟡10)
|
|
3421
|
+
var hitFacts = ingestFacts(hit);
|
|
3422
|
+
if (hitFacts && hitFacts.partial)
|
|
3423
|
+
return { status: "ok", row: hit };
|
|
2924
3424
|
var bad = BAD_STATE_STATUSES[question] || [];
|
|
2925
3425
|
if (bad.indexOf(hit.status) >= 0)
|
|
2926
3426
|
return { status: "bad", row: hit };
|
|
@@ -3043,12 +3543,28 @@ function liveToolItems(tools, registered, rows) {
|
|
|
3043
3543
|
}
|
|
3044
3544
|
return "reachable";
|
|
3045
3545
|
};
|
|
3546
|
+
// 「いま材料にしていない」接続先(#640)。**ループの外で 1 回だけ作る**
|
|
3547
|
+
//(行ごとに作り直すと、行数 × 項目数の走査になる)
|
|
3548
|
+
var excludedRefs = devExcludedRefs(rows);
|
|
3046
3549
|
var stateOf = function (regKey, regRow, fallback) {
|
|
3047
3550
|
var v = liveRowVerdict(regKey, regRow, rows, questionOf(regKey));
|
|
3048
3551
|
if (!v)
|
|
3049
3552
|
return fallback;
|
|
3050
|
-
if (v.status === "ok")
|
|
3553
|
+
if (v.status === "ok") {
|
|
3554
|
+
// 「接続できる」と「Gold の材料にしている」を別に出す(#640・engine #399)。
|
|
3555
|
+
// **届いていない行には出さない**(`v.status === "ok"` の中だけ)——「アクセス不可」の
|
|
3556
|
+
// ほうが直す一手を持っているので、そちらを勝たせる(帯は症状)。
|
|
3557
|
+
// 判定は `devExcludedSkip` 1 本(タイル・操作タブの行と共有)、行の突き合わせは
|
|
3558
|
+
// `liveRowVerdict` 1 本(鍵の畳み方・複数行の勝ち方を 2 枚にしない・R10)
|
|
3559
|
+
// 鮮度の門も 1 本(`devExcludedRefs`)を通す——性質を「会話」に直した接続先の
|
|
3560
|
+
// 古い行で、Home だけが「読み取りの対象外」と言い続けないように
|
|
3561
|
+
if (regKey === "dev") {
|
|
3562
|
+
var iv = liveRowVerdict(regKey, regRow, rows, "ingested");
|
|
3563
|
+
if (iv && devExcludedSkip(iv.row) && excludedRefs[String(iv.row.ref)])
|
|
3564
|
+
return "ok_not_ingested";
|
|
3565
|
+
}
|
|
3051
3566
|
return "reading";
|
|
3567
|
+
}
|
|
3052
3568
|
if (v.status === "bad")
|
|
3053
3569
|
return "waiting";
|
|
3054
3570
|
return "unknown";
|
|
@@ -4547,7 +5063,7 @@ a.ds-tile:hover { text-decoration: none; }
|
|
|
4547
5063
|
.ds-check-label, .ds-check-action, .ds-check-detail,
|
|
4548
5064
|
.ds-pipe-label, .ds-pipe-todo,
|
|
4549
5065
|
.ds-empty, .sec-how, .modal-desc, .ds-toggle-msg,
|
|
4550
|
-
.ds-drive-note, .ds-inactive-note, .ds-gate-help,
|
|
5066
|
+
.ds-drive-note, .ds-inactive-note, .ds-gate-help, .ds-not-ingested-note,
|
|
4551
5067
|
/* 案件カレンダーの行(#428)。**カレンダー ID は空白の無い長い連なり**で、
|
|
4552
5068
|
失敗の理由(detail)も長さが読めない。ここを外すと会議のモーダルが横に溢れる */
|
|
4553
5069
|
.mt-cal-banner, .mt-cal-note, .mt-cal-warn, .mt-cal-primary, .mt-cal-sub, .mt-cal-id .rt-copy-text,
|
|
@@ -4596,6 +5112,13 @@ a.ds-tile:hover { text-decoration: none; }
|
|
|
4596
5112
|
トグルを撤去したいま、読み取られていないことを示すのはこのバッジだけ。
|
|
4597
5113
|
**判断のバッジなので、由来や組織のバッジより強く出す** */
|
|
4598
5114
|
.badge.ds-excluded { background: #f3f4f6; color: #4b5563; border: 1px solid #d1d5db; }
|
|
5115
|
+
/* 「接続はできるが Gold の材料にしていない」印(#640)。**「読み取り停止中」と同じ灰色**
|
|
5116
|
+
——どちらも「いまこの接続先がどう扱われているか」の印で、直すものが無い中立の事実。
|
|
5117
|
+
橙(警告)にすると、直しようのないものを直しに行かせる */
|
|
5118
|
+
.badge.ds-not-ingested { background: #f3f4f6; color: #4b5563; border: 1px solid #d1d5db; }
|
|
5119
|
+
/* その理由と、変えたいときの一手(1 行だけ)。**行の補足(.ds-gate-help)と同じ見た目**だが、
|
|
5120
|
+
「接続を確認」で隠される棚には置かない(押しても変わらない事実のため) */
|
|
5121
|
+
.ds-not-ingested-note { font-size: 12px; color: #6b7280; line-height: 1.6; margin: 4px 0 0; flex: 1 1 100%; }
|
|
4599
5122
|
/* 登録の由来(#118)。**薄く出す**——読み取りの可否や Gold 対象かより弱い情報で、
|
|
4600
5123
|
ここが目立つと「除外」「未宣言」の判断のバッジと同じ強さに見えてしまう */
|
|
4601
5124
|
/* どの Slack ワークスペースのチャンネルか(#104)。**由来のバッジと同じ強さにする**——
|
|
@@ -5192,7 +5715,24 @@ var STATE_ROW_FIELDS = ${JSON.stringify(STATE_ROW_FIELDS)};
|
|
|
5192
5715
|
var RUN_URL_RE = new RegExp(${JSON.stringify(RUN_URL_RE.source)});
|
|
5193
5716
|
var RUN_URL_WRITERS = ${JSON.stringify(RUN_URL_WRITERS)};
|
|
5194
5717
|
var RUN_SOURCE_WORDS = ${JSON.stringify(RUN_SOURCE_WORDS)};
|
|
5718
|
+
// 部分失敗の内訳の欄(#634)。🔴 **この 6 行を落とすと、流し込んだ sanitizeIngestCounts /
|
|
5719
|
+
// sanitizeIngestFailures が未定義を参照して、ライブ取得が丸ごと死ぬ**(定数は export できないので
|
|
5720
|
+
// 値はここで注入する。「STATE_STATUSES」 と同じ作法)
|
|
5721
|
+
var INGEST_FAILURE_KINDS = ${JSON.stringify(INGEST_FAILURE_KINDS)};
|
|
5722
|
+
var INGEST_FAILURES_MAX = ${JSON.stringify(INGEST_FAILURES_MAX)};
|
|
5723
|
+
var INGEST_TEXT_MAX = ${JSON.stringify(INGEST_TEXT_MAX)};
|
|
5724
|
+
var INGEST_COUNT_KEYS = ${JSON.stringify(INGEST_COUNT_KEYS)};
|
|
5725
|
+
var INGEST_COUNT_MAX = ${JSON.stringify(INGEST_COUNT_MAX)};
|
|
5726
|
+
var INGEST_TILE_ROWS = ${JSON.stringify(INGEST_TILE_ROWS)};
|
|
5727
|
+
${sanitizeIngestCounts.toString()}
|
|
5728
|
+
${sanitizeIngestFailures.toString()}
|
|
5195
5729
|
${sanitizeStateNow.toString()}
|
|
5730
|
+
// 「接続はできるが Gold の材料にしていない」中立の行(#640)。🔴 **overlayStateOnFleet /
|
|
5731
|
+
// deriveCapTile / liveToolItems / 行の併記が全部この 1 本を引く。** 落とすと 4 か所が
|
|
5732
|
+
// 未定義を参照して、ライブ取得が丸ごと落ちる(画面が静的表示に戻る)
|
|
5733
|
+
${devExcludedSkip.toString()}
|
|
5734
|
+
${devExcludedRefs.toString()}
|
|
5735
|
+
${devExcludedLabel.toString()}
|
|
5196
5736
|
${overlayStateOnFleet.toString()}
|
|
5197
5737
|
// Home の「使用ツール」(#275)。**判定と表示語の実体はビルド時の定義**——
|
|
5198
5738
|
// 画面側に書き写すと、状態の言い換えが 2 枚になって片方だけ直る。
|
|
@@ -5222,6 +5762,10 @@ ${splitNeededParts.toString()}
|
|
|
5222
5762
|
${stateRefKey.toString()}
|
|
5223
5763
|
${registeredRefKeys.toString()}
|
|
5224
5764
|
${fleetLastSyncOf.toString()}
|
|
5765
|
+
// 部分失敗の判定(#634)。🔴 **ingestedAtOf / deriveCapTile / liveRowVerdict / ingestedEl が
|
|
5766
|
+
// すべてこの 1 本を引く。** 落とすと「最終同期」もタイルも Home も未定義を参照して落ちる
|
|
5767
|
+
${ingestFacts.toString()}
|
|
5768
|
+
${ingestSucceeded.toString()}
|
|
5225
5769
|
${ingestedAtOf.toString()}
|
|
5226
5770
|
${ingestedRowOf.toString()}
|
|
5227
5771
|
${runLogUrlOf.toString()}
|
|
@@ -5234,6 +5778,9 @@ var BAD_STATE_STATUSES = ${JSON.stringify(BAD_STATE_STATUSES)};
|
|
|
5234
5778
|
var STATE_CONFIRMING_TEXT = ${JSON.stringify(STATE_CONFIRMING_TEXT)};
|
|
5235
5779
|
${keyMissingText.toString()}
|
|
5236
5780
|
${deriveCapTile.toString()}
|
|
5781
|
+
// タイルの中の失敗の行(#634)。**stateRefKey / INGEST_FAILURES_MAX より後に置く**
|
|
5782
|
+
${ingestFailureRows.toString()}
|
|
5783
|
+
${ingestFailureText.toString()}
|
|
5237
5784
|
// 登録直後のタイル(#576)。**registeredRefKeys / stateRefKey / BAD_STATE_STATUSES より後に置く**
|
|
5238
5785
|
//(上の 3 つを引く。前に置くと var の BAD_STATE_STATUSES が undefined で判定が丸ごと死ぬ)
|
|
5239
5786
|
${derivePendingTile.toString()}
|
|
@@ -8376,10 +8923,20 @@ ${calendarAddUrl.toString()}
|
|
|
8376
8923
|
// Issues と Discussions は同じ接続先(同じ dev 行)なので、理由・補足は1本目だけが
|
|
8377
8924
|
// 持つ(#346)。同じ長文が2行に並ぶと一覧が案内で埋まる
|
|
8378
8925
|
var seenRef = {};
|
|
8926
|
+
// 「材料にしていない」の 1 行は、**この組織で実際に出した行があってから**畳む(#640)。
|
|
8927
|
+
// 🔴 「seenRef」 を写経して「1 本目かどうか」でフラグを立てると、1 本目が
|
|
8928
|
+
// 併記を持たない行だったときにフラグだけ消費され、**その組織のどの行にも
|
|
8929
|
+
// 補足が出ない**(設計レビュー🟡5)。**出した行だけを数える**
|
|
8930
|
+
var shownExcluded = false;
|
|
8379
8931
|
var cards = g.items.map(function (x) {
|
|
8380
8932
|
var again = !!(x.ref && seenRef[x.ref]);
|
|
8381
8933
|
if (x.ref) seenRef[x.ref] = 1;
|
|
8382
|
-
|
|
8934
|
+
// 🔴 **フラグを立てるのは、その行が実際に補足を出すときだけ。** 出さない行
|
|
8935
|
+
//(同じ ref の 2 本目・「読み取り停止中」)でフラグを消費すると、**その組織の
|
|
8936
|
+
// どの行にも補足が出ない**(設計レビュー🟡5 と同じ罠。条件は 「extSourceCard」 と揃える)
|
|
8937
|
+
var quietExcluded = shownExcluded;
|
|
8938
|
+
if (!again && x && x.ingestExcluded === true && x.goldState !== "excluded") shownExcluded = true;
|
|
8939
|
+
return extSourceCard(x, DS_TYPE[x.type] || { icon: "🔗", label: x.type || "その他" }, { quietHelp: again, quietExcluded: quietExcluded });
|
|
8383
8940
|
});
|
|
8384
8941
|
if (g.state === "warn") {
|
|
8385
8942
|
out.push(orgNoteBox(githubNotConnectedNodes(g), cards));
|
|
@@ -9355,15 +9912,23 @@ ${calendarAddUrl.toString()}
|
|
|
9355
9912
|
/**
|
|
9356
9913
|
* 登録したばかりで、まだ実測の行が1件も届いていない接続先があるか(#523 決定6)。
|
|
9357
9914
|
*
|
|
9358
|
-
* **対象は取り込む4
|
|
9915
|
+
* **対象は取り込む4能力だけ**(実績の問いが 「ingested」 の能力=規則4の文言が
|
|
9359
9916
|
* 「まだ取り込みがありません」の系)。チャット・開発の規則4は既に
|
|
9360
9917
|
* 「接続は未確認です」と言っており、待っていることが読める。
|
|
9361
9918
|
*
|
|
9919
|
+
* 🔴 **判定に 「alsoBad」 の有無を使わない(#640)。** かつては「取り込む4能力は
|
|
9920
|
+
* alsoBad を持つ」という**偶然の一致**を代用していたが、あちらは
|
|
9921
|
+
* 「実績の問いとは別に、悪ければ⚠️にする問い」を並べる欄で、意味が違う。
|
|
9922
|
+
* 開発に 「alsoBad: ["ingested"]」 を足した瞬間、**GitHub を登録した直後15分の
|
|
9923
|
+
* 開発タイルが「登録済み(接続は未確認です)」から「確認しています…」に黙って変わる**
|
|
9924
|
+
* ——今日の 7 能力では 「question === "ingested"」 と同値なので、表示は 1 バイトも
|
|
9925
|
+
* 変わらないまま、意図(規則4の文言の系)をそのまま述語にする。
|
|
9926
|
+
*
|
|
9362
9927
|
* 上限は 「UNSETTLED_TTL_MS」(15分)。過ぎたら中立文に戻る——**待てば出ると言い続けない**。
|
|
9363
9928
|
*/
|
|
9364
9929
|
function capConfirming(derCap) {
|
|
9365
9930
|
var meta = TILE_CAPS[derCap];
|
|
9366
|
-
if (!meta ||
|
|
9931
|
+
if (!meta || meta.question !== "ingested") return false;
|
|
9367
9932
|
var ops = pendingOpsNow();
|
|
9368
9933
|
var now = Date.now();
|
|
9369
9934
|
for (var i = 0; i < ops.length; i++) {
|
|
@@ -10762,6 +11327,24 @@ ${calendarAddUrl.toString()}
|
|
|
10762
11327
|
if (tokenRelevant && sk && secretKindsShown().indexOf(sk) >= 0 && !secretPresentForTile(sk)) {
|
|
10763
11328
|
body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ トークンが未設定です" }));
|
|
10764
11329
|
}
|
|
11330
|
+
// 取り込めなかったファイル(#634)。**帯は症状(件数)・行は一手。**
|
|
11331
|
+
// 帯が「接続OK(251 件取り込み・1 件失敗)」と言っているのに、どのファイルが取り込めて
|
|
11332
|
+
// いないのかが画面から分からないと、利用者は run のログを読みに行くしかない
|
|
11333
|
+
// (=案件リポに入っていない人には辿れない)。
|
|
11334
|
+
// **出すのは導出が答えている回だけ**——まとめ表由来の warn で導出を捨てた回(derive が null)は
|
|
11335
|
+
// 「同期の run そのものが落ちた」という別の事実を言っているので、そこへ内訳を混ぜない
|
|
11336
|
+
if (derive && derive.state === "ok") {
|
|
11337
|
+
var failDerCap = derCapOf(cap, adapter);
|
|
11338
|
+
var failCapId = (TILE_CAPS[failDerCap] && TILE_CAPS[failDerCap].id) || "";
|
|
11339
|
+
var fails = failCapId ? ingestFailureRows(LIVE_STATE, failCapId, refKeysOf(failDerCap)) : [];
|
|
11340
|
+
for (var fi = 0; fi < fails.length && fi < INGEST_TILE_ROWS; fi++) {
|
|
11341
|
+
body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ " + ingestFailureText(fails[fi]) }));
|
|
11342
|
+
}
|
|
11343
|
+
// **残りは畳む。** 20 行並べるとタイルが理由文で埋まる(0.4.71 で溢れた形)
|
|
11344
|
+
if (fails.length > INGEST_TILE_ROWS) {
|
|
11345
|
+
body.appendChild(el("div", { class: "ds-tile-note ds-tile-todo", text: "ほか " + (fails.length - INGEST_TILE_ROWS) + " 件" }));
|
|
11346
|
+
}
|
|
11347
|
+
}
|
|
10765
11348
|
// 🔴 **セットアップの残り(checks)の長文はタイルに出さない(#230・オーナー指摘)。**
|
|
10766
11349
|
//
|
|
10767
11350
|
// 出所は engine の夜間チェックの 「action」 欄で、**この設定画面が無かった頃の
|
|
@@ -11856,7 +12439,14 @@ ${calendarAddUrl.toString()}
|
|
|
11856
12439
|
if (!r) return null;
|
|
11857
12440
|
var when = rtAgo(r.at);
|
|
11858
12441
|
var text = "";
|
|
11859
|
-
|
|
12442
|
+
// 🔴 **部分失敗は「失敗」と書かない(#634・設計レビュー🔴3)。** 判定はタイルと同じ 1 本
|
|
12443
|
+
// (「ingestFacts」)を通す。ここを掃かないと、便 2 で Backlog / Figma が ok +
|
|
12444
|
+
// counts.failed > 0 を書き始めた瞬間、**タイルは「接続OK(N 件取り込み・M 件失敗)」・
|
|
12445
|
+
// 札は「取り込み: ◯◯に成功」**になり、失敗が行から消える(R10 の逆向き)。
|
|
12446
|
+
// 件数を添えるのは失敗が 1 件以上あるときだけ——全部成功した回に「(0 件失敗)」を出さない
|
|
12447
|
+
var f = ingestFacts(r);
|
|
12448
|
+
var fw = f && f.failed > 0 ? "(" + f.failed + " 件失敗)" : "";
|
|
12449
|
+
if (r.status === "ok" || (f && f.partial)) text = (when ? "取り込み: " + when + "に成功" : "取り込み: 成功") + fw;
|
|
11860
12450
|
else if (r.status === "failed") text = when ? "取り込み: " + when + "に失敗" : "取り込み: 失敗";
|
|
11861
12451
|
// 🔴 **「スキップ」で終わらせない(#225)。** 鍵が無くて飛ばしたのが skipped の実体で、
|
|
11862
12452
|
// 「スキップ」とだけ書かれても**次に何をすればいいのか読み取れない**。しかも
|
|
@@ -11864,8 +12454,10 @@ ${calendarAddUrl.toString()}
|
|
|
11864
12454
|
// 語は鍵カードの名前と揃える(keyMissingText の項)
|
|
11865
12455
|
else if (r.status === "skipped") text = "取り込み: 未実行(" + keyMissingText(cap) + ")";
|
|
11866
12456
|
if (!text) return null;
|
|
12457
|
+
// 赤にするのは「1 件も取り込めなかった」回だけ(#634)。部分失敗は取り込めているので、
|
|
12458
|
+
// 件数を添えた成功の札のまま出す(色で「壊れている」と言わない)
|
|
11867
12459
|
var span = el("span", {
|
|
11868
|
-
class: "ds-ingest" + (r.status === "failed" ? " ds-ingest-failed" : ""),
|
|
12460
|
+
class: "ds-ingest" + (r.status === "failed" && !(f && f.partial) ? " ds-ingest-failed" : ""),
|
|
11869
12461
|
text: text,
|
|
11870
12462
|
});
|
|
11871
12463
|
// その行を書いた run へ(#560)。成功でも失敗でも、この時刻を書いた run のログ
|
|
@@ -13948,6 +14540,31 @@ ${calendarAddUrl.toString()}
|
|
|
13948
14540
|
title: "このリポジトリは読み取り対象から除外されています(開発の登録ファイル dev-sources.json の exclude)。この画面からは戻せません。",
|
|
13949
14541
|
}));
|
|
13950
14542
|
}
|
|
14543
|
+
// 「接続できる」と「Gold の材料にしている」を別の状態として併記する(#640・engine #399)。
|
|
14544
|
+
//
|
|
14545
|
+
// **常に出す(gate の値で隠さない)。** 届かないことと材料にしていないことは別の事実で、
|
|
14546
|
+
// 片方を隠すと「アクセス不可を直せば読まれる」と誤読させる(#523・オーナー決定
|
|
14547
|
+
// 「操作タブは常に真実を表示する」)。
|
|
14548
|
+
//
|
|
14549
|
+
// 🔴 **「ds-gate」 ではなく 「badge」 で出す。** 行の「判定のバッジは 1 つ」を
|
|
14550
|
+
// 「connection-probe.test.mjs」 が見張っており、「probeSection」 も 「gateEl」 の参照 1 本で
|
|
14551
|
+
// 置き換える設計なので、「.ds-gate」 を 2 つにすると「押した結果がどちらを差し替えたか」が
|
|
14552
|
+
// 構造的に決まらなくなる。**「読み取り停止中」(人が止めた)と同じ棚**に並べるのが正しい
|
|
14553
|
+
// ——どちらも「判定」ではなく「この接続先がいまどう扱われているか」の印。
|
|
14554
|
+
//
|
|
14555
|
+
// 🔴 **警告色にしない。** 直すものが無い中立の事実で、橙にすると直しに行かせる。
|
|
14556
|
+
// 語は短く(長文を入れると行が折り返す)——性質の変え方は下の補足 1 行が持つ
|
|
14557
|
+
// 🔴 **「読み取り停止中」の行には出さない(実装レビュー🟡4)。** 人が exclude で止めた
|
|
14558
|
+
// 行は engine が観測しなくなる= 2 行とも書き直されないので、古い中立の行が永久に残る。
|
|
14559
|
+
// しかも止めてある以上、性質を「会話」にしても読まれない——**効かない一手**を案内する
|
|
14560
|
+
// ことになる。灰色の札が 2 つ並ぶのも「繰り返さない」に反する
|
|
14561
|
+
if (s.ingestExcluded === true && s.goldState !== "excluded") {
|
|
14562
|
+
badges.appendChild(el("span", {
|
|
14563
|
+
class: "badge ds-not-ingested",
|
|
14564
|
+
text: devExcludedLabel(s.ingestExcludedDetail),
|
|
14565
|
+
title: "接続はできています。この登録は Gold(決定・用語・メンバー・ルール)の材料にしていません。",
|
|
14566
|
+
}));
|
|
14567
|
+
}
|
|
13951
14568
|
if (s.notify === true) badges.appendChild(notifyBadge());
|
|
13952
14569
|
// **どのワークスペースのチャンネルかを、必ず行に出す(#104・2026-08-20 に社内も)。**
|
|
13953
14570
|
//
|
|
@@ -14145,6 +14762,28 @@ ${calendarAddUrl.toString()}
|
|
|
14145
14762
|
if (reason) card.appendChild(neededNode(reason, "ds-gate-help ds-gate-reason"));
|
|
14146
14763
|
}
|
|
14147
14764
|
}
|
|
14765
|
+
// 「材料にしていない」の理由と、変えたいときの一手(#640)。**1 行だけ。**
|
|
14766
|
+
//
|
|
14767
|
+
// 🔴 **「ds-gate-help」 を付けない。** あちらは「接続を確認」を押した瞬間に
|
|
14768
|
+
// 「probeSection」 がまとめて隠す札で、押した人がいちばん誤読しやすい瞬間
|
|
14769
|
+
//(「確認したから読まれるようになった」)に、この 1 行だけが消える。
|
|
14770
|
+
// 押しても変わらない事実なので、隠される棚に置かない(幅の扱いは CSS で揃える)。
|
|
14771
|
+
//
|
|
14772
|
+
// 🔴 **その組織のグループで最初の 1 行にだけ出す(「opts.quietExcluded」)。** GitHub の
|
|
14773
|
+
// 既定は「作業物」なので、全行に出すと一覧が案内で埋まる(#356・オーナー却下の原則)。
|
|
14774
|
+
// 同じリポジトリの 2 本目(Discussions)にも出さない(「opts.quietHelp」 と同じ理由)
|
|
14775
|
+
if (s.ingestExcluded === true && s.goldState !== "excluded" && !(opts && (opts.quietHelp || opts.quietExcluded))) {
|
|
14776
|
+
// 🔴 **性質を直書きしない(実装レビュー🟡2)。** すぐ上の札は理由文が名指しした語だけを
|
|
14777
|
+
// 括弧に入れているのに、ここで「作業物」と書くと、engine が名指しを避けた回に
|
|
14778
|
+
// **同じ行が 2 つのことを言う**(札は「読み取りの対象外」・補足は「作業物」)。
|
|
14779
|
+
// 名指しできた回だけ性質を言い、できなかった回は事実だけを言う
|
|
14780
|
+
var exNature = devExcludedLabel(s.ingestExcludedDetail).replace(/^読み取りの対象外(?/, "").replace(/)$/, "");
|
|
14781
|
+
card.appendChild(el("p", {
|
|
14782
|
+
class: "ds-not-ingested-note",
|
|
14783
|
+
text: (exNature ? "この登録は「" + exNature + "」なので、" : "この登録は、")
|
|
14784
|
+
+ "決定・用語・メンバー・ルールの材料にしていません。議論の場として読ませるなら、登録簿で性質を「会話」にしてください",
|
|
14785
|
+
}));
|
|
14786
|
+
}
|
|
14148
14787
|
// 「接続を確認」(2026-08-19)。**押せる人を絞らない**——読むだけで設定を変えないので、
|
|
14149
14788
|
// 気づいた人がその場で確かめられるほうがよい(オーナー判断)。書き込みになる
|
|
14150
14789
|
// 「表示名を更新する」は、押した結果の中から別のボタンとして出る(probeSection)。
|
package/package.json
CHANGED