misakanet 2.44.2 → 2.46.0
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/.codex-plugin/plugin.json +1 -1
- package/LICENSE +201 -201
- package/README.ja.md +172 -292
- package/README.md +144 -282
- package/README.zh-CN.md +197 -374
- package/SKILL.md +9 -4
- package/lib/client.js +256 -21
- package/package.json +1 -1
- package/skills/misakanet/SKILL.md +9 -4
package/SKILL.md
CHANGED
|
@@ -97,13 +97,17 @@ misakanet_preflight(...)
|
|
|
97
97
|
|
|
98
98
|
Checks risk level before executing a high-risk operation.
|
|
99
99
|
|
|
100
|
-
### Lesson reuse evidence
|
|
100
|
+
### Lesson reuse evidence
|
|
101
101
|
|
|
102
102
|
```
|
|
103
103
|
misakanet_me_events(lesson_id="some-lesson")
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
Returns evidence of a lesson being reused
|
|
106
|
+
Returns evidence of a lesson being reused. `E4` is **observed** reuse: two or more distinct clients
|
|
107
|
+
actually retrieved the lesson (`search_signals.client_hint`) — a count of requests, not of prose.
|
|
108
|
+
Regression-benchmark citations, helpful votes, and a lesson's own `verified by:` line are `E3`: a
|
|
109
|
+
citation is sandbox-level evidence, a vote is another person's opinion, and a provenance line is the
|
|
110
|
+
lesson talking about itself, so none of those hints reaches `E4`.
|
|
107
111
|
|
|
108
112
|
## Examples
|
|
109
113
|
|
|
@@ -174,7 +178,8 @@ Run this roughly every 10 sessions (or weekly):
|
|
|
174
178
|
(`/api/insights/unsolved-map` or the demand board); pick one gap you can
|
|
175
179
|
actually cover and write the lesson.
|
|
176
180
|
5. **Verify your own usage.** Confirm your past submissions were merged and
|
|
177
|
-
are being reused (`me_events`
|
|
181
|
+
are being reused (`me_events` observes distinct-client retrieval and citations; a helpful
|
|
182
|
+
vote is only a hint). If a
|
|
178
183
|
lesson of yours is never cited, its problem statement may not match real
|
|
179
184
|
queries — tighten the title and triggers.
|
|
180
185
|
|
|
@@ -188,7 +193,7 @@ Trigger a contribution automatically when any of these happen:
|
|
|
188
193
|
|---|---|
|
|
189
194
|
| A bug is fixed and CI passes | Queue the fix as a lesson draft (`queue_lesson.py`) |
|
|
190
195
|
| An existing lesson matched but did NOT fix the problem | Submit not-helpful feedback AND intake with `matched_lesson_id` |
|
|
191
|
-
| A lesson solved your problem | Record a helpful/usage report —
|
|
196
|
+
| A lesson solved your problem | Record a helpful/usage report — a hint for the next reader; `E4` itself comes from observed reuse, not a vote |
|
|
192
197
|
| A crash/tombstone is captured | Convert it to a draft lesson (`tombstone_to_draft.py`) |
|
|
193
198
|
| A fix took longer than ~15 minutes and no lesson matched | You just earned the lesson — submit it before context is lost |
|
|
194
199
|
| A lesson's evidence_level is below what you need | Contribute a reproduction/verification and request promotion |
|
package/lib/client.js
CHANGED
|
@@ -27,12 +27,14 @@
|
|
|
27
27
|
* screen, and the only surface from which a human can speak about it.
|
|
28
28
|
*
|
|
29
29
|
* Why the vote is worth this much care: `POST /api/helpful` is the **only** writer of the counter that
|
|
30
|
-
* `misakanet_me_events` reports as `lesson_found_helpful
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* `misakanet_me_events` reports as `lesson_found_helpful`. That event is a *hint*, not the rung: since
|
|
31
|
+
* 2026-10-16 a vote is capped below `E4` at every count (measured then: 3 of the 24 most-retrieved
|
|
32
|
+
* lessons carried any vote, 6 votes in total, 0 of 483 lessons at `E4`), because the person shown the
|
|
33
|
+
* button did not ask the question, and the reuse the rung is about is observable at the transport
|
|
34
|
+
* layer instead. Three rules follow, and they explain several odd-looking details below:
|
|
33
35
|
*
|
|
34
36
|
* 1. **Never vote on the human's behalf.** No auto-vote on a successful build, no default selection,
|
|
35
|
-
* no pre-checked box:
|
|
37
|
+
* no pre-checked box: a vote means something only because a person chose to give it.
|
|
36
38
|
* 2. **Abstention is free and is the default.** Doing nothing is a first-class outcome — the buttons
|
|
37
39
|
* appear, and nothing nags if they are ignored.
|
|
38
40
|
* 3. **Say what each action sends.** 👍 posts the lesson id and nothing else. 👎 has to carry the search
|
|
@@ -174,6 +176,8 @@ window.__ModuleLoader__.load({
|
|
|
174
176
|
"unit.reports": "reports",
|
|
175
177
|
"unit.reportFiled": "report filed",
|
|
176
178
|
"unit.reportsFiled": "reports filed",
|
|
179
|
+
"unit.retrieval": "retrieval",
|
|
180
|
+
"unit.retrievals": "retrievals",
|
|
177
181
|
"panel.asked.searching": "searching…",
|
|
178
182
|
"panel.asked.noMatch": "no lesson matched",
|
|
179
183
|
"panel.asked.top": "· top: ",
|
|
@@ -186,13 +190,35 @@ window.__ModuleLoader__.load({
|
|
|
186
190
|
"panel.asked.voteHint": "Same action as the verdict beside the assistant message, without scrolling back to it. Sends this lesson's id and one constant label (`dsh-web`); nothing else.",
|
|
187
191
|
"panel.asked.voteFailed": "could not reach misakanet.org — nothing was recorded",
|
|
188
192
|
"panel.asked.voteOne": "1 human confirmation — the reuse event now appears (E3: a reuse signal, not CI verification).",
|
|
189
|
-
"panel.asked.voteTwo": "2 human confirmations —
|
|
190
|
-
"panel.asked.voteMore": "human confirmations —
|
|
193
|
+
"panel.asked.voteTwo": "2 human confirmations — recorded as a hint; the E4 rung comes from observable reuse, not a vote.",
|
|
194
|
+
"panel.asked.voteMore": "human confirmations — recorded as a hint; the E4 rung comes from observable reuse, not a vote.",
|
|
191
195
|
"panel.asked.noMatchTag": "no match",
|
|
192
196
|
"panel.asked.agentNext": "Hand this line to the agent:",
|
|
193
197
|
"panel.asked.agentNextCopy": "Copy the next step",
|
|
194
198
|
"panel.asked.agentNextText": "Please record this gap with the MisakaNet intake so the next person finds it: misakanet_submit_intake problem: ",
|
|
195
199
|
"panel.asked.agentNextWhy": "This page cannot file it: a report is re-checked by submitting the same text again, the server's dedup window is finite, and a page-triggered submit could open a second issue for one problem.",
|
|
200
|
+
// The retrieval ranking is the network's own observation, not this session's, and it is not a
|
|
201
|
+
// judgment. Every word is a lookup for the same reason the counters are — a zh reader has to get
|
|
202
|
+
// zh — and the one thing none of these strings may say is that the order is approval: the count
|
|
203
|
+
// is how often a search put that lesson first, which says nothing about whether it helped.
|
|
204
|
+
"panel.hot": "What the corpus returns most",
|
|
205
|
+
"panel.hot.loading": "reading the network's retrieval ranking…",
|
|
206
|
+
"panel.hot.none": "The analytics endpoint ranked no lesson, so this section shows nothing rather than a zero.",
|
|
207
|
+
"panel.hot.rule": "Each number is how many times a search ranked that lesson as its top hit — a retrieval count from the network's own analytics. It is an observed count, not a vote, not an approval, and not evidence that the lesson helped.",
|
|
208
|
+
"panel.hot.limit": "The {n} most-retrieved lessons — the endpoint returns more, so this list stops at {n}.",
|
|
209
|
+
"panel.hot.noCount": "the endpoint did not report a count",
|
|
210
|
+
// The coverage read answers the *other* half of the same question, and it must not read as a
|
|
211
|
+
// second version of the ranking above: section ⑥ counts what searches brought back, this counts
|
|
212
|
+
// what they did not find. Every visible word is a lookup for the same reason the counters are,
|
|
213
|
+
// and the one thing none of these strings may say is that the order is an opinion — it is a
|
|
214
|
+
// count of unanswered searches.
|
|
215
|
+
"panel.coverage": "The topics the corpus cannot answer yet",
|
|
216
|
+
"panel.coverage.loading": "reading the network's coverage board…",
|
|
217
|
+
"panel.coverage.none": "The coverage board ranked no topic, so this section shows nothing rather than a guess.",
|
|
218
|
+
"panel.coverage.rule": "The ranking above counts what searches brought back; this one counts what they did not find. Each row is a topic with the last 30 days' unanswered searches, read from the network's own coverage board, and the number of published lessons that match it.",
|
|
219
|
+
"panel.coverage.lessonsTpl": "{count} in the corpus · ",
|
|
220
|
+
"panel.coverage.unsolvedTpl": "{count} found nothing in 30 days",
|
|
221
|
+
"panel.coverage.totals": "{count} published in all · the topics below are the ones with the most unanswered searches",
|
|
196
222
|
"panel.counters": "this session · counters local to this browser · no account, nothing uploaded but your votes and reports",
|
|
197
223
|
"panel.report.title": "MisakaNet report",
|
|
198
224
|
"panel.report.coveredBy": "covered by ",
|
|
@@ -213,8 +239,7 @@ window.__ModuleLoader__.load({
|
|
|
213
239
|
"panel.trust.used": " · used ",
|
|
214
240
|
"panel.trust.external": "external reuse evidence: ",
|
|
215
241
|
"panel.trust.declared": "declared: ",
|
|
216
|
-
"panel.trust.
|
|
217
|
-
"panel.trust.footnote": "The level is the lesson's own stored evidence. The count is other people's confirmations, not yours: one makes the reuse event appear, the second is what agents read as E4.",
|
|
242
|
+
"panel.trust.footnote": "The level is the lesson's own stored evidence; the count is other people's votes, not yours — recorded as a hint, never as proof. Agents read E4 only from observable reuse, which a vote cannot confer.",
|
|
218
243
|
"panel.activity.session": "this session: ",
|
|
219
244
|
"panel.activity.browser": "on this browser: ",
|
|
220
245
|
"panel.activity.conversion": "conversion",
|
|
@@ -224,7 +249,7 @@ window.__ModuleLoader__.load({
|
|
|
224
249
|
"panel.voice.none": "No cue yet: the server names one on the first search of the session.",
|
|
225
250
|
"panel.voice.play": "Play “",
|
|
226
251
|
"panel.voice.hook": "The local hook is a different thing: it is installed with `--voice` and mutes with MISAKANET_VOICE=0, plays through this machine's own player, and can also raise a desktop notification. A page cannot read or change it, so this panel does not report its state.",
|
|
227
|
-
"panel.footnote": "Every number above comes from this conversation's own transcript rows plus one public GET per lesson (/api/helpful). The trust counts are other people's confirmations, not yours. Reloading the window loses the session rows; the browser counters stay. Receipts shown here are what the agent's own calls returned — a pending report is re-checked when the agent asks again, not by this page.",
|
|
252
|
+
"panel.footnote": "Every number above comes from this conversation's own transcript rows plus one public GET per lesson (/api/helpful), one read of the network's retrieval ranking (/api/analytics) and one read of its coverage board (/api/insights/lesson-coverage). The trust counts are other people's confirmations, not yours. Reloading the window loses the session rows; the browser counters stay. Receipts shown here are what the agent's own calls returned — a pending report is re-checked when the agent asks again, not by this page.",
|
|
228
253
|
"main.footnote": "The left column opens this page; the session-scoped counters live in the session's right-sidebar MisakaNet tab. The numbers here are what this browser has contributed.",
|
|
229
254
|
"tool.rawResult": "raw result",
|
|
230
255
|
"tool.readFailed": "result could not be read",
|
|
@@ -317,6 +342,8 @@ window.__ModuleLoader__.load({
|
|
|
317
342
|
"unit.reports": "条报料",
|
|
318
343
|
"unit.reportFiled": "条报料",
|
|
319
344
|
"unit.reportsFiled": "条报料",
|
|
345
|
+
"unit.retrieval": "次检索",
|
|
346
|
+
"unit.retrievals": "次检索",
|
|
320
347
|
"panel.asked.searching": "检索中……",
|
|
321
348
|
"panel.asked.noMatch": "没有命中任何课程",
|
|
322
349
|
"panel.asked.top": "· 最佳:",
|
|
@@ -329,13 +356,26 @@ window.__ModuleLoader__.load({
|
|
|
329
356
|
"panel.asked.voteHint": "与助手消息旁的裁决行是同一个动作,不用再往回滚。只发这篇课程的 id 和一个固定标签(`dsh-web`),不发别的。",
|
|
330
357
|
"panel.asked.voteFailed": "连不上 misakanet.org —— 这次没有记下任何东西",
|
|
331
358
|
"panel.asked.voteOne": "1 个人工确认 —— 复用事件现在出现了(E3:复用信号,不是 CI 验证)。",
|
|
332
|
-
"panel.asked.voteTwo": "2 个人工确认 ——
|
|
333
|
-
"panel.asked.voteMore": "个人工确认 ——
|
|
359
|
+
"panel.asked.voteTwo": "2 个人工确认 —— 只记为提示,不算证据:E4 那一档来自可观测的复用,投票给不了。",
|
|
360
|
+
"panel.asked.voteMore": "个人工确认 —— 只记为提示,不算证据:E4 那一档来自可观测的复用,投票给不了。",
|
|
334
361
|
"panel.asked.noMatchTag": "无命中",
|
|
335
362
|
"panel.asked.agentNext": "把这行交给 agent:",
|
|
336
363
|
"panel.asked.agentNextCopy": "复制下一步",
|
|
337
364
|
"panel.asked.agentNextText": "请用 MisakaNet 报料记下这个缺口,让下一个人能搜到:misakanet_submit_intake problem: ",
|
|
338
365
|
"panel.asked.agentNextWhy": "这个页面不能替你报:报料的复查方式是原样再提交一次,而服务端的去重窗口是有限的 —— 页面触发的提交可能为同一个问题开出第二个 issue。",
|
|
366
|
+
"panel.hot": "全网返回最多的课程",
|
|
367
|
+
"panel.hot.loading": "正在读全网的检索排行……",
|
|
368
|
+
"panel.hot.none": "统计端点没有排出任何课程 —— 这一节宁可什么都不显示,也不编造一个 0。",
|
|
369
|
+
"panel.hot.rule": "每个数字是这篇课程被检索结果排到首位的次数 —— 来自网络自己的统计端点。它是一个观测到的计数:不是投票,不是认可,也不代表这篇课程真的帮上了忙。",
|
|
370
|
+
"panel.hot.limit": "检索次数最多的 {n} 篇课程 —— 端点返回的更多,这里只列到第 {n} 篇。",
|
|
371
|
+
"panel.hot.noCount": "端点没有给出次数",
|
|
372
|
+
"panel.coverage": "语料还答不上来的主题",
|
|
373
|
+
"panel.coverage.loading": "正在读网络的覆盖看板……",
|
|
374
|
+
"panel.coverage.none": "覆盖看板没有排出任何主题 —— 这一节宁可什么都不显示,也不猜。",
|
|
375
|
+
"panel.coverage.rule": "上面那一节数的是检索带回来了什么;这一节数的是没找到什么。每一行是一个主题,配最近 30 天里没有拿到答案的检索次数(读的是网络自己的覆盖看板),以及语料里匹配该主题的已发布课程数。",
|
|
376
|
+
"panel.coverage.lessonsTpl": "{n} {unit}在库 · ",
|
|
377
|
+
"panel.coverage.unsolvedTpl": "30 天内 {n} {unit}没有答案",
|
|
378
|
+
"panel.coverage.totals": "{n} {unit}已发布 · 下面是没有答案的检索最多的主题",
|
|
339
379
|
"panel.counters": "本会话 · 计数只存在本浏览器 · 没有账号,除了你的投票与报料,不上传任何东西",
|
|
340
380
|
"panel.report.title": "MisakaNet 报料",
|
|
341
381
|
"panel.report.coveredBy": "已被 ",
|
|
@@ -356,8 +396,7 @@ window.__ModuleLoader__.load({
|
|
|
356
396
|
"panel.trust.used": " · 已复用 ",
|
|
357
397
|
"panel.trust.external": "外部复用证据:",
|
|
358
398
|
"panel.trust.declared": "课程自述:",
|
|
359
|
-
"panel.trust.
|
|
360
|
-
"panel.trust.footnote": "等级来自课程自己存的证据;计数是别人的确认,不是你的:前者让复用事件出现,后者才是 agent 读到的 E4。",
|
|
399
|
+
"panel.trust.footnote": "等级来自课程自己存的证据;计数是别人的投票,不是你的 —— 只记为提示,不算证据。agent 读到的 E4 只来自可观测的复用,投票给不了。",
|
|
361
400
|
"panel.activity.session": "本会话:",
|
|
362
401
|
"panel.activity.browser": "本浏览器:",
|
|
363
402
|
"panel.activity.conversion": "转化",
|
|
@@ -367,7 +406,7 @@ window.__ModuleLoader__.load({
|
|
|
367
406
|
"panel.voice.none": "还没有提示音:服务端会在本会话第一次检索时给出。",
|
|
368
407
|
"panel.voice.play": "播放“",
|
|
369
408
|
"panel.voice.hook": "本地钩子是另一回事:用 --voice 安装,用 MISAKANET_VOICE=0 静音,通过本机自己的播放器发声,还能弹桌面通知。页面读不到也改不了它,所以这个面板不报告它的状态。",
|
|
370
|
-
"panel.footnote": "上面的数字都来自本次对话自己的转录,外加每篇课程一次公开 GET(/api/helpful)。可信计数是别人的确认,不是你自己的。重载窗口会丢掉会话行,浏览器计数会留下。这里的回执是 agent 自己调用返回的东西 —— 待处理报料会在 agent 再次提问时复查,而不是这个页面。",
|
|
409
|
+
"panel.footnote": "上面的数字都来自本次对话自己的转录,外加每篇课程一次公开 GET(/api/helpful)、一次对全网检索排行的读取(/api/analytics)和一次对覆盖看板的读取(/api/insights/lesson-coverage)。可信计数是别人的确认,不是你自己的。重载窗口会丢掉会话行,浏览器计数会留下。这里的回执是 agent 自己调用返回的东西 —— 待处理报料会在 agent 再次提问时复查,而不是这个页面。",
|
|
371
410
|
"main.footnote": "左栏打开的就是这一页;会话级的计数在右栏的 MisakaNet 标签里。这里的数字是本浏览器贡献的部分。",
|
|
372
411
|
"tool.rawResult": "原始结果",
|
|
373
412
|
"tool.readFailed": "结果读不出来",
|
|
@@ -651,6 +690,38 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
|
|
|
651
690
|
var OVERLAY_LIMIT = 5;
|
|
652
691
|
var OVERLAY_TIMEOUT_MS = 8000;
|
|
653
692
|
|
|
693
|
+
/**
|
|
694
|
+
* How many rows section ⑥ shows of `/api/analytics`'s `top_lessons`.
|
|
695
|
+
*
|
|
696
|
+
* The owner asked for five and the endpoint returns ten, so the list is capped — and the cap is
|
|
697
|
+
* *printed* by the section (`panel.hot.limit`) rather than applied silently, because a reader who
|
|
698
|
+
* cannot see where a ranking was cut cannot tell it from a ranking that ended.
|
|
699
|
+
*/
|
|
700
|
+
var HOT_LESSON_LIMIT = 5;
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Section ⑦'s target, and the three reads it was chosen over.
|
|
704
|
+
*
|
|
705
|
+
* `/api/insights/lesson-coverage` is the other half of section ⑥'s question, not a second version
|
|
706
|
+
* of it: ⑥ borrows `/api/analytics` to rank *retrievals* — which lessons searches brought back —
|
|
707
|
+
* while this ranks *unmet demand*: `gaps` is ordered by `unsolved30d`, the searches in the last 30
|
|
708
|
+
* days that ended with no usable answer, and each row carries `lessonCount`, the published lessons
|
|
709
|
+
* that do match that topic. A reader who sees both should never think they are two orderings of the
|
|
710
|
+
* same number, which is what `panel.coverage.rule` says out loud.
|
|
711
|
+
*
|
|
712
|
+
* Of the four candidate reads measured live 2026-10-16 (all four answering
|
|
713
|
+
* `access-control-allow-origin: *`, so no server change is needed), this is the only one that both
|
|
714
|
+
* ranks something and says how much knowledge stands behind it. The others: `/api/insights/demand-board`
|
|
715
|
+
* is 146 bytes of family counters with no lesson counts in it; `/api/search-signals/stats` is 705 rows
|
|
716
|
+
* of `{solved, created_at}` — one per search, no lesson id — an unranked event log; `/api/lessons` is
|
|
717
|
+
* the corpus with no retrieval signal in any field, so any "hottest" order over it would be invented
|
|
718
|
+
* here. None of the four exposes a per-lesson retrieval count, which is why ⑥ and ⑦ split the
|
|
719
|
+
* question the way they do: ⑥ has the lessons and no topics, ⑦ has the topics and no individual
|
|
720
|
+
* lessons. The finest grain ⑦ can honestly speak at is the task family.
|
|
721
|
+
*/
|
|
722
|
+
var COVERAGE_ENDPOINT = "https://misakanet.org/api/insights/lesson-coverage";
|
|
723
|
+
var COVERAGE_LIMIT = 5;
|
|
724
|
+
|
|
654
725
|
/**
|
|
655
726
|
* The label every vote from this browser half carries: what cast it, not who.
|
|
656
727
|
*
|
|
@@ -1259,7 +1330,9 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
|
|
|
1259
1330
|
* counters — the review surface, as opposed to the two judging/visibility rows in the transcript.
|
|
1260
1331
|
*
|
|
1261
1332
|
* Every number here has a named source: this session's own log (the rows above), the browser's own
|
|
1262
|
-
* counters, one public `GET /api/helpful` per lesson,
|
|
1333
|
+
* counters, one public `GET /api/helpful` per lesson, one read of the network's retrieval ranking
|
|
1334
|
+
* (`GET /api/analytics`, section ⑥), one read of its coverage board
|
|
1335
|
+
* (`/api/insights/lesson-coverage`, section ⑦), and nothing else. The limits are printed in
|
|
1263
1336
|
* the panel rather than hidden: a reload loses the log, and the local voice hook is not ours to
|
|
1264
1337
|
* read.
|
|
1265
1338
|
*/
|
|
@@ -1278,6 +1351,19 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
|
|
|
1278
1351
|
var voteState = react.useState({});
|
|
1279
1352
|
var panelVotes = voteState[0];
|
|
1280
1353
|
var setPanelVotes = voteState[1];
|
|
1354
|
+
// The retrieval read's own state, and deliberately a third value: `undefined` is "not answered
|
|
1355
|
+
// yet", `null` is "asked and there is no ranking". Collapsing them would make the first paint
|
|
1356
|
+
// say the endpoint ranked nothing, which is a claim the page has not earned yet — and it would
|
|
1357
|
+
// leave no way to tell an empty ranking from one still in flight.
|
|
1358
|
+
var hotState = react.useState(undefined);
|
|
1359
|
+
var hot = hotState[0];
|
|
1360
|
+
var setHot = hotState[1];
|
|
1361
|
+
// Section ⑦'s own state, and the same third value for the same reason: `undefined` is "not
|
|
1362
|
+
// answered yet", `null` is "asked and there is no ranking". Collapsing them would make the first
|
|
1363
|
+
// paint say the board ranked nothing, which is a claim the page has not earned yet.
|
|
1364
|
+
var coverageState = react.useState(undefined);
|
|
1365
|
+
var coverage = coverageState[0];
|
|
1366
|
+
var setCoverage = coverageState[1];
|
|
1281
1367
|
|
|
1282
1368
|
react.useEffect(function () {
|
|
1283
1369
|
return subscribe(sessionId, function () {
|
|
@@ -1334,6 +1420,82 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
|
|
|
1334
1420
|
}
|
|
1335
1421
|
}, [revisionState[0], lessons.length]);
|
|
1336
1422
|
|
|
1423
|
+
// ⑥ the network's most-retrieved lessons, read once per mount.
|
|
1424
|
+
//
|
|
1425
|
+
// The owner asked to see the corpus's hottest lessons without waiting for an agent to surface
|
|
1426
|
+
// one, and the ranking that needs already exists: `GET /api/analytics` — public, `no-store`, and
|
|
1427
|
+
// `access-control-allow-origin: *` (measured 2026-10-16) — carries `top_lessons`, one
|
|
1428
|
+
// `{lesson_id, count}` per lesson, ordered by how often that lesson came back as a search's top
|
|
1429
|
+
// hit.
|
|
1430
|
+
//
|
|
1431
|
+
// **Why this endpoint and not the coverage board** (`/api/insights/lesson-coverage`, the read
|
|
1432
|
+
// PR #3351 adds): the two rank different things and both are honest. The coverage board ranks
|
|
1433
|
+
// *topics* by unmet demand (`unsolved30d`, searches that ended with no usable answer) and says
|
|
1434
|
+
// how many lessons match each topic; it carries no per-lesson retrieval count at all, so a
|
|
1435
|
+
// "hottest lesson" list read from it would be invented here. This endpoint ranks retrievals and
|
|
1436
|
+
// says nothing about whether the lesson helped — a price the section states out loud rather than
|
|
1437
|
+
// dressing the count as a verdict.
|
|
1438
|
+
//
|
|
1439
|
+
// **What is deliberately not rendered: `top_searches`**, the same payload's unfiltered query
|
|
1440
|
+
// list. On 2026-10-16 it led with `pip install timeout` and `Error response from daemon: exit
|
|
1441
|
+
// code 137`, then with `npm run build`, `init` and `start` — generic strings a build tool or a
|
|
1442
|
+
// shell prompt emits, not failure topics. It also carries no lesson link, so it cannot be paired
|
|
1443
|
+
// with the answer that satisfied it. Rendering it as "what people ask" would put noise on the
|
|
1444
|
+
// panel at the same weight as knowledge, so this section shows the ranking the payload really
|
|
1445
|
+
// provides and leaves the query list alone. Nothing here averages it in.
|
|
1446
|
+
//
|
|
1447
|
+
// The read is scheduled, never awaited on the render path: the panel's first paint must not wait
|
|
1448
|
+
// on misakanet.org. `askedHot` is the same once-only guard the per-lesson `GET /api/helpful`
|
|
1449
|
+
// uses, because the effect can re-run and a public endpoint should not be asked twice for one
|
|
1450
|
+
// answer.
|
|
1451
|
+
var askedHot = react.useRef(false);
|
|
1452
|
+
react.useEffect(function () {
|
|
1453
|
+
if (askedHot.current) return;
|
|
1454
|
+
askedHot.current = true;
|
|
1455
|
+
fetch(API + "/api/analytics")
|
|
1456
|
+
.then(function (response) { return response.ok ? response.json() : null; })
|
|
1457
|
+
.then(function (body) {
|
|
1458
|
+
var ranked = body && Array.isArray(body.top_lessons) ? body.top_lessons : null;
|
|
1459
|
+
// An absent or empty `top_lessons` is "no ranking", never a zero: a manufactured `0`
|
|
1460
|
+
// would name a lesson the endpoint did not rank.
|
|
1461
|
+
setHot(ranked && ranked.length ? ranked : null);
|
|
1462
|
+
})
|
|
1463
|
+
.catch(function () { setHot(null); });
|
|
1464
|
+
}, []);
|
|
1465
|
+
|
|
1466
|
+
// ⑦ what the corpus cannot answer yet, read once per mount.
|
|
1467
|
+
//
|
|
1468
|
+
// The complement of ⑥, and deliberately not a re-sort of it. ⑥ ranks *lessons by retrievals*
|
|
1469
|
+
// (`/api/analytics`'s `top_lessons`); this ranks *topics by unanswered searches*
|
|
1470
|
+
// (`/api/insights/lesson-coverage`'s `gaps`, `unsolved30d` descending) and prints how many
|
|
1471
|
+
// published lessons match each topic. Two different measurements of two different things: what
|
|
1472
|
+
// the corpus returns most, and where it has nothing to return. `panel.coverage.rule` states the
|
|
1473
|
+
// difference where the reader meets it, because a panel with two rankings that never says how
|
|
1474
|
+
// they differ invites the reader to assume one is a re-sort of the other.
|
|
1475
|
+
//
|
|
1476
|
+
// Why this endpoint rather than `/api/analytics`: the retrieval ranking carries no topic and no
|
|
1477
|
+
// gap count, and none of the four reads measured on 2026-10-16 carries a per-lesson retrieval
|
|
1478
|
+
// count *and* a topic. `gaps` is the only public ranking of unmet demand, and `lessonCount` is
|
|
1479
|
+
// the only public statement of how much knowledge stands behind each topic.
|
|
1480
|
+
//
|
|
1481
|
+
// Scheduled, never awaited on the render path — the first paint must not wait on misakanet.org —
|
|
1482
|
+
// and guarded by a ref for the same reason as ⑥: the effect can re-run and a public endpoint
|
|
1483
|
+
// should not be asked twice for one answer.
|
|
1484
|
+
var askedCoverage = react.useRef(false);
|
|
1485
|
+
react.useEffect(function () {
|
|
1486
|
+
if (askedCoverage.current) return;
|
|
1487
|
+
askedCoverage.current = true;
|
|
1488
|
+
fetch(COVERAGE_ENDPOINT)
|
|
1489
|
+
.then(function (response) { return response.ok ? response.json() : null; })
|
|
1490
|
+
.then(function (body) {
|
|
1491
|
+
var ranked = body && body.success && Array.isArray(body.gaps) ? body.gaps : null;
|
|
1492
|
+
// An absent or empty `gaps` is "no ranking", never a list of zeroes: the totals line
|
|
1493
|
+
// would otherwise describe a corpus standing behind a ranking that does not exist.
|
|
1494
|
+
setCoverage(ranked && ranked.length ? body : null);
|
|
1495
|
+
})
|
|
1496
|
+
.catch(function () { setCoverage(null); });
|
|
1497
|
+
}, []);
|
|
1498
|
+
|
|
1337
1499
|
var rows = [];
|
|
1338
1500
|
// The same mark as the plugin-list icon and the sidebar chip: the conversation ring has no icon
|
|
1339
1501
|
// channel, so the panel body is the one place the tab can show what it belongs to.
|
|
@@ -1543,12 +1705,15 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
|
|
|
1543
1705
|
// richer receipt (per-event types, `count`/`queries`/`sources`, its own `evidence`) but it is an
|
|
1544
1706
|
// MCP tool, reachable only through the MCP endpoint with a JSON-RPC `tools/call` — and this
|
|
1545
1707
|
// browser half is forbidden from making one (acceptance D4: the same endpoint files issues). The
|
|
1546
|
-
// count therefore carries the helpful-vote signal, and the
|
|
1547
|
-
//
|
|
1708
|
+
// count therefore carries the helpful-vote signal, and the observed distinct-client retrieval,
|
|
1709
|
+
// the regression citations and the declared-provenance hint the tool also aggregates are **not**
|
|
1710
|
+
// rendered. Nothing here is guessed: no field
|
|
1548
1711
|
// this endpoint does not return is displayed.
|
|
1549
1712
|
//
|
|
1550
|
-
// The count
|
|
1551
|
-
//
|
|
1713
|
+
// The count is the helpful-vote signal only, and since 2026-10-16 a vote is capped below E4 at
|
|
1714
|
+
// every count — no row here can claim the rung, which is also why the rule below is stated once.
|
|
1715
|
+
// (`E4` itself is observed elsewhere: `misakanet_me_events` counts distinct clients from
|
|
1716
|
+
// `search_signals`, which this browser half likewise cannot read.)
|
|
1552
1717
|
rows.push(h("div", { key: "s3", style: { marginTop: "12px", fontWeight: 600 } }, T("panel.trust")));
|
|
1553
1718
|
if (!lessons.length) {
|
|
1554
1719
|
rows.push(entryPointHint("s3e", "panel.trust.none"));
|
|
@@ -1561,10 +1726,10 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
|
|
|
1561
1726
|
var confirmations = trust[lesson.lessonId];
|
|
1562
1727
|
var counted = confirmations === undefined ? T("panel.trust.checking")
|
|
1563
1728
|
: (confirmations === null ? T("panel.trust.unavailable")
|
|
1564
|
-
: count(confirmations, T("panel.trust.human"))
|
|
1729
|
+
: count(confirmations, T("panel.trust.human")));
|
|
1565
1730
|
var declared = lesson.evidence || T("panel.trust.noLevel");
|
|
1566
1731
|
var external = h("span", { style: DIM },
|
|
1567
|
-
T("panel.trust.external") + counted
|
|
1732
|
+
T("panel.trust.external") + counted);
|
|
1568
1733
|
return h("div", { key: "l" + lesson.lessonId, style: { borderTop: "1px solid " + MUTED, padding: "4px 0" } },
|
|
1569
1734
|
h("div", null,
|
|
1570
1735
|
h("span", {}, lesson.title || lesson.lessonId),
|
|
@@ -1611,6 +1776,76 @@ var DEFAULT_MCP_URL = "https://misakanet.org/mcp";
|
|
|
1611
1776
|
: T("panel.voice.none")),
|
|
1612
1777
|
h("div", { style: DIM }, T("panel.voice.hook"))));
|
|
1613
1778
|
|
|
1779
|
+
// ⑥ the network's most-retrieved lessons
|
|
1780
|
+
//
|
|
1781
|
+
// Read-only, and about the whole network rather than this session: no lesson here had to be
|
|
1782
|
+
// surfaced by the agent first. There is no vote affordance in this section and none of its copy
|
|
1783
|
+
// says the count is approval — the number is an observed retrieval, and the rule states what it
|
|
1784
|
+
// does and does not mean before the first row.
|
|
1785
|
+
rows.push(h("div", { key: "s6", style: { marginTop: "12px", fontWeight: 600 } }, T("panel.hot")));
|
|
1786
|
+
if (hot === undefined) {
|
|
1787
|
+
rows.push(h("div", { key: "s6w", style: DIM }, T("panel.hot.loading")));
|
|
1788
|
+
} else if (hot === null) {
|
|
1789
|
+
// The endpoint ranked nothing (or did not answer). Saying so beats printing five rows of
|
|
1790
|
+
// `0 retrievals`, which would name lessons the payload never ranked.
|
|
1791
|
+
rows.push(h("div", { key: "s6e", style: DIM }, T("panel.hot.none")));
|
|
1792
|
+
} else {
|
|
1793
|
+
rows.push(h("div", { key: "s6rule", style: DIM }, T("panel.hot.rule")));
|
|
1794
|
+
rows.push(h("div", { key: "s6l", style: DIM }, T("panel.hot.limit", { n: HOT_LESSON_LIMIT })));
|
|
1795
|
+
rows.push(h("div", { key: "s6r" }, hot.slice(0, HOT_LESSON_LIMIT).map(function (entry) {
|
|
1796
|
+
var path = String(entry && entry.lesson_id ? entry.lesson_id : "");
|
|
1797
|
+
var times = entry && typeof entry.count === "number" ? entry.count : null;
|
|
1798
|
+
return h("div", { key: "h" + path, style: { borderTop: "1px solid " + MUTED, padding: "4px 0" } },
|
|
1799
|
+
h("span", { style: { fontFamily: "monospace" } }, path),
|
|
1800
|
+
h("span", { style: DIM }, " · " + (times === null
|
|
1801
|
+
? T("panel.hot.noCount")
|
|
1802
|
+
: count(times, T("unit.retrieval"), T("unit.retrievals")))));
|
|
1803
|
+
})));
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
// ⑦ the topics the corpus cannot answer yet
|
|
1807
|
+
//
|
|
1808
|
+
// Why ⑦ and not ⑥: `main` already numbers the retrieval ranking ⑥ (and owns `key: "s6"` plus the
|
|
1809
|
+
// `panel.hot` keys), so this section takes the next number and the `s7` key rather than sharing
|
|
1810
|
+
// one — two sections with the same React key is a duplicate-key defect, and two sections that
|
|
1811
|
+
// both claim to be ⑥ make the rule lines unreadable. Every key here is `s7*`, and the dictionary
|
|
1812
|
+
// family is `panel.coverage*`, so neither half of that identity is borrowed.
|
|
1813
|
+
//
|
|
1814
|
+
// Read-only, and about the whole network rather than this session: no topic here had to be
|
|
1815
|
+
// searched first. There is no vote affordance in this section and none of its copy says the order
|
|
1816
|
+
// is approval — `unsolved30d` counts unanswered searches, and the rule says what it counts before
|
|
1817
|
+
// the first row and how it differs from the retrieval ranking above.
|
|
1818
|
+
rows.push(h("div", { key: "s7", style: { marginTop: "12px", fontWeight: 600 } }, T("panel.coverage")));
|
|
1819
|
+
if (coverage === undefined) {
|
|
1820
|
+
rows.push(h("div", { key: "s7w", style: DIM }, T("panel.coverage.loading")));
|
|
1821
|
+
} else if (coverage === null) {
|
|
1822
|
+
// The board ranked nothing (or did not answer). Saying so beats printing rows of `0`, which
|
|
1823
|
+
// would name topics the payload never ranked.
|
|
1824
|
+
rows.push(h("div", { key: "s7e", style: DIM }, T("panel.coverage.none")));
|
|
1825
|
+
} else {
|
|
1826
|
+
rows.push(h("div", { key: "s7rule", style: DIM }, T("panel.coverage.rule")));
|
|
1827
|
+
var coverageMetrics = coverage.metrics || {};
|
|
1828
|
+
if (typeof coverageMetrics.publishedLessons === "number") {
|
|
1829
|
+
rows.push(h("div", { key: "s7t", style: DIM },
|
|
1830
|
+
T("panel.coverage.totals", {
|
|
1831
|
+
count: count(coverageMetrics.publishedLessons, T("unit.lesson"), T("unit.lessons")),
|
|
1832
|
+
n: coverageMetrics.publishedLessons, unit: T("unit.lessons"),
|
|
1833
|
+
})));
|
|
1834
|
+
}
|
|
1835
|
+
rows.push(h("div", { key: "s7r" }, coverage.gaps.slice(0, COVERAGE_LIMIT).map(function (gap) {
|
|
1836
|
+
return h("div", { key: "f" + gap.taskFamily, style: { borderTop: "1px solid " + MUTED, padding: "4px 0" } },
|
|
1837
|
+
h("span", {}, gap.taskFamily),
|
|
1838
|
+
h("span", { style: DIM }, " " + T("panel.coverage.lessonsTpl", {
|
|
1839
|
+
count: count(gap.lessonCount, T("unit.lesson"), T("unit.lessons")),
|
|
1840
|
+
n: gap.lessonCount, unit: T("unit.lessons"),
|
|
1841
|
+
})),
|
|
1842
|
+
h("span", { style: DIM }, T("panel.coverage.unsolvedTpl", {
|
|
1843
|
+
count: count(gap.unsolved30d, T("unit.search"), T("unit.searches")),
|
|
1844
|
+
n: gap.unsolved30d, unit: T("unit.searches"),
|
|
1845
|
+
})));
|
|
1846
|
+
})));
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1614
1849
|
rows.push(h("div", { key: "foot", style: Object.assign({}, DIM, { marginTop: "12px" }) },
|
|
1615
1850
|
T("panel.footnote")));
|
|
1616
1851
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "misakanet",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.46.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "MisakaNet failure-memory skill — evidence-rated failure lessons for AI agents. DSH bundle: registers the MCP tools as mcp__misakanet__* (misakanet_search/get_lesson/…), served by the public Streamable HTTP endpoint https://misakanet.org/mcp — live tools from an npm install, in any profile, with no local python. A profile that prefers the repo's own stdio server can override the bundle row.",
|
|
6
6
|
"icon": "icon.svg",
|
|
@@ -106,13 +106,17 @@ misakanet_preflight(...)
|
|
|
106
106
|
|
|
107
107
|
Checks risk level before executing a high-risk operation.
|
|
108
108
|
|
|
109
|
-
### Lesson reuse evidence
|
|
109
|
+
### Lesson reuse evidence
|
|
110
110
|
|
|
111
111
|
```
|
|
112
112
|
misakanet_me_events(lesson_id="some-lesson")
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
-
Returns evidence of a lesson being reused
|
|
115
|
+
Returns evidence of a lesson being reused. `E4` is **observed** reuse: two or more distinct clients
|
|
116
|
+
actually retrieved the lesson (`search_signals.client_hint`) — a count of requests, not of prose.
|
|
117
|
+
Regression-benchmark citations, helpful votes, and a lesson's own `verified by:` line are `E3`: a
|
|
118
|
+
citation is sandbox-level evidence, a vote is another person's opinion, and a provenance line is the
|
|
119
|
+
lesson talking about itself, so none of those hints reaches `E4`.
|
|
116
120
|
|
|
117
121
|
## Examples
|
|
118
122
|
|
|
@@ -185,7 +189,8 @@ Run this roughly every 10 sessions (or weekly):
|
|
|
185
189
|
(`/api/insights/unsolved-map` or the demand board); pick one gap you can
|
|
186
190
|
actually cover and write the lesson.
|
|
187
191
|
5. **Verify your own usage.** Confirm your past submissions were merged and
|
|
188
|
-
are being reused (`me_events`
|
|
192
|
+
are being reused (`me_events` observes distinct-client retrieval and citations; a helpful
|
|
193
|
+
vote is only a hint). If a
|
|
189
194
|
lesson of yours is never cited, its problem statement may not match real
|
|
190
195
|
queries — tighten the title and triggers.
|
|
191
196
|
|
|
@@ -199,7 +204,7 @@ Trigger a contribution automatically when any of these happen:
|
|
|
199
204
|
|---|---|
|
|
200
205
|
| A bug is fixed and CI passes | Queue the fix as a lesson draft (`queue_lesson.py`) |
|
|
201
206
|
| An existing lesson matched but did NOT fix the problem | Submit not-helpful feedback AND intake with `matched_lesson_id` |
|
|
202
|
-
| A lesson solved your problem | Record a helpful/usage report —
|
|
207
|
+
| A lesson solved your problem | Record a helpful/usage report — a hint for the next reader; `E4` itself comes from observed reuse, not a vote |
|
|
203
208
|
| A crash/tombstone is captured | Convert it to a draft lesson (`tombstone_to_draft.py`) |
|
|
204
209
|
| A fix took longer than ~15 minutes and no lesson matched | You just earned the lesson — submit it before context is lost |
|
|
205
210
|
| A lesson's evidence_level is below what you need | Contribute a reproduction/verification and request promotion |
|