@takagaki/cortex-decisions-viewer 0.12.66 → 0.12.68
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 +21 -5
- package/dist/render.js +349 -14
- package/package.json +1 -1
package/dist/build.js
CHANGED
|
@@ -756,8 +756,24 @@ async function readGoldProposals(repoRoot) {
|
|
|
756
756
|
// 27案件を実測したところ、案件の目的が記入済みなのは3件だけだった。
|
|
757
757
|
// 残る24案件では、毎晩のAI生成が**案件を知らないまま**抽出を続けている。
|
|
758
758
|
// ビューアの仕事は、その未記入を見えるようにして「書かせる圧」をかけること。
|
|
759
|
-
/**
|
|
760
|
-
|
|
759
|
+
/**
|
|
760
|
+
* 記入されていないと困る自己記述の節。**未記入の赤い帯はここだけを見る。**
|
|
761
|
+
*
|
|
762
|
+
* 🔴 **旧見出し「この案件について」への両対応は入れない**(#626 便 2・オーナー決定)。
|
|
763
|
+
* engine のマイグレーション 0093 が全プロジェクトの Home.md を改名済みで、旧見出しは
|
|
764
|
+
* どこにも残っていない。両対応にすると、次に見出しが動いたときにどちらが正本か
|
|
765
|
+
* 分からなくなる(改名は engine 側の 1 回きりの操作として完了している)。
|
|
766
|
+
*
|
|
767
|
+
* 🔴 **見出し名を「出す節」と「必須の節」で別々に書かない。** 片方だけ直すと
|
|
768
|
+
* 「節は最上部に出ているのに未記入の赤い帯も出る」という食い違った画面になる
|
|
769
|
+
* (0.12.66 の事故がまさにこの形だった)。必須から並びを組み立てる向きにしておくと、
|
|
770
|
+
* 任意の節を改名しても必須に昇格しない。
|
|
771
|
+
*/
|
|
772
|
+
const HOME_REQUIRED_GROUNDING_HEADINGS = ["このプロジェクトについて", "ゴール"];
|
|
773
|
+
/** あれば出すが、無くてよい節(未記入の判定には使わない) */
|
|
774
|
+
const HOME_OPTIONAL_GROUNDING_HEADINGS = ["体制"];
|
|
775
|
+
/** 新しい形の自己記述の節。画面の最上部に、この並び順で出す */
|
|
776
|
+
const HOME_GROUNDING_HEADINGS = [...HOME_REQUIRED_GROUNDING_HEADINGS, ...HOME_OPTIONAL_GROUNDING_HEADINGS];
|
|
761
777
|
/** 旧い形。1つの節に「解きたいこと」「達成した状態」が同居していた */
|
|
762
778
|
const HOME_LEGACY_GROUNDING_HEADING = "このプロジェクトが目指すもの";
|
|
763
779
|
/**
|
|
@@ -830,9 +846,9 @@ async function buildHomeGrounding(rawMarkdown) {
|
|
|
830
846
|
html: await renderGroundingSection(hit.body),
|
|
831
847
|
});
|
|
832
848
|
}
|
|
833
|
-
//
|
|
834
|
-
// **どちらの節も無ければ未記入。**
|
|
835
|
-
const checkHeadings = hasNew ?
|
|
849
|
+
// 未記入の判定。新形式は「このプロジェクトについて」と「ゴール」の2つ、旧形式は1つの節を見る。
|
|
850
|
+
// **どちらの節も無ければ未記入。** 節ごと消えているプロジェクトが実在する
|
|
851
|
+
const checkHeadings = hasNew ? HOME_REQUIRED_GROUNDING_HEADINGS : [HOME_LEGACY_GROUNDING_HEADING];
|
|
836
852
|
const unfilled = checkHeadings.some((h) => {
|
|
837
853
|
const hit = find(h);
|
|
838
854
|
return !hit || !hit.body.trim() || isUnwritten(hit.body);
|
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,6 +14,8 @@ 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;
|
|
18
20
|
exports.overlayStateOnFleet = overlayStateOnFleet;
|
|
19
21
|
exports.chatRowsToAdd = chatRowsToAdd;
|
|
@@ -38,6 +40,8 @@ exports.secretRegisteredAnywhere = secretRegisteredAnywhere;
|
|
|
38
40
|
exports.sanitizeCapabilitiesNow = sanitizeCapabilitiesNow;
|
|
39
41
|
exports.sanitizeGithubOwnersNow = sanitizeGithubOwnersNow;
|
|
40
42
|
exports.splitNeededParts = splitNeededParts;
|
|
43
|
+
exports.ingestFacts = ingestFacts;
|
|
44
|
+
exports.ingestSucceeded = ingestSucceeded;
|
|
41
45
|
exports.ingestedAtOf = ingestedAtOf;
|
|
42
46
|
exports.ingestedRowOf = ingestedRowOf;
|
|
43
47
|
exports.runLogUrlOf = runLogUrlOf;
|
|
@@ -57,6 +61,8 @@ exports.calendarWaiting = calendarWaiting;
|
|
|
57
61
|
exports.calendarTemplateUrl = calendarTemplateUrl;
|
|
58
62
|
exports.calendarAddUrl = calendarAddUrl;
|
|
59
63
|
exports.deriveCapTile = deriveCapTile;
|
|
64
|
+
exports.ingestFailureRows = ingestFailureRows;
|
|
65
|
+
exports.ingestFailureText = ingestFailureText;
|
|
60
66
|
exports.derivePendingTile = derivePendingTile;
|
|
61
67
|
exports.liveRowVerdict = liveRowVerdict;
|
|
62
68
|
exports.liveRegistryOf = liveRegistryOf;
|
|
@@ -398,7 +404,7 @@ const CLAUDE_CODE_HOME = "https://claude.ai/code/";
|
|
|
398
404
|
/** 素のリンクを**テストから固定するための入れ物**(LIVE_CONFIRM_LIMITS と同じ作法) */
|
|
399
405
|
exports.CLAUDE_CODE_LINK = { home: CLAUDE_CODE_HOME };
|
|
400
406
|
/**
|
|
401
|
-
*
|
|
407
|
+
* 「このプロジェクトについて聞く」導線の指示文。
|
|
402
408
|
*
|
|
403
409
|
* **AI 導線の指示文はここ1か所に持つ。** 書き写した先が増えると、URL は同じでも
|
|
404
410
|
* **開いた先の AI への最初の一言が2通り**になる(`askAiUrl` と対で読む)。
|
|
@@ -1042,6 +1048,89 @@ const RUN_URL_WRITERS = ["ingest", "gold", "nightly"];
|
|
|
1042
1048
|
* (「Webhook」と断定すると嘘になる)
|
|
1043
1049
|
*/
|
|
1044
1050
|
const RUN_SOURCE_WORDS = { central: "中央の処理", button: "その場で確認", auto: "保存時に確認" };
|
|
1051
|
+
/**
|
|
1052
|
+
* 部分失敗の内訳の欄(#634)。サーバ側 `connection-state-keys.mjs` の
|
|
1053
|
+
* `INGEST_FAILURE_KINDS` / `INGEST_FAILURES_MAX` / `INGEST_TEXT_MAX` / `INGEST_COUNT_KEYS` の写し。
|
|
1054
|
+
*
|
|
1055
|
+
* 🔴 **`STATE_ROW_FIELDS` は文字列の欄しか通さない**(`sanitizeStateNow` の `typeof fv === "string"`)。
|
|
1056
|
+
* `counts`(オブジェクト)と `failures`(配列)は**そこを通らない**ので、下の 2 つの篩を
|
|
1057
|
+
* 別に通す。通し忘れると欄は黙って消え、見出しの件数だけが出なくなる——**行は届いているのに
|
|
1058
|
+
* 画面が何も言わない**形なので、実機でも「engine がまだ書いていないのだろう」と読めてしまう。
|
|
1059
|
+
*
|
|
1060
|
+
* ⚠ **定数を export しない**(`exports.X` に化けて、流し込んだ先で未定義になる)。
|
|
1061
|
+
* 突き合わせは `viewer/test/state-vocab-parity.test.mjs`(サーバの正本と同じ値か)。
|
|
1062
|
+
*/
|
|
1063
|
+
const INGEST_FAILURE_KINDS = ["transient", "permanent"];
|
|
1064
|
+
const INGEST_FAILURES_MAX = 20;
|
|
1065
|
+
const INGEST_TEXT_MAX = 200;
|
|
1066
|
+
const INGEST_COUNT_KEYS = ["ok", "failed", "skipped"];
|
|
1067
|
+
const INGEST_COUNT_MAX = 1000000;
|
|
1068
|
+
/**
|
|
1069
|
+
* タイルに並べる失敗の行の本数。**超えたぶんは「ほか N 件」に畳む。**
|
|
1070
|
+
* 20 行並べるとタイルが理由文で埋まる(同じ形の実害が 0.4.71 にある)。
|
|
1071
|
+
*/
|
|
1072
|
+
const INGEST_TILE_ROWS = 3;
|
|
1073
|
+
/** 語と上限を**テストから固定するための入れ物**(`STATE_CONFIRMING` と同じ作法) */
|
|
1074
|
+
exports.INGEST_LIMITS = {
|
|
1075
|
+
kinds: INGEST_FAILURE_KINDS,
|
|
1076
|
+
failuresMax: INGEST_FAILURES_MAX,
|
|
1077
|
+
textMax: INGEST_TEXT_MAX,
|
|
1078
|
+
countKeys: INGEST_COUNT_KEYS,
|
|
1079
|
+
countMax: INGEST_COUNT_MAX,
|
|
1080
|
+
tileRows: INGEST_TILE_ROWS,
|
|
1081
|
+
};
|
|
1082
|
+
/**
|
|
1083
|
+
* `counts` を篩う(#634)。**落として残りを出す**(サーバ側 `normalizeIngestCounts` と同じ規則)。
|
|
1084
|
+
*
|
|
1085
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
1086
|
+
* `INGEST_COUNT_KEYS` / `INGEST_COUNT_MAX` は CLIENT_JS 側に `var` で流し込んである)。
|
|
1087
|
+
*/
|
|
1088
|
+
function sanitizeIngestCounts(v) {
|
|
1089
|
+
if (!v || typeof v !== "object" || Array.isArray(v))
|
|
1090
|
+
return null;
|
|
1091
|
+
var out = {};
|
|
1092
|
+
var any = false;
|
|
1093
|
+
for (var i = 0; i < INGEST_COUNT_KEYS.length; i++) {
|
|
1094
|
+
var k = INGEST_COUNT_KEYS[i];
|
|
1095
|
+
var n = v[k];
|
|
1096
|
+
// **整数・非負・上限つきだけ。** 画面の見出しに出る数なので、`-1` や `1e30` を通さない
|
|
1097
|
+
if (typeof n === "number" && isFinite(n) && Math.floor(n) === n && n >= 0 && n <= INGEST_COUNT_MAX) {
|
|
1098
|
+
out[k] = n;
|
|
1099
|
+
any = true;
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return any ? out : null;
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* `failures` を篩う(#634)。**落として残りを出す**(サーバ側 `normalizeIngestFailures` と同じ規則)。
|
|
1106
|
+
*
|
|
1107
|
+
* `kind` が語彙外の値は `permanent` に倒す。**`transient` に倒さない**——画面は `transient` にだけ
|
|
1108
|
+
* 「次回の取り込みで再試行します」を添えるので、知らない語をそちらへ倒すと、直さないと消えない
|
|
1109
|
+
* 失敗に「待てば直る」と言うことになる。
|
|
1110
|
+
*
|
|
1111
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
1112
|
+
* `INGEST_FAILURE_KINDS` / `INGEST_FAILURES_MAX` / `INGEST_TEXT_MAX` は `var` で流し込んである)。
|
|
1113
|
+
*/
|
|
1114
|
+
function sanitizeIngestFailures(v) {
|
|
1115
|
+
if (!Array.isArray(v))
|
|
1116
|
+
return null;
|
|
1117
|
+
var out = [];
|
|
1118
|
+
for (var i = 0; i < v.length; i++) {
|
|
1119
|
+
if (out.length >= INGEST_FAILURES_MAX)
|
|
1120
|
+
break;
|
|
1121
|
+
var f = v[i];
|
|
1122
|
+
if (!f || typeof f !== "object" || Array.isArray(f))
|
|
1123
|
+
continue;
|
|
1124
|
+
if (typeof f.name !== "string" || !f.name)
|
|
1125
|
+
continue;
|
|
1126
|
+
out.push({
|
|
1127
|
+
name: f.name.slice(0, INGEST_TEXT_MAX),
|
|
1128
|
+
reason: typeof f.reason === "string" ? f.reason.slice(0, INGEST_TEXT_MAX) : "",
|
|
1129
|
+
kind: INGEST_FAILURE_KINDS.indexOf(f.kind) >= 0 ? f.kind : "permanent",
|
|
1130
|
+
});
|
|
1131
|
+
}
|
|
1132
|
+
return out.length ? out : null;
|
|
1133
|
+
}
|
|
1045
1134
|
/** stateNow はライブ専用キーで build.ts の網にかからない。画面側でも許可リストを通す */
|
|
1046
1135
|
function sanitizeStateNow(v) {
|
|
1047
1136
|
if (!Array.isArray(v))
|
|
@@ -1066,6 +1155,19 @@ function sanitizeStateNow(v) {
|
|
|
1066
1155
|
// `runUrl` は run ページの形だけ(#560)。形が違えば**欄だけ落として行は残す**
|
|
1067
1156
|
if (item.runUrl && !RUN_URL_RE.test(item.runUrl))
|
|
1068
1157
|
delete item.runUrl;
|
|
1158
|
+
// 部分失敗の内訳(#634)。**上の走査は文字列の欄しか通さない**ので、オブジェクトと配列の
|
|
1159
|
+
// この 2 欄はここで別に篩う。🔴 ここを落とすと欄は黙って消え、**行は届いているのに
|
|
1160
|
+
// 見出しの件数と失敗の行だけが出ない**——「engine がまだ書いていない」と読めてしまう形になる。
|
|
1161
|
+
// 秘密の除去はサーバ側(`queryConnectionState`)が済ませてある(`detail` と同じ扱い。
|
|
1162
|
+
// 画面側に秘密のパターンの 2 枚目を持たない・設計レビュー🔴2)
|
|
1163
|
+
if (item.question === "ingested") {
|
|
1164
|
+
const c = sanitizeIngestCounts(r.counts);
|
|
1165
|
+
if (c)
|
|
1166
|
+
item.counts = c;
|
|
1167
|
+
const f = sanitizeIngestFailures(r.failures);
|
|
1168
|
+
if (f)
|
|
1169
|
+
item.failures = f;
|
|
1170
|
+
}
|
|
1069
1171
|
out.push(item);
|
|
1070
1172
|
}
|
|
1071
1173
|
return out.length ? out : null;
|
|
@@ -2062,6 +2164,90 @@ function splitNeededParts(text) {
|
|
|
2062
2164
|
out.push({ text: s.slice(at), url: false });
|
|
2063
2165
|
return out;
|
|
2064
2166
|
}
|
|
2167
|
+
/**
|
|
2168
|
+
* 取り込みの行 1 本から「何件取り込めて、何件失敗したか」を読む(#634)。**判定はこの 1 本だけ。**
|
|
2169
|
+
*
|
|
2170
|
+
* 🔴 **タイル(`deriveCapTile`)・Home の「使用ツール」(`liveRowVerdict`)・行の札(`ingestedEl`)・
|
|
2171
|
+
* 「最終同期」(`ingestedAtOf`)が全部ここを通る。** 分けると、同じ案件でタイルは「接続OK」・
|
|
2172
|
+
* Home は「承認待ち」・札は「失敗」になる(`ops-ui-controls-policy.md` R10「タイルと行が違うことを
|
|
2173
|
+
* 言ってはいけない」)。2026-09-16 の nicigas-ncp が踏んだのはまさにこの形の 1 枚目。
|
|
2174
|
+
*
|
|
2175
|
+
* ## 2 つの読み方
|
|
2176
|
+
*
|
|
2177
|
+
* 1. **新形式**(便 2 以降)… `counts` から読む。`status` は `ok`(部分失敗は「取り込めた」側)
|
|
2178
|
+
* 2. **旧形式の橋**(便 1 の暫定)… `status: "failed"` で `counts` が無い行の `detail` から
|
|
2179
|
+
* 件数を読む。engine の書き手は `251件取り込み・1件失敗・38件は取り込めない形式…`
|
|
2180
|
+
* (`scripts/connection-state-write-materials.mjs` の `ingestedDetail`)、中央 Apps Script は
|
|
2181
|
+
* `3件取り込み(カレンダー 2・合図 1)・1件失敗`(`apps-script/src/Main.gs`)と書く。
|
|
2182
|
+
* **cap を問わず全部の `ingested` の行に効く**(会議もこの便で部分失敗になる・設計レビュー🔴4)
|
|
2183
|
+
*
|
|
2184
|
+
* ⚠ **橋は便 2 で消してよい。** engine と中央の書き手が `counts` を書き始めたら、下の
|
|
2185
|
+
* 「旧形式の橋」の分岐ごと削除できる(新形式の行は 1 つ目の分岐で答えが出る)。
|
|
2186
|
+
* 消すときは番人テストの旧形式の題材も一緒に消す。
|
|
2187
|
+
*
|
|
2188
|
+
* 🔴 **正規表現は関数の中に literal で置く(外の const にしない)。** モジュール直下の const は
|
|
2189
|
+
* tsc が `exports.X` に書き換え、`.toString()` で客先へ渡ったこの関数がブラウザで未定義を参照する
|
|
2190
|
+
* ——ライブ取得が丸ごと落ちて画面が静的表示に戻る(`isCalendarId` の項と同じ穴・設計レビュー🔴1)。
|
|
2191
|
+
*
|
|
2192
|
+
* @returns 読めたときだけ。`partial` は「1 件以上取り込めて、1 件以上失敗した」
|
|
2193
|
+
*
|
|
2194
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
2195
|
+
*/
|
|
2196
|
+
function ingestFacts(row) {
|
|
2197
|
+
if (!row || row.question !== "ingested")
|
|
2198
|
+
return null;
|
|
2199
|
+
// 🔴 **`skipped`(鍵が無くて取り込みを始めていない)の行は部分失敗にしない。** 件数が
|
|
2200
|
+
// 付いていても、この行が言いたいのは「◯が未登録です」で、部分失敗として飲み込むと
|
|
2201
|
+
// **鍵を入れるべき案件に「接続OK」と出る**(取るべき行動が違う・#225 が分けたかったもの)
|
|
2202
|
+
var c = row.status === "skipped" ? null : row.counts;
|
|
2203
|
+
// 🔴 **`ok` と `failed` のどちらも読めない `counts` は「新形式」と見なさない。** 契約は
|
|
2204
|
+
// 3 つの鍵を個別に任意にしているので、`{ skipped: 38 }` だけの行がありうる。そこで即
|
|
2205
|
+
// 「0 件取り込み・0 件失敗」と答えると、**同じ行の `detail` にある件数が読まれないまま
|
|
2206
|
+
// 「同期が失敗しています」に戻る**——便 2 が半分だけ移った窓(counts は書き始めたが
|
|
2207
|
+
// status はまだ `failed`)で起きる。読めないなら下の橋へ落とす
|
|
2208
|
+
if (c && typeof c === "object" && !Array.isArray(c) && (typeof c.ok === "number" || typeof c.failed === "number")) {
|
|
2209
|
+
var cok = typeof c.ok === "number" ? c.ok : 0;
|
|
2210
|
+
var cfail = typeof c.failed === "number" ? c.failed : 0;
|
|
2211
|
+
var cskip = typeof c.skipped === "number" ? c.skipped : 0;
|
|
2212
|
+
return { ok: cok, failed: cfail, skipped: cskip, partial: cok > 0 && cfail > 0, legacy: false };
|
|
2213
|
+
}
|
|
2214
|
+
// ---- 旧形式の橋(便 2 で消してよい・上の項) ----
|
|
2215
|
+
if (row.status !== "failed")
|
|
2216
|
+
return null;
|
|
2217
|
+
var d = typeof row.detail === "string" ? row.detail : "";
|
|
2218
|
+
var mo = /([0-9][0-9,]*)\s*件取り込み/.exec(d);
|
|
2219
|
+
var mf = /([0-9][0-9,]*)\s*件失敗/.exec(d);
|
|
2220
|
+
// **件数を持たない失敗はここへ来ない。** 「フォルダを開けないため取り込めませんでした」
|
|
2221
|
+
// 「取り込みが途中で終わりました」は件数の話ではないので、失敗のまま残るのが正しい
|
|
2222
|
+
if (!mo || !mf)
|
|
2223
|
+
return null;
|
|
2224
|
+
var lok = parseInt(String(mo[1]).replace(/,/g, ""), 10);
|
|
2225
|
+
var lfail = parseInt(String(mf[1]).replace(/,/g, ""), 10);
|
|
2226
|
+
if (!(lok >= 0) || !(lfail >= 0))
|
|
2227
|
+
return null;
|
|
2228
|
+
var ms = /([0-9][0-9,]*)\s*件は取り込めない/.exec(d);
|
|
2229
|
+
var lskip = ms ? parseInt(String(ms[1]).replace(/,/g, ""), 10) : 0;
|
|
2230
|
+
// `0件取り込み・1件失敗`(フォルダを開けなかった回)は partial にならない=失敗のまま
|
|
2231
|
+
return { ok: lok, failed: lfail, skipped: lskip >= 0 ? lskip : 0, partial: lok > 0 && lfail > 0, legacy: true };
|
|
2232
|
+
}
|
|
2233
|
+
/**
|
|
2234
|
+
* その行が「取り込めた」と言えるか(#634)。`ok` か、部分失敗(旧形式を含む)。
|
|
2235
|
+
*
|
|
2236
|
+
* **「最終同期」の材料**(`ingestedAtOf` / `ingestedRowOf`)。251 件取り込んだ回は
|
|
2237
|
+
* **実際に取り込めている**ので、その回の時刻を「最終同期」として出すのが R4
|
|
2238
|
+
* (「最終同期」は取り込めた時刻だけ)に沿う。旧形式では `status` が `failed` なので、
|
|
2239
|
+
* ここを通さないと**前回の成功時刻が出続ける**(設計レビュー🟡11)。
|
|
2240
|
+
*
|
|
2241
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
2242
|
+
*/
|
|
2243
|
+
function ingestSucceeded(row) {
|
|
2244
|
+
if (!row)
|
|
2245
|
+
return false;
|
|
2246
|
+
if (row.status === "ok")
|
|
2247
|
+
return true;
|
|
2248
|
+
var f = ingestFacts(row);
|
|
2249
|
+
return !!(f && f.partial);
|
|
2250
|
+
}
|
|
2065
2251
|
/**
|
|
2066
2252
|
* `stateNow` から、その能力の「最後に取り込めた時刻」を引く(#227 の時刻の一貫化)。
|
|
2067
2253
|
*
|
|
@@ -2087,8 +2273,14 @@ function ingestedAtOf(rows, capId, refs) {
|
|
|
2087
2273
|
continue;
|
|
2088
2274
|
if (refs && !refs[stateRefKey(capId, r.ref, r.workspace)])
|
|
2089
2275
|
continue;
|
|
2090
|
-
|
|
2091
|
-
|
|
2276
|
+
// **部分失敗の回も「取り込めた」**(#634・`ingestSucceeded`)。251 件は実際にこの回で
|
|
2277
|
+
// 取り込めているので、前回の成功時刻を出し続けるほうが嘘になる。
|
|
2278
|
+
// 履歴(`lastOkAt`)と今回の時刻の**新しいほう**を採る——同じ 1 行の中の 2 つの時刻で、
|
|
2279
|
+
// どちらもミリ秒ありの ISO に正規化済み(サーバの `normalizeAt`)なので辞書順で比べられる
|
|
2280
|
+
const hist = typeof r.lastOkAt === "string" ? r.lastOkAt : "";
|
|
2281
|
+
const okAt = ingestSucceeded(r) && typeof r.at === "string" ? r.at : "";
|
|
2282
|
+
const at = okAt > hist ? okAt : hist;
|
|
2283
|
+
if (at > best)
|
|
2092
2284
|
best = at;
|
|
2093
2285
|
}
|
|
2094
2286
|
return best;
|
|
@@ -2111,8 +2303,12 @@ function ingestedRowOf(rows, capId, refs) {
|
|
|
2111
2303
|
continue;
|
|
2112
2304
|
if (refs && !refs[stateRefKey(capId, r.ref, r.workspace)])
|
|
2113
2305
|
continue;
|
|
2114
|
-
|
|
2115
|
-
|
|
2306
|
+
// 選び方は `ingestedAtOf` と**同じ 1 本**(分かれると「最終同期の時刻」と
|
|
2307
|
+
// 「そのリンク先の run」が別の行のものになる)
|
|
2308
|
+
const hist = typeof r.lastOkAt === "string" ? r.lastOkAt : "";
|
|
2309
|
+
const okAt = ingestSucceeded(r) && typeof r.at === "string" ? r.at : "";
|
|
2310
|
+
const at = okAt > hist ? okAt : hist;
|
|
2311
|
+
if (at > best) {
|
|
2116
2312
|
best = at;
|
|
2117
2313
|
bestRow = r;
|
|
2118
2314
|
}
|
|
@@ -2146,9 +2342,13 @@ function runLogUrlOf(row) {
|
|
|
2146
2342
|
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
2147
2343
|
*/
|
|
2148
2344
|
function lastSyncRunUrlOf(row) {
|
|
2149
|
-
|
|
2345
|
+
// **部分失敗の回も「取り込めた」(#634)。** ここを `status === "ok"` のままにすると、
|
|
2346
|
+
// 「最終同期」の時刻は部分失敗の回のものになったのに、その時刻を書いた run へ行けない
|
|
2347
|
+
// ——R9(その時刻を書いた run にだけリンクを出す)が、まさにこの便の対象の回で満たせない
|
|
2348
|
+
if (!ingestSucceeded(row))
|
|
2150
2349
|
return "";
|
|
2151
|
-
|
|
2350
|
+
// 「最終同期」として出した時刻がこの行の `at` でないなら、リンク先の run は別の回のもの
|
|
2351
|
+
if (row.lastOkAt && row.lastOkAt > row.at)
|
|
2152
2352
|
return "";
|
|
2153
2353
|
return runLogUrlOf(row);
|
|
2154
2354
|
}
|
|
@@ -2702,6 +2902,9 @@ function deriveCapTile(cap, ctx) {
|
|
|
2702
2902
|
var noToken = 0;
|
|
2703
2903
|
var unreachable = 0;
|
|
2704
2904
|
var okSeen = false;
|
|
2905
|
+
// 部分失敗の内訳の合計(#634)。**能力の全接続先を足す**——タイルは能力ぜんたいを 1 語で言う
|
|
2906
|
+
var sumOk = 0;
|
|
2907
|
+
var sumFailed = 0;
|
|
2705
2908
|
for (var i = 0; i < rows.length; i++) {
|
|
2706
2909
|
var r = rows[i];
|
|
2707
2910
|
if (!r || r.cap !== meta.id)
|
|
@@ -2720,6 +2923,29 @@ function deriveCapTile(cap, ctx) {
|
|
|
2720
2923
|
// ——Drive に届いていても取り込みが0件なら「まだ取り込みがありません」が事実
|
|
2721
2924
|
if (r.question === meta.question && (r.status === "ok" || r.lastOkAt))
|
|
2722
2925
|
okSeen = true;
|
|
2926
|
+
// 🔴 **部分失敗は「失敗」と数えない(#634・オーナー決定 2026-09-16)。** 251 件成功・
|
|
2927
|
+
// 1 件失敗の行を `failed` に数えると、規則2 が能力ぜんたいを「同期が失敗しています」に
|
|
2928
|
+
// 丸める——99.6% 取り込めているのに「共有資料が壊れている」と読める画面になる。
|
|
2929
|
+
// **取り込めた実績はあるので `okSeen` は立てる**(旧形式の行は `status: "failed"` なので、
|
|
2930
|
+
// ここで立てないと規則4 の「まだ取り込みがありません」に落ちる)。
|
|
2931
|
+
// 件数は規則3 の見出しへ回す。判定の実体は `ingestFacts` 1 本(タイル・Home・行の札で共有)
|
|
2932
|
+
var facts = r.question === meta.question ? ingestFacts(r) : null;
|
|
2933
|
+
if (facts) {
|
|
2934
|
+
sumOk += facts.ok;
|
|
2935
|
+
sumFailed += facts.failed;
|
|
2936
|
+
}
|
|
2937
|
+
if (facts && facts.partial) {
|
|
2938
|
+
okSeen = true;
|
|
2939
|
+
continue;
|
|
2940
|
+
}
|
|
2941
|
+
// 🔴 **`ok` と名乗っていても 1 件も取り込めていなければ失敗として数える(#634)。**
|
|
2942
|
+
// `failed` を「1 件も取り込めなかった」に限定した約束の裏返しで、書き手が取り違えると
|
|
2943
|
+
// 見出しが「接続OK(0 件取り込み・3 件失敗)」という**自分で矛盾した文**になる。
|
|
2944
|
+
// 契約では弾かない(行ごと落とすと失敗そのものが画面から消える)ので、ここで倒す
|
|
2945
|
+
if (facts && facts.ok === 0 && facts.failed > 0) {
|
|
2946
|
+
failed++;
|
|
2947
|
+
continue;
|
|
2948
|
+
}
|
|
2723
2949
|
var bad = BAD_STATE_STATUSES[r.question] || [];
|
|
2724
2950
|
if (bad.indexOf(r.status) < 0)
|
|
2725
2951
|
continue;
|
|
@@ -2753,6 +2979,13 @@ function deriveCapTile(cap, ctx) {
|
|
|
2753
2979
|
return { rule: 2, state: "warn", text: unreachable + "件が読めていません", keyMissing: false };
|
|
2754
2980
|
}
|
|
2755
2981
|
// ---- 規則3: 実績あり → 接続OK ----
|
|
2982
|
+
//
|
|
2983
|
+
// **部分失敗は件数で言う(#634)。** 「接続OK」の一言で丸めると 1 件の失敗が画面から消え、
|
|
2984
|
+
// 「同期が失敗しています」で丸めると 251 件の成功が消える。**帯は症状だけ**
|
|
2985
|
+
// (`ui-wording-symptom-then-one-action`)——何を直すかはタイルの中の失敗の行が言う
|
|
2986
|
+
if (okSeen && sumFailed > 0) {
|
|
2987
|
+
return { rule: 3, state: "ok", text: "接続OK(" + sumOk + " 件取り込み・" + sumFailed + " 件失敗)", keyMissing: false };
|
|
2988
|
+
}
|
|
2756
2989
|
if (okSeen)
|
|
2757
2990
|
return { rule: 3, state: "ok", text: "接続OK", keyMissing: false };
|
|
2758
2991
|
// ---- 規則4: 実績なし → 中立 ----
|
|
@@ -2765,6 +2998,56 @@ function deriveCapTile(cap, ctx) {
|
|
|
2765
2998
|
return { rule: 4, state: "idle", text: STATE_CONFIRMING_TEXT, keyMissing: false };
|
|
2766
2999
|
return { rule: 4, state: "idle", text: meta.noFact, keyMissing: false };
|
|
2767
3000
|
}
|
|
3001
|
+
/**
|
|
3002
|
+
* その能力で取り込めなかったファイルの一覧(#634)。**タイルの中に 1 行ずつ出す材料。**
|
|
3003
|
+
*
|
|
3004
|
+
* 選び方は `ingestedAtOf` と同じ篩(`cap` / `question === "ingested"` / 登録簿の `refs`)。
|
|
3005
|
+
* 孤児行(外した接続先の残骸)の失敗を並べない——外したフォルダのファイル名が
|
|
3006
|
+
* いつまでも画面に残る(#523 決定2 と同じ理由)。
|
|
3007
|
+
*
|
|
3008
|
+
* 旧形式の行には一覧が無い(`detail` は件数しか持たない)ので 0 件になる。**それが橋の限界**で、
|
|
3009
|
+
* 便 1 では帯の件数だけが出る。便 2 で書き手が `failures` を書き始めると行が並ぶ。
|
|
3010
|
+
*
|
|
3011
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す。
|
|
3012
|
+
* `stateRefKey` / `INGEST_FAILURES_MAX` はこれより前に流し込んである)。
|
|
3013
|
+
*/
|
|
3014
|
+
function ingestFailureRows(rows, capId, refs) {
|
|
3015
|
+
var out = [];
|
|
3016
|
+
if (!Array.isArray(rows) || !capId)
|
|
3017
|
+
return out;
|
|
3018
|
+
for (var i = 0; i < rows.length; i++) {
|
|
3019
|
+
var r = rows[i];
|
|
3020
|
+
if (!r || r.cap !== capId || r.question !== "ingested")
|
|
3021
|
+
continue;
|
|
3022
|
+
if (refs && !refs[stateRefKey(capId, r.ref, r.workspace)])
|
|
3023
|
+
continue;
|
|
3024
|
+
if (!Array.isArray(r.failures))
|
|
3025
|
+
continue;
|
|
3026
|
+
for (var j = 0; j < r.failures.length; j++) {
|
|
3027
|
+
if (out.length >= INGEST_FAILURES_MAX)
|
|
3028
|
+
return out;
|
|
3029
|
+
var f = r.failures[j];
|
|
3030
|
+
if (f && f.name)
|
|
3031
|
+
out.push(f);
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
return out;
|
|
3035
|
+
}
|
|
3036
|
+
/**
|
|
3037
|
+
* 取り込めなかったファイル 1 件の 1 行(#634)。**行は一手**(`ui-wording-symptom-then-one-action`)。
|
|
3038
|
+
*
|
|
3039
|
+
* 一手を添えるのは `transient`(相手側の一時エラー)だけ。**`permanent` には足さない**
|
|
3040
|
+
* ——恒久エラーの直し方は理由ごとに違う(権限を直す/形式を変える)ので、一律の案内を
|
|
3041
|
+
* ここで作ると嘘になる。何を直すかは書き手が `reason` に書く。
|
|
3042
|
+
*
|
|
3043
|
+
* **クロージャを持たない純関数に保つこと**(`.toString()` でクライアントへ渡す)。
|
|
3044
|
+
*/
|
|
3045
|
+
function ingestFailureText(f) {
|
|
3046
|
+
var name = String((f && f.name) || "");
|
|
3047
|
+
var reason = String((f && f.reason) || "");
|
|
3048
|
+
var line = reason ? name + " — " + reason : name;
|
|
3049
|
+
return f && f.kind === "transient" ? line + "(次回の取り込みで再試行します)" : line;
|
|
3050
|
+
}
|
|
2768
3051
|
/**
|
|
2769
3052
|
* 「まとめ表が知らない行がある」能力のタイルに、行と同じ答えを出せるか(#576)。
|
|
2770
3053
|
*
|
|
@@ -2921,6 +3204,13 @@ function liveRowVerdict(regKey, reg, rows, question) {
|
|
|
2921
3204
|
return null;
|
|
2922
3205
|
if (hit.status === "ok")
|
|
2923
3206
|
return { status: "ok", row: hit };
|
|
3207
|
+
// 🔴 **部分失敗は「承認待ち」と言わない(#634)。** タイル(`deriveCapTile`)が
|
|
3208
|
+
// 「接続OK(251 件取り込み・1 件失敗)」と言っている横で、Home の同じ接続先が
|
|
3209
|
+
// 「承認待ち」になると、同じ画面の 2 か所が別のことを言う(R10)。判定は `ingestFacts` 1 本。
|
|
3210
|
+
// **`row` は必ず添える**(呼び手が `v.row.at` を読む・設計レビュー🟡10)
|
|
3211
|
+
var hitFacts = ingestFacts(hit);
|
|
3212
|
+
if (hitFacts && hitFacts.partial)
|
|
3213
|
+
return { status: "ok", row: hit };
|
|
2924
3214
|
var bad = BAD_STATE_STATUSES[question] || [];
|
|
2925
3215
|
if (bad.indexOf(hit.status) >= 0)
|
|
2926
3216
|
return { status: "bad", row: hit };
|
|
@@ -5192,6 +5482,17 @@ var STATE_ROW_FIELDS = ${JSON.stringify(STATE_ROW_FIELDS)};
|
|
|
5192
5482
|
var RUN_URL_RE = new RegExp(${JSON.stringify(RUN_URL_RE.source)});
|
|
5193
5483
|
var RUN_URL_WRITERS = ${JSON.stringify(RUN_URL_WRITERS)};
|
|
5194
5484
|
var RUN_SOURCE_WORDS = ${JSON.stringify(RUN_SOURCE_WORDS)};
|
|
5485
|
+
// 部分失敗の内訳の欄(#634)。🔴 **この 6 行を落とすと、流し込んだ sanitizeIngestCounts /
|
|
5486
|
+
// sanitizeIngestFailures が未定義を参照して、ライブ取得が丸ごと死ぬ**(定数は export できないので
|
|
5487
|
+
// 値はここで注入する。「STATE_STATUSES」 と同じ作法)
|
|
5488
|
+
var INGEST_FAILURE_KINDS = ${JSON.stringify(INGEST_FAILURE_KINDS)};
|
|
5489
|
+
var INGEST_FAILURES_MAX = ${JSON.stringify(INGEST_FAILURES_MAX)};
|
|
5490
|
+
var INGEST_TEXT_MAX = ${JSON.stringify(INGEST_TEXT_MAX)};
|
|
5491
|
+
var INGEST_COUNT_KEYS = ${JSON.stringify(INGEST_COUNT_KEYS)};
|
|
5492
|
+
var INGEST_COUNT_MAX = ${JSON.stringify(INGEST_COUNT_MAX)};
|
|
5493
|
+
var INGEST_TILE_ROWS = ${JSON.stringify(INGEST_TILE_ROWS)};
|
|
5494
|
+
${sanitizeIngestCounts.toString()}
|
|
5495
|
+
${sanitizeIngestFailures.toString()}
|
|
5195
5496
|
${sanitizeStateNow.toString()}
|
|
5196
5497
|
${overlayStateOnFleet.toString()}
|
|
5197
5498
|
// Home の「使用ツール」(#275)。**判定と表示語の実体はビルド時の定義**——
|
|
@@ -5222,6 +5523,10 @@ ${splitNeededParts.toString()}
|
|
|
5222
5523
|
${stateRefKey.toString()}
|
|
5223
5524
|
${registeredRefKeys.toString()}
|
|
5224
5525
|
${fleetLastSyncOf.toString()}
|
|
5526
|
+
// 部分失敗の判定(#634)。🔴 **ingestedAtOf / deriveCapTile / liveRowVerdict / ingestedEl が
|
|
5527
|
+
// すべてこの 1 本を引く。** 落とすと「最終同期」もタイルも Home も未定義を参照して落ちる
|
|
5528
|
+
${ingestFacts.toString()}
|
|
5529
|
+
${ingestSucceeded.toString()}
|
|
5225
5530
|
${ingestedAtOf.toString()}
|
|
5226
5531
|
${ingestedRowOf.toString()}
|
|
5227
5532
|
${runLogUrlOf.toString()}
|
|
@@ -5234,6 +5539,9 @@ var BAD_STATE_STATUSES = ${JSON.stringify(BAD_STATE_STATUSES)};
|
|
|
5234
5539
|
var STATE_CONFIRMING_TEXT = ${JSON.stringify(STATE_CONFIRMING_TEXT)};
|
|
5235
5540
|
${keyMissingText.toString()}
|
|
5236
5541
|
${deriveCapTile.toString()}
|
|
5542
|
+
// タイルの中の失敗の行(#634)。**stateRefKey / INGEST_FAILURES_MAX より後に置く**
|
|
5543
|
+
${ingestFailureRows.toString()}
|
|
5544
|
+
${ingestFailureText.toString()}
|
|
5237
5545
|
// 登録直後のタイル(#576)。**registeredRefKeys / stateRefKey / BAD_STATE_STATUSES より後に置く**
|
|
5238
5546
|
//(上の 3 つを引く。前に置くと var の BAD_STATE_STATUSES が undefined で判定が丸ごと死ぬ)
|
|
5239
5547
|
${derivePendingTile.toString()}
|
|
@@ -5757,10 +6065,10 @@ ${calendarAddUrl.toString()}
|
|
|
5757
6065
|
// 顧客も押すボタンなので、内側の運用事情を文面に出さない。
|
|
5758
6066
|
var AI_COMMON = "";
|
|
5759
6067
|
// Home(案件の入口)のAI編集。Gold層の4区画と違い規約READMEが無いので、Home自身を読ませて直させる。
|
|
5760
|
-
//
|
|
6068
|
+
// 「このプロジェクトについて」「ゴール」はAIが判断の方向を決める材料なので、未記入なら埋めることを促す。
|
|
5761
6069
|
var HOME_EDIT_PROMPT = [
|
|
5762
6070
|
"Cortex/Home.md を読んでから、このプロジェクトの入口ページを修正してください。",
|
|
5763
|
-
"
|
|
6071
|
+
"何を直すかは、まずわたしに質問してください。「このプロジェクトについて」「ゴール」が未記入なら、そこを埋めるところから始めてください。",
|
|
5764
6072
|
"frontmatter(識別カード・engine 設定)は触らないでください。日付・進捗・実績値は書かないでください(変わるものの正本は課題管理ツール等の外部にあります)。",
|
|
5765
6073
|
"このサイトの使い方の説明は書かないでください(説明は画面の側が持っています)。",
|
|
5766
6074
|
"書き込む前に、変更内容を提示して承認を得てください。",
|
|
@@ -5774,7 +6082,7 @@ ${calendarAddUrl.toString()}
|
|
|
5774
6082
|
* 「何をする案件か・誰のためか・どうなったら成功か」を必ず聞かせる。
|
|
5775
6083
|
*/
|
|
5776
6084
|
var HOME_WRITE_PROMPT = [
|
|
5777
|
-
"Cortex/Home.md
|
|
6085
|
+
"Cortex/Home.md を読んでから、「このプロジェクトについて」「ゴール」の2つの節を埋めてください。",
|
|
5778
6086
|
"まず、わたしに質問して内容を聞き出してください。少なくとも次の3つを確かめてください: 何をするプロジェクトか/誰のためのプロジェクトか(顧客・利用者は誰か)/どうなったら成功と言えるか。",
|
|
5779
6087
|
"聞き出した内容をもとに、2つの節を具体的な散文で書いてください。雛形の {{…}} は残さず、実際の内容に置き換えてください。",
|
|
5780
6088
|
"この2つの節は、毎晩のAI生成(決定事項・用語集・メンバー・ルールの抽出)がプロジェクトを理解するために読む場所です。抽象的な言葉ではなく、このプロジェクトに固有の具体で書いてください。",
|
|
@@ -7150,7 +7458,7 @@ ${calendarAddUrl.toString()}
|
|
|
7150
7458
|
"この仕組みについて": 1,
|
|
7151
7459
|
"情報の見方": 1,
|
|
7152
7460
|
"このサイトで見られるもの": 1,
|
|
7153
|
-
"
|
|
7461
|
+
"このプロジェクトについて": 1,
|
|
7154
7462
|
"ゴール": 1,
|
|
7155
7463
|
"体制": 1,
|
|
7156
7464
|
"このプロジェクトが目指すもの": 1,
|
|
@@ -10762,6 +11070,24 @@ ${calendarAddUrl.toString()}
|
|
|
10762
11070
|
if (tokenRelevant && sk && secretKindsShown().indexOf(sk) >= 0 && !secretPresentForTile(sk)) {
|
|
10763
11071
|
body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ トークンが未設定です" }));
|
|
10764
11072
|
}
|
|
11073
|
+
// 取り込めなかったファイル(#634)。**帯は症状(件数)・行は一手。**
|
|
11074
|
+
// 帯が「接続OK(251 件取り込み・1 件失敗)」と言っているのに、どのファイルが取り込めて
|
|
11075
|
+
// いないのかが画面から分からないと、利用者は run のログを読みに行くしかない
|
|
11076
|
+
// (=案件リポに入っていない人には辿れない)。
|
|
11077
|
+
// **出すのは導出が答えている回だけ**——まとめ表由来の warn で導出を捨てた回(derive が null)は
|
|
11078
|
+
// 「同期の run そのものが落ちた」という別の事実を言っているので、そこへ内訳を混ぜない
|
|
11079
|
+
if (derive && derive.state === "ok") {
|
|
11080
|
+
var failDerCap = derCapOf(cap, adapter);
|
|
11081
|
+
var failCapId = (TILE_CAPS[failDerCap] && TILE_CAPS[failDerCap].id) || "";
|
|
11082
|
+
var fails = failCapId ? ingestFailureRows(LIVE_STATE, failCapId, refKeysOf(failDerCap)) : [];
|
|
11083
|
+
for (var fi = 0; fi < fails.length && fi < INGEST_TILE_ROWS; fi++) {
|
|
11084
|
+
body.appendChild(el("div", { class: "ds-tile-note", text: "⚠ " + ingestFailureText(fails[fi]) }));
|
|
11085
|
+
}
|
|
11086
|
+
// **残りは畳む。** 20 行並べるとタイルが理由文で埋まる(0.4.71 で溢れた形)
|
|
11087
|
+
if (fails.length > INGEST_TILE_ROWS) {
|
|
11088
|
+
body.appendChild(el("div", { class: "ds-tile-note ds-tile-todo", text: "ほか " + (fails.length - INGEST_TILE_ROWS) + " 件" }));
|
|
11089
|
+
}
|
|
11090
|
+
}
|
|
10765
11091
|
// 🔴 **セットアップの残り(checks)の長文はタイルに出さない(#230・オーナー指摘)。**
|
|
10766
11092
|
//
|
|
10767
11093
|
// 出所は engine の夜間チェックの 「action」 欄で、**この設定画面が無かった頃の
|
|
@@ -11856,7 +12182,14 @@ ${calendarAddUrl.toString()}
|
|
|
11856
12182
|
if (!r) return null;
|
|
11857
12183
|
var when = rtAgo(r.at);
|
|
11858
12184
|
var text = "";
|
|
11859
|
-
|
|
12185
|
+
// 🔴 **部分失敗は「失敗」と書かない(#634・設計レビュー🔴3)。** 判定はタイルと同じ 1 本
|
|
12186
|
+
// (「ingestFacts」)を通す。ここを掃かないと、便 2 で Backlog / Figma が ok +
|
|
12187
|
+
// counts.failed > 0 を書き始めた瞬間、**タイルは「接続OK(N 件取り込み・M 件失敗)」・
|
|
12188
|
+
// 札は「取り込み: ◯◯に成功」**になり、失敗が行から消える(R10 の逆向き)。
|
|
12189
|
+
// 件数を添えるのは失敗が 1 件以上あるときだけ——全部成功した回に「(0 件失敗)」を出さない
|
|
12190
|
+
var f = ingestFacts(r);
|
|
12191
|
+
var fw = f && f.failed > 0 ? "(" + f.failed + " 件失敗)" : "";
|
|
12192
|
+
if (r.status === "ok" || (f && f.partial)) text = (when ? "取り込み: " + when + "に成功" : "取り込み: 成功") + fw;
|
|
11860
12193
|
else if (r.status === "failed") text = when ? "取り込み: " + when + "に失敗" : "取り込み: 失敗";
|
|
11861
12194
|
// 🔴 **「スキップ」で終わらせない(#225)。** 鍵が無くて飛ばしたのが skipped の実体で、
|
|
11862
12195
|
// 「スキップ」とだけ書かれても**次に何をすればいいのか読み取れない**。しかも
|
|
@@ -11864,8 +12197,10 @@ ${calendarAddUrl.toString()}
|
|
|
11864
12197
|
// 語は鍵カードの名前と揃える(keyMissingText の項)
|
|
11865
12198
|
else if (r.status === "skipped") text = "取り込み: 未実行(" + keyMissingText(cap) + ")";
|
|
11866
12199
|
if (!text) return null;
|
|
12200
|
+
// 赤にするのは「1 件も取り込めなかった」回だけ(#634)。部分失敗は取り込めているので、
|
|
12201
|
+
// 件数を添えた成功の札のまま出す(色で「壊れている」と言わない)
|
|
11867
12202
|
var span = el("span", {
|
|
11868
|
-
class: "ds-ingest" + (r.status === "failed" ? " ds-ingest-failed" : ""),
|
|
12203
|
+
class: "ds-ingest" + (r.status === "failed" && !(f && f.partial) ? " ds-ingest-failed" : ""),
|
|
11869
12204
|
text: text,
|
|
11870
12205
|
});
|
|
11871
12206
|
// その行を書いた run へ(#560)。成功でも失敗でも、この時刻を書いた run のログ
|
package/package.json
CHANGED