fix(ticket-management): 采纳复核 —— 段位词表补全 + 评论可执行物提示 + 顺序如实判定
复核回执 R-20260924-103820-a8f2(现状梳理)逐条实跑核对后落地。 【采纳】 - 词表按 #157/#159 真实小节名补齐:`结论`(结论即提案)→ 提案 · `生产实测` → 背景 · `基线` → 效果; `取证记录` 进「其他已识别小节」(原报「未归类」是误报)。 - 去掉「更正 / 补充」特例 ⇒ 顺序改按主叙事**如实**判定(复核指出 `## 影响面(实测更正)` 被错列成「后置小节」);提示语写明「确为更正小节后置时可保留,由人判断」。 - 缺「提案」的措辞给出路:提示「若『结论即提案』或结论为『无需处置』,请显式写一段声明」。 - 评论清单加 `[可执行]` / `[? 非可执行]` 列(启发式:代码围栏或 SQL 关键字)⇒ 「评论只应剩必须可执行物」由人肉核变为机器提示 + 人核;SKILL §8.1/§8.3 同步。 - `selftest` 13 → **20** 条(补 结论即提案 / 基线归效果 / 取证记录 / 可执行物三态 / 方案明细不被「实测」抢走)。 【自查中自己踩的坑(已修 + 加回归用例)】 - 首版把裸词 `实测` 加进背景词表 ⇒ `## 方案明细(…实测代价)` 被抢归「问题背景」,`#158` 误报「顺序 ⚠」。 改为 `生产实测`,并加 selftest 锁死该回归。 【不采纳(附理由)】 - 「`闸门=不处置` 作为提案的替代」:闸门是断言 4 的受控词表(等口径 / 等取证 / 等部署), 新增取值属口径变更且会使「不能开工」语义漂移 ⇒ 改用「`## 结论` 可替代提案 + 正文显式声明」, **不动闸门词表**;确要加取值请用户拍板。 【验证 · E1 · 本机】 - `node --check` 通过;`selftest` **20/20**;`verify 157 158 159` = **21/21**。 - `assess`:#158 四段全 ✓ · 顺序 OK · 空壳 无 · 两条评论均判 `[可执行]`; #157 四段(结果 / 效果未发生 ⇒ 不长空壳)· 顺序 OK; #159 ⇒ **顺序 ⚠**(`影响面` 落在 `机制` 之后)—— 如实判定,前移或按更正小节保留由属主定。
This commit is contained in:
@@ -170,16 +170,19 @@ node bin/ticket.mjs rename 142 --repo=xbc/xbc --domain=平台 [--gate=等口径]
|
|||||||
|
|
||||||
| 段 | 回答的问题 | 典型小标题 |
|
| 段 | 回答的问题 | 典型小标题 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **① 问题背景**(必备) | 谁 / 什么场景 / 什么问题 · 影响面 · 根因(含证据) | `## 现象` `## 根因` `## 影响面` |
|
| **① 问题背景**(必备) | 谁 / 什么场景 / 什么问题 · 影响面 · 根因(含证据 / 生产实测) | `## 现象` `## 根因` `## 影响面` `## 生产实测阶梯` |
|
||||||
| **② 处置提案**(必备) | 打算怎么做 / 已选哪条路线 / 为什么 | `## 方案` `## 机制` `## 两条修复路线` |
|
| **② 处置提案**(必备) | 打算怎么做 / 已选哪条路线 / 为什么 | `## 方案` `## 机制` `## 两条修复路线` `## 结论` |
|
||||||
| **③ 处置结果**(按事实) | 实际做了什么 · 当前状态(已执行 / 挂起 / 未动) | `## 执行回执` `## 当前状态` |
|
| **③ 处置结果**(按事实) | 实际做了什么 · 当前状态(已执行 / 挂起 / 未动) | `## 执行回执` `## 当前状态` `## 处置结果` |
|
||||||
| **④ 效果对比**(按事实) | 改前 vs 改后:**指标 + 口径 + 时点**同表对照 | `## 效果对比` `## T0 / T1 对照` |
|
| **④ 效果对比**(按事实) | 改前 vs 改后:**指标 + 口径 + 时点**同表对照 | `## 效果对比` `## 生产基线 T0` `## T0 / T1 对照` |
|
||||||
|
|
||||||
- ③④ **只在真的做了 / 真的测了之后**才写;**未发生就整段不写**(不留空壳 —— 空壳等于噪音)。
|
- ③④ **只在真的做了 / 真的测了之后**才写;**未发生就整段不写**(不留空壳 —— 空壳等于噪音)。
|
||||||
- **可选补段**(一律放四段之后):`## 验收判据` · `## 反证条件` · `## 遗留`。
|
- **同一段可以有多个并列小节**:用 `## <段名> · <子题>`(实测:`## 效果对比 · 生产基线 T0` + `## 效果对比 · T1/T2 判据与实测`)—— `assess` 会**合并计为一节**。
|
||||||
|
- **没有「提案」时不要硬造**:`## 结论`(**结论即提案**)可替代;若结论是「**无需处置**」或「**与 #n 不是同一根因**」,就在正文**显式写一段结论声明**。
|
||||||
|
- **其他已识别小节**(不计入四段):`## 验收判据` · `## 反证条件` · `## 遗留` · `## 取证记录`。
|
||||||
- 正文必须**自包含**(§1 规则 3)—— 只读正文就能回答:**现在什么状态 / 要做什么 / 怎么验收 / 效果如何**。
|
- 正文必须**自包含**(§1 规则 3)—— 只读正文就能回答:**现在什么状态 / 要做什么 / 怎么验收 / 效果如何**。
|
||||||
2. **评论只保留「必须可执行物」**:可执行 SQL / 命令 / 待跑清单。**其余过程性评论一律删除**(不是「标注过时后留着」)。
|
2. **评论只保留「必须可执行物」**:可执行 SQL / 命令 / 待跑清单。**其余过程性评论一律删除**(不是「标注过时后留着」)。
|
||||||
⚠ **可执行物不得因「正文已列名字」而删**(2026-09-24 实测事故):DDL / SQL 这类东西,**判定可删的唯一判据是「其全文已被正文或另一条评论完整承载」**;只列了对象名(如索引名清单)**不等于**承载了可执行语句 —— 一旦删除,工单就**没法照着做**了。
|
⚠ **可执行物不得因「正文已列名字」而删**(2026-09-24 实测事故):DDL / SQL 这类东西,**判定可删的唯一判据是「其全文已被正文或另一条评论完整承载」**;只列了对象名(如索引名清单)**不等于**承载了可执行语句 —— 一旦删除,工单就**没法照着做**了。
|
||||||
|
**工具辅助**:`assess` / `comments` 会给每条评论标 `[可执行]` / `[? 非可执行]`(**启发式**:含代码围栏或 SQL 关键字)—— 只作提示,**仍需人核**。
|
||||||
⚠ **工单里的 SQL 必须逐条原样**(2026-09-24 实测事故):**禁止**对语句做「拼接 / 去尾 / 换分隔符」式字符串变换(实测把 `, ALGORITHM=INPLACE, LOCK=NONE;` 换成逗号 ⇒ 18 条语句连成一段**无分号的废码**);**回退脚本不得留占位符**(列序 / 唯一性要填实)—— 否则「可执行物」实际**不可执行**。**投递前必须实跑验证**(结构影子库零数据逐条执行,正反向都要过)。
|
⚠ **工单里的 SQL 必须逐条原样**(2026-09-24 实测事故):**禁止**对语句做「拼接 / 去尾 / 换分隔符」式字符串变换(实测把 `, ALGORITHM=INPLACE, LOCK=NONE;` 换成逗号 ⇒ 18 条语句连成一段**无分号的废码**);**回退脚本不得留占位符**(列序 / 唯一性要填实)—— 否则「可执行物」实际**不可执行**。**投递前必须实跑验证**(结构影子库零数据逐条执行,正反向都要过)。
|
||||||
3. **过程流水与逐条取证进外部记录**(分析文档 / 台账底册),工单里**只在末尾引用块登记**(§3.3);人读部分**不得**出现外部路径与编号。
|
3. **过程流水与逐条取证进外部记录**(分析文档 / 台账底册),工单里**只在末尾引用块登记**(§3.3);人读部分**不得**出现外部路径与编号。
|
||||||
4. **口径更新必须同步正文与标题**:凡结论/数量/清单发生变化(如「24 条」→「23 条」、「删 5」→「删 4」),必须**同一次**改掉**标题 + 正文 + 摘要评论**(`update` 支持标题与正文同提交);**旧口径若仍在被引用(如执行清单),必须显式标注「已废止」并写明正确值**。
|
4. **口径更新必须同步正文与标题**:凡结论/数量/清单发生变化(如「24 条」→「23 条」、「删 5」→「删 4」),必须**同一次**改掉**标题 + 正文 + 摘要评论**(`update` 支持标题与正文同提交);**旧口径若仍在被引用(如执行清单),必须显式标注「已废止」并写明正确值**。
|
||||||
@@ -216,7 +219,7 @@ node bin/ticket.mjs rename 142 --repo=xbc/xbc --domain=平台 [--gate=等口径]
|
|||||||
- [ ] 四段**顺序正确**,**没有空壳段**(标题下没内容 ⇒ 删);
|
- [ ] 四段**顺序正确**,**没有空壳段**(标题下没内容 ⇒ 删);
|
||||||
- [ ] 正文**没有**「旧版 / 已废止」并列堆叠 —— 旧值已删,或压成一句「已废止,正确值 X」;
|
- [ ] 正文**没有**「旧版 / 已废止」并列堆叠 —— 旧值已删,或压成一句「已废止,正确值 X」;
|
||||||
- [ ] 标题 / 正文 / 评论里的**数量与范围一致**(「23 条」不能只改一处);
|
- [ ] 标题 / 正文 / 评论里的**数量与范围一致**(「23 条」不能只改一处);
|
||||||
- [ ] 评论**只剩可执行物**,且**可执行物完整可跑**(是全文,不是对象名);
|
- [ ] 评论**只剩可执行物**,且**可执行物完整可跑**(是全文,不是对象名);工具标 `[? 非可执行]` 的**逐条核过**;
|
||||||
- [ ] 同一条信息**没有两处详写**(一处详写,其余 `#n` 交叉引用);
|
- [ ] 同一条信息**没有两处详写**(一处详写,其余 `#n` 交叉引用);
|
||||||
- [ ] 首行机读行仍是 ` | ` 形态、字段顺序未被打乱。
|
- [ ] 首行机读行仍是 ` | ` 形态、字段顺序未被打乱。
|
||||||
|
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ const BAD = ["codes/", "分析/数据/", "分析/D-", "分析/E-", "分析/A-",
|
|||||||
|
|
||||||
// §8.1「任务全貌」四段:正文骨架词表(数组顺序 = 规范顺序)
|
// §8.1「任务全貌」四段:正文骨架词表(数组顺序 = 规范顺序)
|
||||||
const SECTIONS = [
|
const SECTIONS = [
|
||||||
{ key: "问题背景", words: ["现象", "根因", "影响面", "问题背景", "背景"], need: "必备" },
|
{ key: "问题背景", words: ["现象", "根因", "影响面", "问题背景", "背景", "生产实测"], need: "必备" },
|
||||||
{ key: "处置提案", words: ["处置提案", "方案", "机制", "提案", "路线"], need: "必备" },
|
{ key: "处置提案", words: ["处置提案", "方案", "机制", "提案", "路线", "结论"], need: "必备" },
|
||||||
{ key: "处置结果", words: ["处置结果", "执行回执", "当前状态", "回执", "结果"], need: "按事实" },
|
{ key: "处置结果", words: ["处置结果", "执行回执", "当前状态", "回执", "结果"], need: "按事实" },
|
||||||
{ key: "效果对比", words: ["效果对比", "收益验证", "收益", "对照", "效果"], need: "按事实" }
|
{ key: "效果对比", words: ["效果对比", "收益验证", "收益", "对照", "效果", "基线"], need: "按事实" }
|
||||||
];
|
];
|
||||||
const OPTIONAL_SECTIONS = ["验收判据", "反证条件", "遗留"];
|
// 四段之外「已识别的小节」:不计入四段,但也不该被报成「未归类」
|
||||||
|
const OPTIONAL_SECTIONS = ["验收判据", "反证条件", "遗留", "取证记录"];
|
||||||
|
|
||||||
// 取值型选项:`--repo=o/r` 与 `--repo o/r` 两种写法都收(等价;布尔开关不吃后一个参数)
|
// 取值型选项:`--repo=o/r` 与 `--repo o/r` 两种写法都收(等价;布尔开关不吃后一个参数)
|
||||||
const VALUE_OPTS = ["repo", "title", "body-file", "label", "milestone", "token-file", "host", "state", "limit", "domain", "gate", "dump"];
|
const VALUE_OPTS = ["repo", "title", "body-file", "label", "milestone", "token-file", "host", "state", "limit", "domain", "gate", "dump"];
|
||||||
@@ -100,7 +101,6 @@ function sectionIndex(text) {
|
|||||||
}
|
}
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
function isPostNote(text) { return /更正|订正|勘误|补充|追加/.test(text); }
|
|
||||||
function isOptional(text) { return OPTIONAL_SECTIONS.some(function (w) { return text.indexOf(w) >= 0; }); }
|
function isOptional(text) { return OPTIONAL_SECTIONS.some(function (w) { return text.indexOf(w) >= 0; }); }
|
||||||
function structureOf(body) {
|
function structureOf(body) {
|
||||||
const lines = humanPart(body).split(NL);
|
const lines = humanPart(body).split(NL);
|
||||||
@@ -119,16 +119,15 @@ function structureOf(body) {
|
|||||||
if (!inner) empty.push(lines[heads[k].pos].trim());
|
if (!inner) empty.push(lines[heads[k].pos].trim());
|
||||||
}
|
}
|
||||||
// 归类只看 ## 级;「更正 / 补充」小节允许后置,不计入顺序
|
// 归类只看 ## 级;「更正 / 补充」小节允许后置,不计入顺序
|
||||||
|
// 归类只看 ## 级;顺序按「主叙事」如实判定(「更正 / 补充」小节若后置会如实报出,由人判断)
|
||||||
const tops = heads.filter(function (h) { return h.level === 2; })
|
const tops = heads.filter(function (h) { return h.level === 2; })
|
||||||
.map(function (h) { return { text: h.text, idx: sectionIndex(h.text), post: isPostNote(h.text) }; });
|
.map(function (h) { return { text: h.text, idx: sectionIndex(h.text) }; });
|
||||||
const main = tops.filter(function (t) { return !t.post && t.idx >= 0; });
|
|
||||||
const order = main.map(function (t) { return t.idx; });
|
|
||||||
const ordered = order.every(function (v, i) { return i === 0 || order[i - 1] <= v; });
|
|
||||||
const hits = tops.filter(function (t) { return t.idx >= 0; });
|
const hits = tops.filter(function (t) { return t.idx >= 0; });
|
||||||
const postNotes = tops.filter(function (t) { return t.post; }).map(function (t) { return t.text; });
|
const order = hits.map(function (t) { return t.idx; });
|
||||||
const unclassified = tops.filter(function (t) { return t.idx < 0 && !t.post && !isOptional(t.text); }).map(function (t) { return t.text; });
|
const ordered = order.every(function (v, i) { return i === 0 || order[i - 1] <= v; });
|
||||||
|
const unclassified = tops.filter(function (t) { return t.idx < 0 && !isOptional(t.text); }).map(function (t) { return t.text; });
|
||||||
const optional = OPTIONAL_SECTIONS.filter(function (w) { return heads.some(function (h) { return h.text.indexOf(w) >= 0; }); });
|
const optional = OPTIONAL_SECTIONS.filter(function (w) { return heads.some(function (h) { return h.text.indexOf(w) >= 0; }); });
|
||||||
return { hits: hits, ordered: ordered, empty: empty, optional: optional, postNotes: postNotes, unclassified: unclassified };
|
return { hits: hits, ordered: ordered, empty: empty, optional: optional, unclassified: unclassified };
|
||||||
}
|
}
|
||||||
function printStructure(body) {
|
function printStructure(body) {
|
||||||
const st = structureOf(body);
|
const st = structureOf(body);
|
||||||
@@ -136,15 +135,21 @@ function printStructure(body) {
|
|||||||
for (let i = 0; i < SECTIONS.length; i++) {
|
for (let i = 0; i < SECTIONS.length; i++) {
|
||||||
const own = st.hits.filter(function (x) { return x.idx === i; }).map(function (x) { return "## " + x.text; });
|
const own = st.hits.filter(function (x) { return x.idx === i; }).map(function (x) { return "## " + x.text; });
|
||||||
const tail = own.length ? " " + own.join(" · ")
|
const tail = own.length ? " " + own.join(" · ")
|
||||||
: (SECTIONS[i].need === "必备" ? " ← 未识别到(必备段:确认是否用了同义小标题,或确实缺)" : " ← 未识别到(未发生 / 未测就不写,不要空壳)");
|
: (SECTIONS[i].need === "必备" ? " ← 未识别到(必备段:确认是否用了同义小标题;若「结论即提案」或结论为「无需处置」,请显式写一段声明)" : " ← 未识别到(未发生 / 未测就不写,不要空壳)");
|
||||||
console.log(" " + (own.length ? "[✓]" : "[ ]") + " " + SECTIONS[i].key + "(" + SECTIONS[i].need + ")" + tail);
|
console.log(" " + (own.length ? "[✓]" : "[ ]") + " " + SECTIONS[i].key + "(" + SECTIONS[i].need + ")" + tail);
|
||||||
}
|
}
|
||||||
console.log(" 顺序:" + (st.ordered ? "OK" : "⚠ 主叙事未按 背景→提案→结果→效果 排列 ⇒ 调整(更正 / 补充小节不计)"));
|
console.log(" 顺序:" + (st.ordered ? "OK" : "⚠ 主叙事未按 背景→提案→结果→效果 排列 ⇒ 调整(确为「更正 / 补充」后置小节时可保留,由人判断)"));
|
||||||
if (st.postNotes.length) console.log(" 后置小节(不计顺序):" + st.postNotes.map(function (t) { return "## " + t; }).join(" · "));
|
|
||||||
if (st.unclassified.length) console.log(" 未归类 ## 小节(人工判断归属):" + st.unclassified.map(function (t) { return "## " + t; }).join(" · "));
|
if (st.unclassified.length) console.log(" 未归类 ## 小节(人工判断归属):" + st.unclassified.map(function (t) { return "## " + t; }).join(" · "));
|
||||||
console.log(" 空壳段:" + (st.empty.length ? "✗ " + st.empty.join(" · ") + "(删掉或补齐)" : "无"));
|
console.log(" 空壳段:" + (st.empty.length ? "✗ " + st.empty.join(" · ") + "(删掉或补齐)" : "无"));
|
||||||
console.log(" 可选段:" + (st.optional.length ? st.optional.join(" · ") : "无"));
|
console.log(" 其他已识别小节(不计入四段):" + (st.optional.length ? st.optional.join(" · ") : "无"));
|
||||||
}
|
}
|
||||||
|
// 「必须可执行物」的启发式判据(§8.1 第 2 条):含代码围栏或 SQL 关键字 —— 只作提示,仍需人核
|
||||||
|
const FENCE = String.fromCharCode(96, 96, 96);
|
||||||
|
function isExecutable(body) {
|
||||||
|
const b = String(body || "");
|
||||||
|
return b.indexOf(FENCE) >= 0 || /\b(SELECT|INSERT|UPDATE|DELETE|ALTER|CREATE|DROP|SHOW|EXPLAIN|ANALYZE|DESCRIBE|SET)\b/i.test(b);
|
||||||
|
}
|
||||||
|
|
||||||
// 自检(不联网、不需要令牌):node bin/ticket.mjs selftest
|
// 自检(不联网、不需要令牌):node bin/ticket.mjs selftest
|
||||||
function cmdSelftest() {
|
function cmdSelftest() {
|
||||||
let pass = 0, fail = 0;
|
let pass = 0, fail = 0;
|
||||||
@@ -177,6 +182,18 @@ function cmdSelftest() {
|
|||||||
t("空壳段可被识别", sEmpty.empty.length === 1);
|
t("空壳段可被识别", sEmpty.empty.length === 1);
|
||||||
const sOrder = structureOf("域=平台-性能-索引" + NL + NL + "## 效果对比" + NL + "a" + NL + NL + "## 现象" + NL + "b");
|
const sOrder = structureOf("域=平台-性能-索引" + NL + NL + "## 效果对比" + NL + "a" + NL + NL + "## 现象" + NL + "b");
|
||||||
t("乱序可被识别", sOrder.ordered === false);
|
t("乱序可被识别", sOrder.ordered === false);
|
||||||
|
// —— 复核采纳:结论即提案 / 基线归效果 / 取证记录不再误报 / 可执行物启发式 ——
|
||||||
|
const sConclusion = structureOf("域=平台-性能-分区" + NL + NL + "## 现象" + NL + "坏。" + NL + NL + "## 结论:与索引的分工" + NL + "分工。");
|
||||||
|
t("「结论即提案」可被识别为提案", sConclusion.hits.some(function (x) { return x.idx === 1; }) === true);
|
||||||
|
const sBaseline = structureOf("域=平台-性能-索引" + NL + NL + "## 现象" + NL + "x" + NL + NL + "## 生产基线 T0" + NL + "y");
|
||||||
|
t("「基线」可被识别为效果对比", sBaseline.hits.some(function (x) { return x.idx === 3; }) === true);
|
||||||
|
const sEvid = structureOf("域=平台-性能-索引" + NL + NL + "## 现象" + NL + "x" + NL + NL + "## 取证记录" + NL + "y");
|
||||||
|
t("「取证记录」不再报成未归类", sEvid.unclassified.length === 0 && sEvid.optional.indexOf("取证记录") >= 0);
|
||||||
|
const sDetail = structureOf("域=平台-性能-索引" + NL + NL + "## 现象" + NL + "x" + NL + NL + "## 方案明细(… 实测代价)" + NL + "y");
|
||||||
|
t("「方案明细(…实测代价)」归提案(不被『实测』抢走)", sDetail.hits.some(function (x) { return x.idx === 1; }) === true);
|
||||||
|
t("可执行物启发式:代码围栏", isExecutable(FENCE + "bash" + NL + "echo hi" + NL + FENCE) === true);
|
||||||
|
t("可执行物启发式:SQL 关键字", isExecutable("跑这条 SELECT 即可") === true);
|
||||||
|
t("可执行物启发式:过程叙述判非可执行", isExecutable("今天讨论了一下,暂时没有结论") === false);
|
||||||
console.log("自检 " + pass + "/" + (pass + fail));
|
console.log("自检 " + pass + "/" + (pass + fail));
|
||||||
return fail === 0 ? 0 : 1;
|
return fail === 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
@@ -243,8 +260,9 @@ async function cmdAssess(api, opt, n) {
|
|||||||
console.log("评论清单(" + comments.length + ")—— 只应剩「必须可执行物」(§8.1 第 2 条):");
|
console.log("评论清单(" + comments.length + ")—— 只应剩「必须可执行物」(§8.1 第 2 条):");
|
||||||
for (const c of comments) {
|
for (const c of comments) {
|
||||||
const b = String(c.body || "");
|
const b = String(c.body || "");
|
||||||
console.log(" " + c.id + " " + b.length + " 字 首行: " + (b.split(NL)[0] || "").slice(0, 90));
|
console.log(" " + c.id + " " + b.length + " 字 " + (isExecutable(b) ? "[可执行]" : "[? 非可执行]") + " 首行: " + (b.split(NL)[0] || "").slice(0, 90));
|
||||||
}
|
}
|
||||||
|
console.log(" ([可执行] 为**启发式**:含代码围栏或 SQL 关键字 ⇒ 仍需人核)");
|
||||||
if (comments.length > 1) console.log(" ⚠ 多条评论 ⇒ 逐条过 §8.2;删前先备份:comments " + n + " --dump=<文件>(§8.1 第 5 条)");
|
if (comments.length > 1) console.log(" ⚠ 多条评论 ⇒ 逐条过 §8.2;删前先备份:comments " + n + " --dump=<文件>(§8.1 第 5 条)");
|
||||||
}
|
}
|
||||||
console.log("下一步:把本轮候选信息逐条填进 §8.0 的落点表 ⇒ 再 create / update / comment-add。");
|
console.log("下一步:把本轮候选信息逐条填进 §8.0 的落点表 ⇒ 再 create / update / comment-add。");
|
||||||
@@ -329,8 +347,9 @@ async function cmdComments(api, opt, n) {
|
|||||||
console.log("#" + n + " 评论 " + arr.length + " 条(只应剩「必须可执行物」):");
|
console.log("#" + n + " 评论 " + arr.length + " 条(只应剩「必须可执行物」):");
|
||||||
for (const c of arr) {
|
for (const c of arr) {
|
||||||
const b = String(c.body || "");
|
const b = String(c.body || "");
|
||||||
console.log(" " + c.id + " " + b.length + " 字 首行: " + (b.split(NL)[0] || "").slice(0, 90));
|
console.log(" " + c.id + " " + b.length + " 字 " + (isExecutable(b) ? "[可执行]" : "[? 非可执行]") + " 首行: " + (b.split(NL)[0] || "").slice(0, 90));
|
||||||
}
|
}
|
||||||
|
console.log("([可执行] 为**启发式**:含代码围栏或 SQL 关键字 ⇒ 仍需人核)");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
async function cmdCommentAdd(api, opt, n) {
|
async function cmdCommentAdd(api, opt, n) {
|
||||||
|
|||||||
Reference in New Issue
Block a user