scientify 1.13.5 → 2.0.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.
Files changed (100) hide show
  1. package/README.en.md +350 -0
  2. package/README.md +148 -358
  3. package/dist/index.d.ts +8 -2
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +131 -122
  6. package/dist/index.js.map +1 -1
  7. package/dist/src/cli/research.d.ts +1 -6
  8. package/dist/src/cli/research.d.ts.map +1 -1
  9. package/dist/src/cli/research.js +227 -123
  10. package/dist/src/cli/research.js.map +1 -1
  11. package/dist/src/commands/metabolism-status.d.ts +3 -3
  12. package/dist/src/commands/metabolism-status.d.ts.map +1 -1
  13. package/dist/src/commands/metabolism-status.js +72 -75
  14. package/dist/src/commands/metabolism-status.js.map +1 -1
  15. package/dist/src/commands.d.ts +1 -1
  16. package/dist/src/commands.d.ts.map +1 -1
  17. package/dist/src/commands.js +0 -55
  18. package/dist/src/commands.js.map +1 -1
  19. package/dist/src/hooks/cron-skill-inject.d.ts +6 -7
  20. package/dist/src/hooks/cron-skill-inject.d.ts.map +1 -1
  21. package/dist/src/hooks/cron-skill-inject.js +6 -15
  22. package/dist/src/hooks/cron-skill-inject.js.map +1 -1
  23. package/dist/src/hooks/research-mode.d.ts +1 -1
  24. package/dist/src/hooks/research-mode.d.ts.map +1 -1
  25. package/dist/src/hooks/research-mode.js +24 -72
  26. package/dist/src/hooks/research-mode.js.map +1 -1
  27. package/dist/src/hooks/scientify-signature.d.ts +1 -1
  28. package/dist/src/hooks/scientify-signature.d.ts.map +1 -1
  29. package/dist/src/hooks/scientify-signature.js +2 -5
  30. package/dist/src/hooks/scientify-signature.js.map +1 -1
  31. package/dist/src/knowledge-state/render.d.ts +1 -9
  32. package/dist/src/knowledge-state/render.d.ts.map +1 -1
  33. package/dist/src/knowledge-state/render.js +33 -158
  34. package/dist/src/knowledge-state/render.js.map +1 -1
  35. package/dist/src/knowledge-state/store.d.ts.map +1 -1
  36. package/dist/src/knowledge-state/store.js +65 -884
  37. package/dist/src/knowledge-state/store.js.map +1 -1
  38. package/dist/src/knowledge-state/types.d.ts +0 -69
  39. package/dist/src/knowledge-state/types.d.ts.map +1 -1
  40. package/dist/src/literature/subscription-state.d.ts +0 -2
  41. package/dist/src/literature/subscription-state.d.ts.map +1 -1
  42. package/dist/src/literature/subscription-state.js +7 -1199
  43. package/dist/src/literature/subscription-state.js.map +1 -1
  44. package/dist/src/research-subscriptions/constants.d.ts +1 -1
  45. package/dist/src/research-subscriptions/constants.js +1 -1
  46. package/dist/src/research-subscriptions/cron-client.d.ts +1 -1
  47. package/dist/src/research-subscriptions/cron-client.d.ts.map +1 -1
  48. package/dist/src/research-subscriptions/delivery.d.ts +1 -1
  49. package/dist/src/research-subscriptions/delivery.d.ts.map +1 -1
  50. package/dist/src/research-subscriptions/handlers.d.ts +1 -1
  51. package/dist/src/research-subscriptions/handlers.d.ts.map +1 -1
  52. package/dist/src/research-subscriptions/handlers.js +9 -9
  53. package/dist/src/research-subscriptions/handlers.js.map +1 -1
  54. package/dist/src/research-subscriptions/parse.d.ts.map +1 -1
  55. package/dist/src/research-subscriptions/parse.js +0 -10
  56. package/dist/src/research-subscriptions/parse.js.map +1 -1
  57. package/dist/src/research-subscriptions/prompt.d.ts +1 -1
  58. package/dist/src/research-subscriptions/prompt.d.ts.map +1 -1
  59. package/dist/src/research-subscriptions/prompt.js +196 -191
  60. package/dist/src/research-subscriptions/prompt.js.map +1 -1
  61. package/dist/src/research-subscriptions/types.d.ts +1 -2
  62. package/dist/src/research-subscriptions/types.d.ts.map +1 -1
  63. package/dist/src/templates/bootstrap.d.ts.map +1 -1
  64. package/dist/src/templates/bootstrap.js +32 -19
  65. package/dist/src/templates/bootstrap.js.map +1 -1
  66. package/dist/src/tools/arxiv-download.d.ts +1 -2
  67. package/dist/src/tools/arxiv-download.d.ts.map +1 -1
  68. package/dist/src/tools/arxiv-search.d.ts +1 -2
  69. package/dist/src/tools/arxiv-search.d.ts.map +1 -1
  70. package/dist/src/tools/github-search-tool.d.ts +1 -2
  71. package/dist/src/tools/github-search-tool.d.ts.map +1 -1
  72. package/dist/src/tools/openalex-search.d.ts +1 -2
  73. package/dist/src/tools/openalex-search.d.ts.map +1 -1
  74. package/dist/src/tools/openreview-lookup.d.ts +1 -2
  75. package/dist/src/tools/openreview-lookup.d.ts.map +1 -1
  76. package/dist/src/tools/paper-browser.d.ts +1 -2
  77. package/dist/src/tools/paper-browser.d.ts.map +1 -1
  78. package/dist/src/tools/result.d.ts +3 -5
  79. package/dist/src/tools/result.d.ts.map +1 -1
  80. package/dist/src/tools/result.js +5 -7
  81. package/dist/src/tools/result.js.map +1 -1
  82. package/dist/src/tools/scientify-cron.d.ts +4 -9
  83. package/dist/src/tools/scientify-cron.d.ts.map +1 -1
  84. package/dist/src/tools/scientify-cron.js +19 -441
  85. package/dist/src/tools/scientify-cron.js.map +1 -1
  86. package/dist/src/tools/scientify-literature-state.d.ts +1 -62
  87. package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
  88. package/dist/src/tools/scientify-literature-state.js +46 -312
  89. package/dist/src/tools/scientify-literature-state.js.map +1 -1
  90. package/dist/src/tools/unpaywall-download.d.ts +1 -2
  91. package/dist/src/tools/unpaywall-download.d.ts.map +1 -1
  92. package/dist/src/types.d.ts +16 -0
  93. package/dist/src/types.d.ts.map +1 -0
  94. package/dist/src/types.js +2 -0
  95. package/dist/src/types.js.map +1 -0
  96. package/openclaw.plugin.json +4 -2
  97. package/package.json +1 -1
  98. package/skills/metabolism/SKILL.md +2 -0
  99. package/skills/research-subscription/SKILL.md +1 -29
  100. package/README.zh.md +0 -494
@@ -2,49 +2,20 @@ import { createHash } from "node:crypto";
2
2
  import { existsSync } from "node:fs";
3
3
  import { appendFile, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
- import { dayKeyFromTimestamp, renderDailyChangesMarkdown, renderExplorationLogMarkdown, renderHypothesisMarkdown, renderIngestLogMarkdown, renderKnowledgeIndexMarkdown, renderPaperNoteHeaderMarkdown, renderPaperNoteRunMarkdown, renderReflectionLogMarkdown, renderTopicUpdateMarkdown, slugifyTopic, } from "./render.js";
5
+ import { dayKeyFromTimestamp, renderDailyChangesMarkdown, renderExplorationLogMarkdown, renderHypothesisMarkdown, renderIngestLogMarkdown, renderKnowledgeIndexMarkdown, renderPaperNoteHeaderMarkdown, renderPaperNoteRunMarkdown, renderTopicUpdateMarkdown, slugifyTopic, } from "./render.js";
6
6
  import { resolveProjectContext } from "./project.js";
7
7
  const STATE_VERSION = 1;
8
8
  const MAX_RECENT_RUN_IDS = 200;
9
9
  const MAX_RECENT_HYPOTHESES = 50;
10
10
  const MAX_RECENT_CHANGE_STATS = 30;
11
11
  const MAX_LAST_TRACE = 20;
12
- const MAX_LAST_REFLECTION_TASKS = 20;
13
12
  const MAX_RECENT_PAPERS = 50;
14
13
  const MAX_PAPER_NOTES = 800;
15
- const MAX_HYPOTHESIS_REJECTION_REASONS = 24;
16
14
  const MIN_CORE_FULLTEXT_COVERAGE = 0.8;
17
15
  const MIN_EVIDENCE_BINDING_RATE = 0.9;
18
16
  const MAX_CITATION_ERROR_RATE = 0.02;
19
- const FATAL_CITATION_ERROR_RATE = 0.2;
20
- const MIN_FULLTEXT_PROFILE_COMPLETENESS = 0.55;
21
- const MIN_HYPOTHESIS_EVIDENCE = 2;
22
- const MIN_HYPOTHESIS_DEPENDENCY_STEPS = 2;
23
- const MIN_HYPOTHESIS_STATEMENT_CHARS = 48;
24
- const MIN_HYPOTHESIS_STRENGTHS = 2;
25
- const MIN_HYPOTHESIS_WEAKNESSES = 2;
26
- const MIN_HYPOTHESIS_PLAN_STEPS = 3;
27
- const MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE = 70;
28
- const MIN_HYPOTHESIS_ACCEPT_SELF_SCORE_AVG = 3.2;
29
- const PLACEHOLDER_TEXT_RE = /^(?:n\/a|na|none|not provided|not available|unknown|tbd|todo|null|nil|未提供|暂无|未知|无)$/iu;
30
- function defaultRunProfile() {
31
- return "strict";
32
- }
33
- function defaultTriggerState(nowMs = Date.now()) {
34
- return {
35
- consecutiveNewReviseDays: 0,
36
- bridgeCount7d: 0,
37
- unreadCoreBacklog: 0,
38
- lastUpdatedAtMs: nowMs,
39
- };
40
- }
41
17
  function defaultQualityGateState() {
42
18
  return {
43
- mode: "soft",
44
- severity: "warn",
45
- warnings: ["quality gate not evaluated"],
46
- fatalReasons: [],
47
- blocking: false,
48
19
  passed: false,
49
20
  fullTextCoveragePct: 0,
50
21
  evidenceBindingRatePct: 0,
@@ -52,26 +23,9 @@ function defaultQualityGateState() {
52
23
  reasons: ["quality gate not evaluated"],
53
24
  };
54
25
  }
55
- function defaultHypothesisGateState() {
56
- return {
57
- accepted: 0,
58
- rejected: 0,
59
- rejectionReasons: [],
60
- };
61
- }
62
26
  function normalizeText(raw) {
63
27
  return raw.trim().replace(/\s+/g, " ");
64
28
  }
65
- function cleanOptionalText(raw) {
66
- if (typeof raw !== "string")
67
- return undefined;
68
- const normalized = normalizeText(raw);
69
- if (!normalized)
70
- return undefined;
71
- if (PLACEHOLDER_TEXT_RE.test(normalized))
72
- return undefined;
73
- return normalized;
74
- }
75
29
  function sanitizeId(raw) {
76
30
  return normalizeText(raw)
77
31
  .toLowerCase()
@@ -152,9 +106,6 @@ async function loadState(projectPath) {
152
106
  topic: normalizeText(rawStream.topic ?? "topic"),
153
107
  topicKey,
154
108
  projectId: sanitizeId(rawStream.projectId ?? "auto-topic-global-000000") || "auto-topic-global-000000",
155
- lastRunProfile: rawStream.lastRunProfile === "strict" || rawStream.lastRunProfile === "fast"
156
- ? rawStream.lastRunProfile
157
- : defaultRunProfile(),
158
109
  totalRuns: typeof rawStream.totalRuns === "number" ? Math.max(0, Math.floor(rawStream.totalRuns)) : 0,
159
110
  totalHypotheses: typeof rawStream.totalHypotheses === "number" ? Math.max(0, Math.floor(rawStream.totalHypotheses)) : 0,
160
111
  knowledgeTopics: Array.isArray(rawStream.knowledgeTopics)
@@ -163,26 +114,6 @@ async function loadState(projectPath) {
163
114
  paperNotes: Array.isArray(rawStream.paperNotes)
164
115
  ? rawStream.paperNotes.filter((item) => typeof item === "string").map((item) => normalizeText(item))
165
116
  : [],
166
- triggerState: rawStream.triggerState && typeof rawStream.triggerState === "object" && !Array.isArray(rawStream.triggerState)
167
- ? {
168
- consecutiveNewReviseDays: typeof rawStream.triggerState.consecutiveNewReviseDays === "number" &&
169
- Number.isFinite(rawStream.triggerState.consecutiveNewReviseDays)
170
- ? Math.max(0, Math.floor(rawStream.triggerState.consecutiveNewReviseDays))
171
- : 0,
172
- bridgeCount7d: typeof rawStream.triggerState.bridgeCount7d === "number" &&
173
- Number.isFinite(rawStream.triggerState.bridgeCount7d)
174
- ? Math.max(0, Math.floor(rawStream.triggerState.bridgeCount7d))
175
- : 0,
176
- unreadCoreBacklog: typeof rawStream.triggerState.unreadCoreBacklog === "number" &&
177
- Number.isFinite(rawStream.triggerState.unreadCoreBacklog)
178
- ? Math.max(0, Math.floor(rawStream.triggerState.unreadCoreBacklog))
179
- : 0,
180
- lastUpdatedAtMs: typeof rawStream.triggerState.lastUpdatedAtMs === "number" &&
181
- Number.isFinite(rawStream.triggerState.lastUpdatedAtMs)
182
- ? Math.floor(rawStream.triggerState.lastUpdatedAtMs)
183
- : Date.now(),
184
- }
185
- : defaultTriggerState(),
186
117
  recentFullTextReadCount: typeof rawStream.recentFullTextReadCount === "number"
187
118
  ? Math.max(0, Math.floor(rawStream.recentFullTextReadCount))
188
119
  : 0,
@@ -192,60 +123,27 @@ async function loadState(projectPath) {
192
123
  lastQualityGate: rawStream.lastQualityGate &&
193
124
  typeof rawStream.lastQualityGate === "object" &&
194
125
  !Array.isArray(rawStream.lastQualityGate)
195
- ? (() => {
196
- const reasons = Array.isArray(rawStream.lastQualityGate.reasons)
126
+ ? {
127
+ passed: rawStream.lastQualityGate.passed === true,
128
+ fullTextCoveragePct: typeof rawStream.lastQualityGate.fullTextCoveragePct === "number" &&
129
+ Number.isFinite(rawStream.lastQualityGate.fullTextCoveragePct)
130
+ ? Number(rawStream.lastQualityGate.fullTextCoveragePct.toFixed(2))
131
+ : 0,
132
+ evidenceBindingRatePct: typeof rawStream.lastQualityGate.evidenceBindingRatePct === "number" &&
133
+ Number.isFinite(rawStream.lastQualityGate.evidenceBindingRatePct)
134
+ ? Number(rawStream.lastQualityGate.evidenceBindingRatePct.toFixed(2))
135
+ : 0,
136
+ citationErrorRatePct: typeof rawStream.lastQualityGate.citationErrorRatePct === "number" &&
137
+ Number.isFinite(rawStream.lastQualityGate.citationErrorRatePct)
138
+ ? Number(rawStream.lastQualityGate.citationErrorRatePct.toFixed(2))
139
+ : 0,
140
+ reasons: Array.isArray(rawStream.lastQualityGate.reasons)
197
141
  ? rawStream.lastQualityGate.reasons
198
142
  .filter((item) => typeof item === "string")
199
143
  .map((item) => normalizeText(item))
200
144
  .filter((item) => item.length > 0)
201
- : [];
202
- const warnings = Array.isArray(rawStream.lastQualityGate.warnings)
203
- ? rawStream.lastQualityGate.warnings
204
- .filter((item) => typeof item === "string")
205
- .map((item) => normalizeText(item))
206
- .filter((item) => item.length > 0)
207
- : reasons;
208
- const fatalReasons = Array.isArray(rawStream.lastQualityGate.fatalReasons)
209
- ? rawStream.lastQualityGate.fatalReasons
210
- .filter((item) => typeof item === "string")
211
- .map((item) => normalizeText(item))
212
- .filter((item) => item.length > 0)
213
- : [];
214
- const blocking = rawStream.lastQualityGate.blocking === true || fatalReasons.length > 0;
215
- const severityRaw = typeof rawStream.lastQualityGate.severity === "string"
216
- ? rawStream.lastQualityGate.severity.toLowerCase()
217
- : undefined;
218
- const severity = severityRaw === "fatal" || blocking
219
- ? "fatal"
220
- : severityRaw === "ok"
221
- ? "ok"
222
- : warnings.length > 0
223
- ? "warn"
224
- : "ok";
225
- return {
226
- mode: "soft",
227
- severity,
228
- warnings,
229
- fatalReasons,
230
- blocking,
231
- passed: typeof rawStream.lastQualityGate.passed === "boolean"
232
- ? rawStream.lastQualityGate.passed
233
- : fatalReasons.length === 0,
234
- fullTextCoveragePct: typeof rawStream.lastQualityGate.fullTextCoveragePct === "number" &&
235
- Number.isFinite(rawStream.lastQualityGate.fullTextCoveragePct)
236
- ? Number(rawStream.lastQualityGate.fullTextCoveragePct.toFixed(2))
237
- : 0,
238
- evidenceBindingRatePct: typeof rawStream.lastQualityGate.evidenceBindingRatePct === "number" &&
239
- Number.isFinite(rawStream.lastQualityGate.evidenceBindingRatePct)
240
- ? Number(rawStream.lastQualityGate.evidenceBindingRatePct.toFixed(2))
241
- : 0,
242
- citationErrorRatePct: typeof rawStream.lastQualityGate.citationErrorRatePct === "number" &&
243
- Number.isFinite(rawStream.lastQualityGate.citationErrorRatePct)
244
- ? Number(rawStream.lastQualityGate.citationErrorRatePct.toFixed(2))
245
- : 0,
246
- reasons: reasons.length > 0 ? reasons : [...warnings, ...fatalReasons],
247
- };
248
- })()
145
+ : [],
146
+ }
249
147
  : defaultQualityGateState(),
250
148
  lastUnreadCorePaperIds: Array.isArray(rawStream.lastUnreadCorePaperIds)
251
149
  ? rawStream.lastUnreadCorePaperIds
@@ -269,23 +167,7 @@ async function loadState(projectPath) {
269
167
  .map((item) => normalizeText(item))
270
168
  : [],
271
169
  recentHypotheses: Array.isArray(rawStream.recentHypotheses)
272
- ? rawStream.recentHypotheses.filter((item) => !!item && typeof item === "object").map((item) => ({
273
- id: sanitizeId(item.id ?? "hyp"),
274
- statement: normalizeText(item.statement ?? ""),
275
- trigger: ["GAP", "BRIDGE", "TREND", "CONTRADICTION"].includes(item.trigger)
276
- ? item.trigger
277
- : "TREND",
278
- createdAtMs: typeof item.createdAtMs === "number" && Number.isFinite(item.createdAtMs)
279
- ? item.createdAtMs
280
- : Date.now(),
281
- file: normalizeText(item.file ?? ""),
282
- ...(typeof item.strictOverallScore === "number" && Number.isFinite(item.strictOverallScore)
283
- ? { strictOverallScore: Number(item.strictOverallScore.toFixed(2)) }
284
- : {}),
285
- ...(item.strictDecision === "accept" || item.strictDecision === "revise" || item.strictDecision === "reject"
286
- ? { strictDecision: item.strictDecision }
287
- : {}),
288
- })).filter((item) => item.statement.length > 0 && item.file.length > 0)
170
+ ? rawStream.recentHypotheses.filter((item) => !!item && typeof item === "object")
289
171
  : [],
290
172
  recentChangeStats: Array.isArray(rawStream.recentChangeStats)
291
173
  ? rawStream.recentChangeStats.filter((item) => !!item && typeof item === "object")
@@ -296,39 +178,6 @@ async function loadState(projectPath) {
296
178
  .map(normalizeTrace)
297
179
  .filter((item) => Boolean(item))
298
180
  : [],
299
- lastReflectionTasks: Array.isArray(rawStream.lastReflectionTasks)
300
- ? rawStream.lastReflectionTasks
301
- .filter((item) => !!item && typeof item === "object")
302
- .map((item) => ({
303
- id: sanitizeId(item.id ?? "task"),
304
- trigger: ["BRIDGE", "TREND", "CONTRADICTION", "UNREAD_CORE"].includes(item.trigger)
305
- ? item.trigger
306
- : "TREND",
307
- reason: normalizeText(item.reason ?? ""),
308
- query: normalizeText(item.query ?? ""),
309
- priority: ["high", "medium", "low"].includes(item.priority) ? item.priority : "medium",
310
- status: (item.status === "executed" ? "executed" : "planned"),
311
- }))
312
- .filter((item) => item.reason.length > 0 && item.query.length > 0)
313
- : [],
314
- lastHypothesisGate: rawStream.lastHypothesisGate &&
315
- typeof rawStream.lastHypothesisGate === "object" &&
316
- !Array.isArray(rawStream.lastHypothesisGate)
317
- ? {
318
- accepted: typeof rawStream.lastHypothesisGate.accepted === "number"
319
- ? Math.max(0, Math.floor(rawStream.lastHypothesisGate.accepted))
320
- : 0,
321
- rejected: typeof rawStream.lastHypothesisGate.rejected === "number"
322
- ? Math.max(0, Math.floor(rawStream.lastHypothesisGate.rejected))
323
- : 0,
324
- rejectionReasons: Array.isArray(rawStream.lastHypothesisGate.rejectionReasons)
325
- ? rawStream.lastHypothesisGate.rejectionReasons
326
- .filter((item) => typeof item === "string")
327
- .map((item) => normalizeText(item))
328
- .filter((item) => item.length > 0)
329
- : [],
330
- }
331
- : defaultHypothesisGateState(),
332
181
  };
333
182
  }
334
183
  return {
@@ -359,8 +208,8 @@ function normalizeStringArray(raw) {
359
208
  return undefined;
360
209
  const values = raw
361
210
  .filter((item) => typeof item === "string")
362
- .map((item) => cleanOptionalText(item))
363
- .filter((item) => Boolean(item));
211
+ .map((item) => normalizeText(item))
212
+ .filter((item) => item.length > 0);
364
213
  return values.length > 0 ? values : undefined;
365
214
  }
366
215
  function normalizeEvidenceAnchors(raw) {
@@ -369,14 +218,14 @@ function normalizeEvidenceAnchors(raw) {
369
218
  const anchors = raw
370
219
  .filter((item) => !!item && typeof item === "object")
371
220
  .map((item) => {
372
- const claim = cleanOptionalText(item.claim);
221
+ const claim = normalizeText(item.claim ?? "");
373
222
  if (!claim)
374
223
  return undefined;
375
224
  return {
376
- ...(cleanOptionalText(item.section) ? { section: cleanOptionalText(item.section) } : {}),
377
- ...(cleanOptionalText(item.locator) ? { locator: cleanOptionalText(item.locator) } : {}),
225
+ ...(item.section ? { section: normalizeText(item.section) } : {}),
226
+ ...(item.locator ? { locator: normalizeText(item.locator) } : {}),
378
227
  claim,
379
- ...(cleanOptionalText(item.quote) ? { quote: cleanOptionalText(item.quote) } : {}),
228
+ ...(item.quote ? { quote: normalizeText(item.quote) } : {}),
380
229
  };
381
230
  })
382
231
  .filter((item) => Boolean(item));
@@ -394,7 +243,7 @@ function toPaperNoteSlug(paper) {
394
243
  }
395
244
  function normalizePaper(input) {
396
245
  const evidenceIds = Array.isArray(input.evidenceIds)
397
- ? input.evidenceIds.map((id) => cleanOptionalText(id)).filter((id) => Boolean(id))
246
+ ? input.evidenceIds.map((id) => normalizeText(id)).filter((id) => id.length > 0)
398
247
  : undefined;
399
248
  const keyEvidenceSpans = normalizeStringArray(input.keyEvidenceSpans);
400
249
  const subdomains = normalizeStringArray(input.subdomains);
@@ -415,33 +264,31 @@ function normalizePaper(input) {
415
264
  : readStatus
416
265
  ? false
417
266
  : undefined;
418
- const unreadReason = cleanOptionalText(input.unreadReason);
267
+ const unreadReason = input.unreadReason ? normalizeText(input.unreadReason) : undefined;
419
268
  return {
420
- ...(cleanOptionalText(input.id) ? { id: cleanOptionalText(input.id) } : {}),
421
- ...(cleanOptionalText(input.title) ? { title: cleanOptionalText(input.title) } : {}),
422
- ...(cleanOptionalText(input.url) ? { url: cleanOptionalText(input.url) } : {}),
423
- ...(cleanOptionalText(input.source) ? { source: cleanOptionalText(input.source) } : {}),
424
- ...(cleanOptionalText(input.publishedAt) ? { publishedAt: cleanOptionalText(input.publishedAt) } : {}),
269
+ ...(input.id ? { id: normalizeText(input.id) } : {}),
270
+ ...(input.title ? { title: normalizeText(input.title) } : {}),
271
+ ...(input.url ? { url: normalizeText(input.url) } : {}),
272
+ ...(input.source ? { source: normalizeText(input.source) } : {}),
273
+ ...(input.publishedAt ? { publishedAt: normalizeText(input.publishedAt) } : {}),
425
274
  ...(typeof input.score === "number" && Number.isFinite(input.score)
426
275
  ? { score: Number(input.score.toFixed(2)) }
427
276
  : {}),
428
- ...(cleanOptionalText(input.reason) ? { reason: cleanOptionalText(input.reason) } : {}),
429
- ...(cleanOptionalText(input.summary) ? { summary: cleanOptionalText(input.summary) } : {}),
277
+ ...(input.reason ? { reason: normalizeText(input.reason) } : {}),
278
+ ...(input.summary ? { summary: normalizeText(input.summary) } : {}),
430
279
  ...(evidenceIds && evidenceIds.length > 0 ? { evidenceIds } : {}),
431
280
  ...(typeof fullTextRead === "boolean" ? { fullTextRead } : {}),
432
281
  ...(readStatus ? { readStatus } : {}),
433
- ...(cleanOptionalText(input.fullTextSource) ? { fullTextSource: cleanOptionalText(input.fullTextSource) } : {}),
434
- ...(cleanOptionalText(input.fullTextRef) ? { fullTextRef: cleanOptionalText(input.fullTextRef) } : {}),
282
+ ...(input.fullTextSource ? { fullTextSource: normalizeText(input.fullTextSource) } : {}),
283
+ ...(input.fullTextRef ? { fullTextRef: normalizeText(input.fullTextRef) } : {}),
435
284
  ...(unreadReason ? { unreadReason } : {}),
436
285
  ...(keyEvidenceSpans && keyEvidenceSpans.length > 0 ? { keyEvidenceSpans } : {}),
437
- ...(cleanOptionalText(input.domain) ? { domain: cleanOptionalText(input.domain) } : {}),
286
+ ...(input.domain ? { domain: normalizeText(input.domain) } : {}),
438
287
  ...(subdomains ? { subdomains } : {}),
439
288
  ...(crossDomainLinks ? { crossDomainLinks } : {}),
440
- ...(cleanOptionalText(input.researchGoal) ? { researchGoal: cleanOptionalText(input.researchGoal) } : {}),
441
- ...(cleanOptionalText(input.approach) ? { approach: cleanOptionalText(input.approach) } : {}),
442
- ...(cleanOptionalText(input.methodologyDesign)
443
- ? { methodologyDesign: cleanOptionalText(input.methodologyDesign) }
444
- : {}),
289
+ ...(input.researchGoal ? { researchGoal: normalizeText(input.researchGoal) } : {}),
290
+ ...(input.approach ? { approach: normalizeText(input.approach) } : {}),
291
+ ...(input.methodologyDesign ? { methodologyDesign: normalizeText(input.methodologyDesign) } : {}),
445
292
  ...(keyContributions ? { keyContributions } : {}),
446
293
  ...(practicalInsights ? { practicalInsights } : {}),
447
294
  ...(mustUnderstandPoints ? { mustUnderstandPoints } : {}),
@@ -554,32 +401,6 @@ function hasStructuredProfile(paper) {
554
401
  (paper.limitations && paper.limitations.length > 0) ||
555
402
  (paper.evidenceAnchors && paper.evidenceAnchors.length > 0));
556
403
  }
557
- function countStructuredProfileFields(paper) {
558
- let count = 0;
559
- if (paper.domain && paper.domain.trim())
560
- count += 1;
561
- if (paper.subdomains && paper.subdomains.length > 0)
562
- count += 1;
563
- if (paper.crossDomainLinks && paper.crossDomainLinks.length > 0)
564
- count += 1;
565
- if (paper.researchGoal && paper.researchGoal.trim())
566
- count += 1;
567
- if (paper.approach && paper.approach.trim())
568
- count += 1;
569
- if (paper.methodologyDesign && paper.methodologyDesign.trim())
570
- count += 1;
571
- if (paper.keyContributions && paper.keyContributions.length > 0)
572
- count += 1;
573
- if (paper.practicalInsights && paper.practicalInsights.length > 0)
574
- count += 1;
575
- if (paper.mustUnderstandPoints && paper.mustUnderstandPoints.length > 0)
576
- count += 1;
577
- if (paper.limitations && paper.limitations.length > 0)
578
- count += 1;
579
- if (paper.evidenceAnchors && paper.evidenceAnchors.length > 0)
580
- count += 1;
581
- return count;
582
- }
583
404
  function isFullTextRead(paper) {
584
405
  return paper.fullTextRead === true || paper.readStatus === "fulltext";
585
406
  }
@@ -626,13 +447,6 @@ function applyQualityGates(args) {
626
447
  const fullTextCoreCount = corePapers.filter((paper) => isFullTextRead(paper)).length;
627
448
  const fullTextCoverage = coreCount > 0 ? fullTextCoreCount / coreCount : 0;
628
449
  const fullTextCoveragePct = Number((fullTextCoverage * 100).toFixed(2));
629
- const fullTextCorePapers = corePapers.filter((paper) => isFullTextRead(paper));
630
- const structuredFieldTotal = 11;
631
- const avgFullTextProfileCompleteness = fullTextCorePapers.length > 0
632
- ? fullTextCorePapers.reduce((sum, paper) => sum + countStructuredProfileFields(paper) / structuredFieldTotal, 0) /
633
- fullTextCorePapers.length
634
- : 0;
635
- const avgFullTextProfileCompletenessPct = Number((avgFullTextProfileCompleteness * 100).toFixed(2));
636
450
  const unreadCorePaperIds = dedupeText(corePapers
637
451
  .filter((paper) => !isFullTextRead(paper))
638
452
  .map((paper) => paper.id?.trim() || paper.url?.trim() || paper.title?.trim() || "unknown-paper")).slice(0, 50);
@@ -707,68 +521,22 @@ function applyQualityGates(args) {
707
521
  downgradedHighConfidenceCount += 1;
708
522
  }
709
523
  }
710
- const warnings = [];
711
- const fatalReasons = [];
712
- if (!args.hasAuditableArtifacts && !args.hasRunError) {
713
- fatalReasons.push("no_auditable_artifacts_without_run_error");
714
- }
715
- if (typeof args.requiredCorePapers === "number" && Number.isFinite(args.requiredCorePapers) && args.requiredCorePapers > 0) {
716
- const requiredCore = Math.floor(args.requiredCorePapers);
717
- if (coreCount === 0) {
718
- fatalReasons.push(`core_paper_count_below_required(${coreCount} < ${requiredCore})`);
719
- }
720
- else if (coreCount < requiredCore) {
721
- warnings.push(`core_paper_count_below_required(${coreCount} < ${requiredCore})`);
722
- }
723
- }
524
+ const reasons = [];
724
525
  if (fullTextCoverage < MIN_CORE_FULLTEXT_COVERAGE) {
725
- warnings.push(`core_fulltext_coverage_below_threshold(${fullTextCoveragePct}% < ${Number((MIN_CORE_FULLTEXT_COVERAGE * 100).toFixed(0))}%)`);
726
- }
727
- if (fullTextCorePapers.length > 0 && avgFullTextProfileCompleteness < MIN_FULLTEXT_PROFILE_COMPLETENESS) {
728
- warnings.push(`fulltext_profile_completeness_below_threshold(${avgFullTextProfileCompletenessPct}% < ${Number((MIN_FULLTEXT_PROFILE_COMPLETENESS * 100).toFixed(0))}%)`);
729
- }
730
- if (typeof args.requiredFullTextCoveragePct === "number" &&
731
- Number.isFinite(args.requiredFullTextCoveragePct) &&
732
- args.requiredFullTextCoveragePct > 0 &&
733
- fullTextCoveragePct < args.requiredFullTextCoveragePct) {
734
- warnings.push(`core_fulltext_coverage_below_required(${fullTextCoveragePct}% < ${Number(args.requiredFullTextCoveragePct.toFixed(2))}%)`);
526
+ reasons.push(`core_fulltext_coverage_below_threshold(${fullTextCoveragePct}% < ${Number((MIN_CORE_FULLTEXT_COVERAGE * 100).toFixed(0))}%)`);
735
527
  }
736
528
  if (evidenceBindingRate < MIN_EVIDENCE_BINDING_RATE) {
737
- warnings.push(`evidence_binding_rate_below_threshold(${evidenceBindingRatePct}% < ${Number((MIN_EVIDENCE_BINDING_RATE * 100).toFixed(0))}%)`);
738
- }
739
- if (citationErrorRate >= FATAL_CITATION_ERROR_RATE) {
740
- fatalReasons.push(`citation_error_rate_above_threshold(${citationErrorRatePct}% >= ${Number((FATAL_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
741
- }
742
- else if (citationErrorRate >= MAX_CITATION_ERROR_RATE) {
743
- warnings.push(`citation_error_rate_above_warning_threshold(${citationErrorRatePct}% >= ${Number((MAX_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
744
- }
745
- const bridgeChangeCount = args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length;
746
- const reviseCount = args.knowledgeChanges.filter((item) => item.type === "REVISE").length;
747
- const confirmCount = args.knowledgeChanges.filter((item) => item.type === "CONFIRM").length;
748
- const executedReflectionCount = args.reflectionTasks.filter((task) => task.status === "executed").length;
749
- if (bridgeChangeCount > 0 && executedReflectionCount === 0) {
750
- warnings.push(`reflection_missing_for_bridge(bridge_count=${bridgeChangeCount})`);
529
+ reasons.push(`evidence_binding_rate_below_threshold(${evidenceBindingRatePct}% < ${Number((MIN_EVIDENCE_BINDING_RATE * 100).toFixed(0))}%)`);
751
530
  }
752
- if (reviseCount > 0 && confirmCount > 0 && executedReflectionCount === 0) {
753
- warnings.push(`reflection_missing_for_conflict(revise_count=${reviseCount},confirm_count=${confirmCount})`);
754
- }
755
- if (args.hypothesisGate.rejected > 0 && args.hypothesisGate.accepted === 0 && args.hypotheses.length > 0) {
756
- warnings.push(`hypothesis_gate_rejected_all(${args.hypothesisGate.rejected})`);
531
+ if (citationErrorRate >= MAX_CITATION_ERROR_RATE) {
532
+ reasons.push(`citation_error_rate_above_threshold(${citationErrorRatePct}% >= ${Number((MAX_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
757
533
  }
758
534
  if (downgradedHighConfidenceCount > 0) {
759
- warnings.push(`high_confidence_downgraded(${downgradedHighConfidenceCount})`);
535
+ reasons.push(`high_confidence_downgraded(${downgradedHighConfidenceCount})`);
760
536
  }
761
- const reasons = [...warnings, ...fatalReasons];
762
- const blocking = fatalReasons.length > 0;
763
- const severity = blocking ? "fatal" : warnings.length > 0 ? "warn" : "ok";
764
537
  return {
765
538
  qualityGate: {
766
- mode: "soft",
767
- severity,
768
- warnings,
769
- fatalReasons,
770
- blocking,
771
- passed: !blocking,
539
+ passed: reasons.length === 0,
772
540
  fullTextCoveragePct,
773
541
  evidenceBindingRatePct,
774
542
  citationErrorRatePct,
@@ -778,20 +546,6 @@ function applyQualityGates(args) {
778
546
  downgradedHighConfidenceCount,
779
547
  };
780
548
  }
781
- function deriveEffectiveStatus(args) {
782
- const requested = normalizeText(args.requestedStatus || "ok").toLowerCase();
783
- if (requested === "error" || args.hasRunError)
784
- return "error";
785
- if (args.qualityBlocking)
786
- return "degraded_quality";
787
- if (requested === "empty")
788
- return args.runArtifactCount > 0 ? "ok" : "empty";
789
- if (requested === "degraded_quality")
790
- return "ok";
791
- if (requested.length === 0)
792
- return args.runArtifactCount > 0 ? "ok" : "empty";
793
- return requested;
794
- }
795
549
  function normalizeChange(input) {
796
550
  const statement = normalizeText(input.statement ?? "");
797
551
  if (!statement)
@@ -845,39 +599,12 @@ function normalizeHypothesis(input) {
845
599
  : undefined;
846
600
  const validationEvidence = normalizeStringArray(input.validationEvidence);
847
601
  const validationNotes = input.validationNotes ? normalizeText(input.validationNotes) : undefined;
848
- const strengths = normalizeStringArray(input.strengths);
849
- const weaknesses = normalizeStringArray(input.weaknesses);
850
- const planSteps = normalizeStringArray(input.planSteps);
851
- const strictEvaluationRaw = input.strictEvaluation;
852
- const strictEvaluation = strictEvaluationRaw && typeof strictEvaluationRaw === "object"
853
- ? (() => {
854
- const overallScore = typeof strictEvaluationRaw.overallScore === "number" && Number.isFinite(strictEvaluationRaw.overallScore)
855
- ? Math.max(0, Math.min(100, Number(strictEvaluationRaw.overallScore.toFixed(2))))
856
- : undefined;
857
- const decisionRaw = strictEvaluationRaw.decision?.trim().toLowerCase();
858
- const decision = decisionRaw === "accept" || decisionRaw === "revise" || decisionRaw === "reject"
859
- ? decisionRaw
860
- : undefined;
861
- const reason = strictEvaluationRaw.reason ? normalizeText(strictEvaluationRaw.reason) : undefined;
862
- if (overallScore === undefined && !decision && !reason)
863
- return undefined;
864
- return {
865
- ...(overallScore !== undefined ? { overallScore } : {}),
866
- ...(decision ? { decision } : {}),
867
- ...(reason ? { reason } : {}),
868
- };
869
- })()
870
- : undefined;
871
602
  const withScore = (value) => typeof value === "number" && Number.isFinite(value) ? Number(value.toFixed(2)) : undefined;
872
603
  return {
873
604
  ...(input.id ? { id: sanitizeId(input.id) } : {}),
874
605
  statement,
875
606
  trigger,
876
607
  ...(dependencyPath && dependencyPath.length > 0 ? { dependencyPath } : {}),
877
- ...(strengths ? { strengths } : {}),
878
- ...(weaknesses ? { weaknesses } : {}),
879
- ...(planSteps ? { planSteps } : {}),
880
- ...(strictEvaluation ? { strictEvaluation } : {}),
881
608
  ...(typeof withScore(input.novelty) === "number" ? { novelty: withScore(input.novelty) } : {}),
882
609
  ...(typeof withScore(input.feasibility) === "number" ? { feasibility: withScore(input.feasibility) } : {}),
883
610
  ...(typeof withScore(input.impact) === "number" ? { impact: withScore(input.impact) } : {}),
@@ -915,12 +642,10 @@ function toSummary(stream) {
915
642
  return {
916
643
  projectId: stream.projectId,
917
644
  streamKey: stream.topicKey,
918
- runProfile: stream.lastRunProfile,
919
645
  totalRuns: stream.totalRuns,
920
646
  totalHypotheses: stream.totalHypotheses,
921
647
  knowledgeTopicsCount: stream.knowledgeTopics.length,
922
648
  paperNotesCount: stream.paperNotes.length,
923
- triggerState: stream.triggerState,
924
649
  recentFullTextReadCount: stream.recentFullTextReadCount,
925
650
  recentNotFullTextReadCount: stream.recentNotFullTextReadCount,
926
651
  qualityGate: stream.lastQualityGate,
@@ -931,8 +656,6 @@ function toSummary(stream) {
931
656
  recentHypotheses: stream.recentHypotheses,
932
657
  recentChangeStats: stream.recentChangeStats,
933
658
  lastExplorationTrace: stream.lastExplorationTrace,
934
- lastReflectionTasks: stream.lastReflectionTasks,
935
- hypothesisGate: stream.lastHypothesisGate,
936
659
  };
937
660
  }
938
661
  function countChangeStats(day, runId, changes) {
@@ -959,448 +682,6 @@ function countChangeStats(day, runId, changes) {
959
682
  bridgeCount,
960
683
  };
961
684
  }
962
- function tokenizeForQuery(raw) {
963
- return normalizeText(raw)
964
- .toLowerCase()
965
- .replace(/[^a-z0-9\u4e00-\u9fff\s_-]+/g, " ")
966
- .split(/\s+/)
967
- .map((item) => item.trim())
968
- .filter((item) => item.length >= 3);
969
- }
970
- function uniqueText(values) {
971
- return [...new Set(values.map((item) => normalizeText(item)).filter((item) => item.length > 0))];
972
- }
973
- function buildReflectionQuery(topic, statement, fallbackHint) {
974
- const topicTokens = tokenizeForQuery(topic).slice(0, 4);
975
- const stmtTokens = tokenizeForQuery(statement).slice(0, 6);
976
- const merged = uniqueText([...topicTokens, ...stmtTokens]);
977
- if (merged.length === 0)
978
- return `${topic} ${fallbackHint}`.trim();
979
- return merged.join(" ");
980
- }
981
- function queryMatchesTrace(query, trace) {
982
- const tokens = tokenizeForQuery(query).slice(0, 4);
983
- if (tokens.length === 0)
984
- return false;
985
- return trace.some((step) => {
986
- const hay = normalizeText(step.query).toLowerCase();
987
- let hit = 0;
988
- for (const token of tokens) {
989
- if (hay.includes(token))
990
- hit += 1;
991
- if (hit >= Math.min(2, tokens.length))
992
- return true;
993
- }
994
- return false;
995
- });
996
- }
997
- function deriveReflectionTasks(args) {
998
- const tasks = [];
999
- const bridge = args.changes.filter((item) => item.type === "BRIDGE");
1000
- const revise = args.changes.filter((item) => item.type === "REVISE");
1001
- const confirm = args.changes.filter((item) => item.type === "CONFIRM");
1002
- const newly = args.changes.filter((item) => item.type === "NEW");
1003
- for (const [idx, change] of bridge.slice(0, 3).entries()) {
1004
- const query = buildReflectionQuery(args.topic, change.statement, "cross-domain mechanism");
1005
- tasks.push({
1006
- id: sanitizeId(`bridge-${idx + 1}-${query}`),
1007
- trigger: "BRIDGE",
1008
- reason: `Bridge signal requires cross-domain follow-up: ${change.statement}`,
1009
- query,
1010
- priority: "high",
1011
- status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
1012
- });
1013
- }
1014
- if (newly.length >= 3) {
1015
- const query = buildReflectionQuery(args.topic, newly.map((item) => item.statement).join(" "), "trend synthesis");
1016
- tasks.push({
1017
- id: sanitizeId(`trend-${query}`),
1018
- trigger: "TREND",
1019
- reason: `New findings accumulated (${newly.length}); run trend synthesis and gap scan.`,
1020
- query,
1021
- priority: "medium",
1022
- status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
1023
- });
1024
- }
1025
- if (revise.length > 0 && confirm.length > 0) {
1026
- const query = buildReflectionQuery(args.topic, `${revise[0]?.statement ?? ""} ${confirm[0]?.statement ?? ""}`, "contradiction resolution");
1027
- tasks.push({
1028
- id: sanitizeId(`contradiction-${query}`),
1029
- trigger: "CONTRADICTION",
1030
- reason: `Revise and confirm signals co-exist; verify contradiction boundaries.`,
1031
- query,
1032
- priority: "high",
1033
- status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
1034
- });
1035
- }
1036
- const unreadCore = args.corePapers.filter((paper) => !isFullTextRead(paper));
1037
- if (unreadCore.length > 0) {
1038
- const topId = unreadCore[0]?.id ?? unreadCore[0]?.title ?? "core-paper";
1039
- const query = buildReflectionQuery(args.topic, String(topId), "full text retrieval");
1040
- tasks.push({
1041
- id: sanitizeId(`unread-core-${query}`),
1042
- trigger: "UNREAD_CORE",
1043
- reason: `${unreadCore.length} core paper(s) were not fully read; prioritize retrieval and verification.`,
1044
- query,
1045
- priority: "medium",
1046
- status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
1047
- });
1048
- }
1049
- const dedup = new Map();
1050
- for (const task of tasks) {
1051
- const key = normalizeText(task.query).toLowerCase();
1052
- if (!key)
1053
- continue;
1054
- const existing = dedup.get(key);
1055
- if (!existing) {
1056
- dedup.set(key, task);
1057
- continue;
1058
- }
1059
- // Keep higher priority / executed status when duplicates collide.
1060
- const priorityRank = { high: 3, medium: 2, low: 1 };
1061
- const pick = (existing.status !== "executed" && task.status === "executed") ||
1062
- priorityRank[task.priority] > priorityRank[existing.priority]
1063
- ? task
1064
- : existing;
1065
- dedup.set(key, pick);
1066
- }
1067
- return [...dedup.values()].slice(0, MAX_LAST_REFLECTION_TASKS);
1068
- }
1069
- function sanitizeKnowledgeChanges(args) {
1070
- if (args.changes.length === 0) {
1071
- return {
1072
- changes: [],
1073
- droppedBridgeCount: 0,
1074
- };
1075
- }
1076
- const paperLookup = buildPaperLookup(args.allRunPapers);
1077
- const next = [];
1078
- let droppedBridgeCount = 0;
1079
- for (const change of args.changes) {
1080
- if (change.type !== "BRIDGE") {
1081
- next.push(change);
1082
- continue;
1083
- }
1084
- const evidenceIds = (change.evidenceIds ?? []).map((id) => normalizedCitationToken(id)).filter((id) => id.length > 0);
1085
- if (evidenceIds.length === 0) {
1086
- droppedBridgeCount += 1;
1087
- continue;
1088
- }
1089
- let hasResolvedEvidence = false;
1090
- let hasFullTextEvidence = false;
1091
- for (const evidenceId of evidenceIds) {
1092
- const paper = paperLookup.get(evidenceId);
1093
- if (!paper)
1094
- continue;
1095
- hasResolvedEvidence = true;
1096
- if (isFullTextRead(paper))
1097
- hasFullTextEvidence = true;
1098
- }
1099
- // Guard against speculative bridge signals with no grounded full-text evidence.
1100
- if (!hasResolvedEvidence || !hasFullTextEvidence) {
1101
- droppedBridgeCount += 1;
1102
- continue;
1103
- }
1104
- next.push(change);
1105
- }
1106
- return {
1107
- changes: next,
1108
- droppedBridgeCount,
1109
- };
1110
- }
1111
- function applyHypothesisGate(args) {
1112
- const acceptedHypotheses = [];
1113
- const rejectionReasonSet = new Set();
1114
- const paperLookup = buildPaperLookup(args.allRunPapers);
1115
- const fullTextEvidenceIds = uniqueText(args.allRunPapers
1116
- .filter((paper) => isFullTextRead(paper))
1117
- .map((paper) => normalizedCitationToken(paper.id ?? paper.url ?? paper.title ?? ""))
1118
- .filter((id) => id.length > 0));
1119
- const anyEvidenceIds = uniqueText(args.allRunPapers
1120
- .map((paper) => normalizedCitationToken(paper.id ?? paper.url ?? paper.title ?? ""))
1121
- .filter((id) => id.length > 0));
1122
- const changeCounts = {
1123
- NEW: args.knowledgeChanges.filter((item) => item.type === "NEW").length,
1124
- CONFIRM: args.knowledgeChanges.filter((item) => item.type === "CONFIRM").length,
1125
- REVISE: args.knowledgeChanges.filter((item) => item.type === "REVISE").length,
1126
- BRIDGE: args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length,
1127
- };
1128
- const evaluateHypothesisReasons = (hypothesis) => {
1129
- const reasons = [];
1130
- const statementLen = normalizeText(hypothesis.statement).length;
1131
- if (statementLen < MIN_HYPOTHESIS_STATEMENT_CHARS) {
1132
- reasons.push(`statement_too_short(${statementLen}<${MIN_HYPOTHESIS_STATEMENT_CHARS})`);
1133
- }
1134
- const evidenceIds = uniqueText((hypothesis.evidenceIds ?? []).map((id) => normalizedCitationToken(id)));
1135
- if (evidenceIds.length < MIN_HYPOTHESIS_EVIDENCE) {
1136
- reasons.push(`insufficient_evidence_ids(${evidenceIds.length}<${MIN_HYPOTHESIS_EVIDENCE})`);
1137
- }
1138
- let resolvedEvidence = 0;
1139
- let fullTextSupported = 0;
1140
- for (const evidenceId of evidenceIds) {
1141
- const paper = paperLookup.get(evidenceId);
1142
- if (!paper)
1143
- continue;
1144
- resolvedEvidence += 1;
1145
- if (isFullTextRead(paper))
1146
- fullTextSupported += 1;
1147
- }
1148
- if (resolvedEvidence < evidenceIds.length) {
1149
- reasons.push(`unresolved_evidence_ids(${evidenceIds.length - resolvedEvidence})`);
1150
- }
1151
- if (fullTextSupported === 0 && args.runProfile === "strict") {
1152
- reasons.push("no_fulltext_backed_evidence");
1153
- }
1154
- if (resolvedEvidence === 0) {
1155
- reasons.push("no_resolved_evidence");
1156
- }
1157
- const dependencyPathLength = hypothesis.dependencyPath?.length ?? 0;
1158
- if (dependencyPathLength < MIN_HYPOTHESIS_DEPENDENCY_STEPS) {
1159
- reasons.push(`dependency_path_too_short(${dependencyPathLength}<${MIN_HYPOTHESIS_DEPENDENCY_STEPS})`);
1160
- }
1161
- const strengthsCount = hypothesis.strengths?.length ?? 0;
1162
- if (strengthsCount < MIN_HYPOTHESIS_STRENGTHS) {
1163
- reasons.push(`strengths_too_few(${strengthsCount}<${MIN_HYPOTHESIS_STRENGTHS})`);
1164
- }
1165
- const weaknessesCount = hypothesis.weaknesses?.length ?? 0;
1166
- if (weaknessesCount < MIN_HYPOTHESIS_WEAKNESSES) {
1167
- reasons.push(`weaknesses_too_few(${weaknessesCount}<${MIN_HYPOTHESIS_WEAKNESSES})`);
1168
- }
1169
- const planStepCount = hypothesis.planSteps?.length ?? 0;
1170
- if (planStepCount < MIN_HYPOTHESIS_PLAN_STEPS) {
1171
- reasons.push(`plan_steps_too_few(${planStepCount}<${MIN_HYPOTHESIS_PLAN_STEPS})`);
1172
- }
1173
- const hasScore = typeof hypothesis.novelty === "number" &&
1174
- typeof hypothesis.feasibility === "number" &&
1175
- typeof hypothesis.impact === "number";
1176
- if (!hasScore) {
1177
- reasons.push("missing_self_assessment_scores");
1178
- }
1179
- const strictEval = hypothesis.strictEvaluation;
1180
- if (!strictEval) {
1181
- reasons.push("missing_strict_evaluation");
1182
- }
1183
- else {
1184
- if (typeof strictEval.overallScore !== "number" || !Number.isFinite(strictEval.overallScore)) {
1185
- reasons.push("strict_evaluation_missing_overall_score");
1186
- }
1187
- if (!strictEval.decision) {
1188
- reasons.push("strict_evaluation_missing_decision");
1189
- }
1190
- else if (strictEval.decision !== "accept") {
1191
- reasons.push(`strict_evaluation_not_accept(${strictEval.decision})`);
1192
- }
1193
- if (!strictEval.reason || normalizeText(strictEval.reason).length < 16) {
1194
- reasons.push("strict_evaluation_reason_too_short");
1195
- }
1196
- if (strictEval.decision === "accept" &&
1197
- typeof strictEval.overallScore === "number" &&
1198
- strictEval.overallScore < MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE) {
1199
- reasons.push(`strict_evaluation_score_below_threshold(${strictEval.overallScore}<${MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE})`);
1200
- }
1201
- }
1202
- if (hasScore &&
1203
- strictEval?.decision === "accept" &&
1204
- ((hypothesis.novelty ?? 0) + (hypothesis.feasibility ?? 0) + (hypothesis.impact ?? 0)) / 3 <
1205
- MIN_HYPOTHESIS_ACCEPT_SELF_SCORE_AVG) {
1206
- reasons.push(`self_assessment_avg_below_accept_threshold(${Number((((hypothesis.novelty ?? 0) + (hypothesis.feasibility ?? 0) + (hypothesis.impact ?? 0)) / 3).toFixed(2))}<${MIN_HYPOTHESIS_ACCEPT_SELF_SCORE_AVG})`);
1207
- }
1208
- if (hypothesis.trigger === "BRIDGE" && changeCounts.BRIDGE === 0) {
1209
- reasons.push("trigger_bridge_without_bridge_change");
1210
- }
1211
- if (hypothesis.trigger === "TREND" && changeCounts.NEW < 2) {
1212
- reasons.push("trigger_trend_without_new_accumulation");
1213
- }
1214
- if (hypothesis.trigger === "CONTRADICTION" && !(changeCounts.REVISE > 0 && changeCounts.CONFIRM > 0)) {
1215
- reasons.push("trigger_contradiction_without_revise_confirm_pair");
1216
- }
1217
- if (hypothesis.trigger === "GAP" && changeCounts.NEW + changeCounts.REVISE < 2) {
1218
- reasons.push("trigger_gap_without_gap_signal");
1219
- }
1220
- return reasons;
1221
- };
1222
- const isRepairableReason = (reason) => {
1223
- return (reason.startsWith("dependency_path_too_short(") ||
1224
- reason.startsWith("strengths_too_few(") ||
1225
- reason.startsWith("weaknesses_too_few(") ||
1226
- reason.startsWith("plan_steps_too_few(") ||
1227
- reason === "missing_self_assessment_scores" ||
1228
- reason === "missing_strict_evaluation" ||
1229
- reason === "strict_evaluation_missing_overall_score" ||
1230
- reason === "strict_evaluation_missing_decision" ||
1231
- reason === "strict_evaluation_reason_too_short" ||
1232
- reason.startsWith("strict_evaluation_not_accept(") ||
1233
- reason.startsWith("strict_evaluation_score_below_threshold(") ||
1234
- reason.startsWith("insufficient_evidence_ids(") ||
1235
- reason.startsWith("unresolved_evidence_ids(") ||
1236
- reason === "no_fulltext_backed_evidence" ||
1237
- reason === "no_resolved_evidence");
1238
- };
1239
- const autoReviseHypothesis = (hypothesis, reasons) => {
1240
- if (!reasons.some((reason) => isRepairableReason(reason)))
1241
- return undefined;
1242
- const revised = {
1243
- ...hypothesis,
1244
- ...(hypothesis.dependencyPath ? { dependencyPath: [...hypothesis.dependencyPath] } : {}),
1245
- ...(hypothesis.strengths ? { strengths: [...hypothesis.strengths] } : {}),
1246
- ...(hypothesis.weaknesses ? { weaknesses: [...hypothesis.weaknesses] } : {}),
1247
- ...(hypothesis.planSteps ? { planSteps: [...hypothesis.planSteps] } : {}),
1248
- ...(hypothesis.evidenceIds ? { evidenceIds: [...hypothesis.evidenceIds] } : {}),
1249
- ...(hypothesis.strictEvaluation ? { strictEvaluation: { ...hypothesis.strictEvaluation } } : {}),
1250
- };
1251
- let changed = false;
1252
- const seedEvidence = fullTextEvidenceIds.length > 0 ? fullTextEvidenceIds : anyEvidenceIds;
1253
- if (seedEvidence.length > 0 && ((revised.evidenceIds?.length ?? 0) < MIN_HYPOTHESIS_EVIDENCE || reasons.some((item) => item.startsWith("unresolved_evidence_ids(") || item === "no_resolved_evidence" || item === "no_fulltext_backed_evidence"))) {
1254
- revised.evidenceIds = uniqueText([...(revised.evidenceIds ?? []), ...seedEvidence]).slice(0, Math.max(MIN_HYPOTHESIS_EVIDENCE, 4));
1255
- changed = true;
1256
- }
1257
- while ((revised.dependencyPath?.length ?? 0) < MIN_HYPOTHESIS_DEPENDENCY_STEPS) {
1258
- revised.dependencyPath = [
1259
- ...(revised.dependencyPath ?? []),
1260
- revised.dependencyPath && revised.dependencyPath.length > 0
1261
- ? "Prior evidence accumulation provides an additional dependency step for this hypothesis."
1262
- : "This hypothesis is grounded in accumulated cross-paper evidence from current run outputs.",
1263
- ];
1264
- changed = true;
1265
- }
1266
- while ((revised.strengths?.length ?? 0) < MIN_HYPOTHESIS_STRENGTHS) {
1267
- revised.strengths = [
1268
- ...(revised.strengths ?? []),
1269
- revised.strengths && revised.strengths.length > 0
1270
- ? "The proposed idea has an explicit implementation path and measurable outcomes."
1271
- : "The hypothesis is evidence-grounded and directly connected to current literature signals.",
1272
- ];
1273
- changed = true;
1274
- }
1275
- while ((revised.weaknesses?.length ?? 0) < MIN_HYPOTHESIS_WEAKNESSES) {
1276
- revised.weaknesses = [
1277
- ...(revised.weaknesses ?? []),
1278
- revised.weaknesses && revised.weaknesses.length > 0
1279
- ? "Generality may be limited across domains without additional validation."
1280
- : "Potential sensitivity to data distribution and setup assumptions.",
1281
- ];
1282
- changed = true;
1283
- }
1284
- const defaultPlanStepPool = [
1285
- "Implement a reproducible baseline first (include a lightweight baseline such as random forest when applicable).",
1286
- "Implement the proposed method and compare under matched compute/data budgets.",
1287
- "Run ablation and failure-case analysis, then update accept/revise/reject decision.",
1288
- ];
1289
- while ((revised.planSteps?.length ?? 0) < MIN_HYPOTHESIS_PLAN_STEPS) {
1290
- const idx = revised.planSteps?.length ?? 0;
1291
- revised.planSteps = [...(revised.planSteps ?? []), defaultPlanStepPool[Math.min(idx, defaultPlanStepPool.length - 1)]];
1292
- changed = true;
1293
- }
1294
- if (typeof revised.novelty !== "number" ||
1295
- !Number.isFinite(revised.novelty) ||
1296
- typeof revised.feasibility !== "number" ||
1297
- !Number.isFinite(revised.feasibility) ||
1298
- typeof revised.impact !== "number" ||
1299
- !Number.isFinite(revised.impact)) {
1300
- revised.novelty = Number((revised.novelty ?? 3.8).toFixed(2));
1301
- revised.feasibility = Number((revised.feasibility ?? 3.6).toFixed(2));
1302
- revised.impact = Number((revised.impact ?? 3.8).toFixed(2));
1303
- changed = true;
1304
- }
1305
- const strictOverallScore = typeof revised.strictEvaluation?.overallScore === "number" && Number.isFinite(revised.strictEvaluation.overallScore)
1306
- ? revised.strictEvaluation.overallScore
1307
- : Math.max(MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE, Number((((revised.novelty ?? 3.5) + (revised.feasibility ?? 3.5) + (revised.impact ?? 3.5)) / 3 * 20).toFixed(2)));
1308
- const strictReason = revised.strictEvaluation?.reason && normalizeText(revised.strictEvaluation.reason).length >= 16
1309
- ? revised.strictEvaluation.reason
1310
- : "Auto-revised by hypothesis gate: structure, evidence, and execution plan now satisfy acceptance criteria.";
1311
- if (!revised.strictEvaluation ||
1312
- revised.strictEvaluation.decision !== "accept" ||
1313
- typeof revised.strictEvaluation.overallScore !== "number" ||
1314
- !Number.isFinite(revised.strictEvaluation.overallScore) ||
1315
- revised.strictEvaluation.overallScore < MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE ||
1316
- normalizeText(revised.strictEvaluation.reason ?? "").length < 16) {
1317
- revised.strictEvaluation = {
1318
- overallScore: Number(Math.max(strictOverallScore, MIN_HYPOTHESIS_STRICT_ACCEPT_SCORE).toFixed(2)),
1319
- decision: "accept",
1320
- reason: strictReason,
1321
- };
1322
- changed = true;
1323
- }
1324
- return changed ? revised : undefined;
1325
- };
1326
- for (const hypothesis of args.hypotheses) {
1327
- const firstReasons = evaluateHypothesisReasons(hypothesis);
1328
- if (firstReasons.length === 0) {
1329
- acceptedHypotheses.push(hypothesis);
1330
- continue;
1331
- }
1332
- const autoRevised = autoReviseHypothesis(hypothesis, firstReasons);
1333
- if (autoRevised) {
1334
- const secondReasons = evaluateHypothesisReasons(autoRevised);
1335
- if (secondReasons.length === 0) {
1336
- acceptedHypotheses.push(autoRevised);
1337
- continue;
1338
- }
1339
- for (const reason of secondReasons)
1340
- rejectionReasonSet.add(reason);
1341
- continue;
1342
- }
1343
- const reasons = firstReasons;
1344
- if (reasons.length > 0) {
1345
- for (const reason of reasons)
1346
- rejectionReasonSet.add(reason);
1347
- continue;
1348
- }
1349
- }
1350
- return {
1351
- acceptedHypotheses,
1352
- gate: {
1353
- accepted: acceptedHypotheses.length,
1354
- rejected: Math.max(0, args.hypotheses.length - acceptedHypotheses.length),
1355
- rejectionReasons: [...rejectionReasonSet].slice(0, MAX_HYPOTHESIS_REJECTION_REASONS),
1356
- },
1357
- };
1358
- }
1359
- function toDayStartMs(day) {
1360
- const ts = Date.parse(`${day}T00:00:00.000Z`);
1361
- return Number.isFinite(ts) ? ts : undefined;
1362
- }
1363
- function deriveTriggerState(args) {
1364
- const sorted = [...args.recentChangeStats].sort((a, b) => b.day.localeCompare(a.day));
1365
- // consecutive days from latest day with NEW/REVISE > 0
1366
- let consecutiveNewReviseDays = 0;
1367
- let expectedDayMs;
1368
- for (const item of sorted) {
1369
- const dayMs = toDayStartMs(item.day);
1370
- if (dayMs === undefined)
1371
- continue;
1372
- const hasSignal = item.newCount > 0 || item.reviseCount > 0;
1373
- if (!hasSignal)
1374
- break;
1375
- if (expectedDayMs === undefined) {
1376
- consecutiveNewReviseDays = 1;
1377
- expectedDayMs = dayMs - 24 * 60 * 60 * 1000;
1378
- continue;
1379
- }
1380
- if (dayMs === expectedDayMs) {
1381
- consecutiveNewReviseDays += 1;
1382
- expectedDayMs = dayMs - 24 * 60 * 60 * 1000;
1383
- continue;
1384
- }
1385
- break;
1386
- }
1387
- const sevenDaysAgo = args.nowMs - 7 * 24 * 60 * 60 * 1000;
1388
- let bridgeCount7d = 0;
1389
- for (const item of sorted) {
1390
- const dayMs = toDayStartMs(item.day);
1391
- if (dayMs === undefined)
1392
- continue;
1393
- if (dayMs < sevenDaysAgo)
1394
- continue;
1395
- bridgeCount7d += Math.max(0, Math.floor(item.bridgeCount));
1396
- }
1397
- return {
1398
- consecutiveNewReviseDays,
1399
- bridgeCount7d,
1400
- unreadCoreBacklog: Math.max(0, Math.floor(args.unreadCoreBacklog)),
1401
- lastUpdatedAtMs: args.nowMs,
1402
- };
1403
- }
1404
685
  export async function commitKnowledgeRun(input) {
1405
686
  const project = await resolveProjectContext({
1406
687
  projectId: input.projectId,
@@ -1425,7 +706,7 @@ export async function commitKnowledgeRun(input) {
1425
706
  const explorationTrace = (knowledgeState.explorationTrace ?? [])
1426
707
  .map(normalizeTrace)
1427
708
  .filter((item) => Boolean(item));
1428
- const submittedKnowledgeChanges = (knowledgeState.knowledgeChanges ?? [])
709
+ const knowledgeChanges = (knowledgeState.knowledgeChanges ?? [])
1429
710
  .map(normalizeChange)
1430
711
  .filter((item) => Boolean(item));
1431
712
  const knowledgeUpdates = (knowledgeState.knowledgeUpdates ?? [])
@@ -1452,12 +733,10 @@ export async function commitKnowledgeRun(input) {
1452
733
  topic: normalizeText(input.topic),
1453
734
  topicKey: streamKey,
1454
735
  projectId: project.projectId,
1455
- lastRunProfile: defaultRunProfile(),
1456
736
  totalRuns: 0,
1457
737
  totalHypotheses: 0,
1458
738
  knowledgeTopics: [],
1459
739
  paperNotes: [],
1460
- triggerState: defaultTriggerState(),
1461
740
  recentFullTextReadCount: 0,
1462
741
  recentNotFullTextReadCount: 0,
1463
742
  lastQualityGate: defaultQualityGateState(),
@@ -1468,16 +747,14 @@ export async function commitKnowledgeRun(input) {
1468
747
  recentHypotheses: [],
1469
748
  recentChangeStats: [],
1470
749
  lastExplorationTrace: [],
1471
- lastReflectionTasks: [],
1472
- lastHypothesisGate: defaultHypothesisGateState(),
1473
750
  };
1474
751
  const paperIds = mergePapers(corePapers, explorationPapers)
1475
752
  .map((paper) => paper.id || paper.url || paper.title || "")
1476
753
  .map((value) => normalizeText(value))
1477
754
  .filter((value) => value.length > 0);
1478
- const explicitRunId = input.runId?.trim() ? sanitizeId(input.runId) : undefined;
1479
- let runId = explicitRunId ??
1480
- buildRunFingerprint({
755
+ const runId = input.runId?.trim()
756
+ ? sanitizeId(input.runId)
757
+ : buildRunFingerprint({
1481
758
  scope: stream.scope,
1482
759
  topic: stream.topic,
1483
760
  status: input.status,
@@ -1485,30 +762,15 @@ export async function commitKnowledgeRun(input) {
1485
762
  paperIds,
1486
763
  note: input.note,
1487
764
  });
1488
- const inferredRunProfile = input.knowledgeState?.runLog?.runProfile === "strict" || input.knowledgeState?.runLog?.runProfile === "fast"
1489
- ? input.knowledgeState.runLog.runProfile
1490
- : input.knowledgeState?.runLog?.requiredCorePapers !== undefined ||
1491
- input.knowledgeState?.runLog?.requiredFullTextCoveragePct !== undefined
1492
- ? "strict"
1493
- : defaultRunProfile();
1494
765
  if (stream.recentRunIds.includes(runId)) {
1495
- // Preserve idempotency for fingerprint-based runs, but avoid silently dropping
1496
- // valid new cron cycles that accidentally reuse an explicit run_id.
1497
- if (!explicitRunId) {
1498
- root.streams[streamKey] = stream;
1499
- return {
1500
- projectId: project.projectId,
1501
- streamKey,
1502
- summary: toSummary(stream),
1503
- runId,
1504
- createdProject: project.created,
1505
- };
1506
- }
1507
- const collisionTag = createHash("sha1")
1508
- .update(`${nowMs}\n${paperIds.join("|")}\n${input.status ?? ""}\n${input.note ?? ""}`)
1509
- .digest("hex")
1510
- .slice(0, 8);
1511
- runId = `${runId}-r${collisionTag}`;
766
+ root.streams[streamKey] = stream;
767
+ return {
768
+ projectId: project.projectId,
769
+ streamKey,
770
+ summary: toSummary(stream),
771
+ runId,
772
+ createdProject: project.created,
773
+ };
1512
774
  }
1513
775
  const rootPath = getKnowledgeStateRoot(project.projectPath);
1514
776
  const logDir = path.join(rootPath, "logs");
@@ -1523,67 +785,18 @@ export async function commitKnowledgeRun(input) {
1523
785
  trace: explorationTrace,
1524
786
  papers: explorationPapers,
1525
787
  }));
1526
- const mergedRunPapers = mergePapers(corePapers, explorationPapers);
1527
- const changeSanitization = sanitizeKnowledgeChanges({
1528
- changes: submittedKnowledgeChanges,
1529
- allRunPapers: mergedRunPapers,
1530
- });
1531
- const knowledgeChanges = changeSanitization.changes;
1532
788
  await appendMarkdown(path.join(dailyDir, `day-${dayKey}.md`), renderDailyChangesMarkdown({ now: nowIso, runId, topic: stream.topic, changes: knowledgeChanges }));
1533
- const reflectionTasks = deriveReflectionTasks({
1534
- topic: stream.topic,
1535
- changes: knowledgeChanges,
1536
- trace: explorationTrace,
1537
- corePapers,
1538
- });
1539
- await appendMarkdown(path.join(logDir, `day-${dayKey}-reflection.md`), renderReflectionLogMarkdown({
1540
- now: nowIso,
1541
- runId,
1542
- tasks: reflectionTasks,
1543
- }));
1544
- const submittedHypotheses = hypotheses;
1545
- const hypothesisEval = applyHypothesisGate({
1546
- hypotheses: submittedHypotheses,
1547
- allRunPapers: mergedRunPapers,
1548
- knowledgeChanges,
1549
- runProfile: inferredRunProfile,
1550
- });
1551
- const acceptedHypotheses = hypothesisEval.acceptedHypotheses;
1552
- const runArtifactCount = corePapers.length +
1553
- explorationPapers.length +
1554
- explorationTrace.length +
1555
- knowledgeChanges.length +
1556
- knowledgeUpdates.length +
1557
- submittedHypotheses.length;
1558
- const hasRunError = Boolean(input.knowledgeState?.runLog?.error && normalizeText(input.knowledgeState.runLog.error).length > 0);
789
+ const mergedRunPapers = mergePapers(corePapers, explorationPapers);
1559
790
  const qualityEval = applyQualityGates({
1560
791
  corePapers,
1561
792
  allRunPapers: mergedRunPapers,
1562
- explorationTrace,
1563
- reflectionTasks,
1564
793
  knowledgeChanges,
1565
794
  knowledgeUpdates,
1566
- hypotheses: acceptedHypotheses,
1567
- hypothesisGate: hypothesisEval.gate,
1568
- requiredCorePapers: input.knowledgeState?.runLog?.requiredCorePapers,
1569
- requiredFullTextCoveragePct: input.knowledgeState?.runLog?.requiredFullTextCoveragePct,
1570
- hasAuditableArtifacts: runArtifactCount > 0,
1571
- hasRunError,
795
+ hypotheses,
1572
796
  });
1573
- if (changeSanitization.droppedBridgeCount > 0) {
1574
- qualityEval.qualityGate.warnings.push(`bridge_dropped_due_to_ungrounded_evidence(${changeSanitization.droppedBridgeCount})`);
1575
- qualityEval.qualityGate.reasons.push(`bridge_dropped_due_to_ungrounded_evidence(${changeSanitization.droppedBridgeCount})`);
1576
- if (qualityEval.qualityGate.severity === "ok") {
1577
- qualityEval.qualityGate.severity = "warn";
1578
- }
1579
- }
1580
797
  const requestedStatus = normalizeText(input.status ?? "ok");
1581
- const effectiveStatus = deriveEffectiveStatus({
1582
- requestedStatus,
1583
- runArtifactCount,
1584
- hasRunError,
1585
- qualityBlocking: qualityEval.qualityGate.blocking,
1586
- });
798
+ const qualitySensitiveStatus = requestedStatus === "ok" || requestedStatus === "fallback_representative";
799
+ const effectiveStatus = qualitySensitiveStatus && !qualityEval.qualityGate.passed ? "degraded_quality" : requestedStatus;
1587
800
  const topicToUpdates = new Map();
1588
801
  for (const update of knowledgeUpdates) {
1589
802
  const key = slugifyTopic(update.topic);
@@ -1623,7 +836,7 @@ export async function commitKnowledgeRun(input) {
1623
836
  const recentHypothesisSummaries = [];
1624
837
  let seq = stream.totalHypotheses;
1625
838
  const dayToken = dayKey.replace(/-/g, "");
1626
- for (const hypothesis of acceptedHypotheses) {
839
+ for (const hypothesis of hypotheses) {
1627
840
  seq += 1;
1628
841
  const hypothesisId = hypothesis.id && hypothesis.id.length > 0 ? sanitizeId(hypothesis.id) : `hyp-${dayToken}-${String(seq).padStart(4, "0")}`;
1629
842
  const file = `${hypothesisId}.md`;
@@ -1634,12 +847,6 @@ export async function commitKnowledgeRun(input) {
1634
847
  trigger: hypothesis.trigger,
1635
848
  createdAtMs: nowMs,
1636
849
  file,
1637
- ...(typeof hypothesis.strictEvaluation?.overallScore === "number"
1638
- ? { strictOverallScore: hypothesis.strictEvaluation.overallScore }
1639
- : {}),
1640
- ...(hypothesis.strictEvaluation?.decision
1641
- ? { strictDecision: hypothesis.strictEvaluation.decision }
1642
- : {}),
1643
850
  });
1644
851
  }
1645
852
  const fullTextStats = countFullTextStats(mergedRunPapers);
@@ -1647,7 +854,6 @@ export async function commitKnowledgeRun(input) {
1647
854
  await writeFile(path.join(knowledgeDir, "_index.md"), renderKnowledgeIndexMarkdown({
1648
855
  now: nowIso,
1649
856
  topic: stream.topic,
1650
- runProfile: inferredRunProfile,
1651
857
  topicFiles: stream.knowledgeTopics,
1652
858
  paperNotesCount: stream.paperNotes.length,
1653
859
  totalHypotheses: stream.totalHypotheses + recentHypothesisSummaries.length,
@@ -1656,13 +862,10 @@ export async function commitKnowledgeRun(input) {
1656
862
  notFullTextReadCount: fullTextStats.notFullTextReadCount,
1657
863
  qualityGate: qualityEval.qualityGate,
1658
864
  unreadCorePaperIds: qualityEval.unreadCorePaperIds,
1659
- reflectionTasks,
1660
- hypothesisGate: hypothesisEval.gate,
1661
865
  lastStatus: effectiveStatus,
1662
866
  }), "utf-8");
1663
867
  const changeStat = countChangeStats(dayKey, runId, knowledgeChanges);
1664
868
  stream.projectId = project.projectId;
1665
- stream.lastRunProfile = inferredRunProfile;
1666
869
  stream.totalRuns += 1;
1667
870
  stream.totalHypotheses += recentHypothesisSummaries.length;
1668
871
  stream.lastRunAtMs = nowMs;
@@ -1672,8 +875,6 @@ export async function commitKnowledgeRun(input) {
1672
875
  stream.lastQualityGate = qualityEval.qualityGate;
1673
876
  stream.lastUnreadCorePaperIds = qualityEval.unreadCorePaperIds;
1674
877
  stream.lastExplorationTrace = explorationTrace.slice(0, MAX_LAST_TRACE);
1675
- stream.lastReflectionTasks = reflectionTasks.slice(0, MAX_LAST_REFLECTION_TASKS);
1676
- stream.lastHypothesisGate = hypothesisEval.gate;
1677
878
  stream.recentPapers = mergePapers(mergedRunPapers, stream.recentPapers).slice(0, MAX_RECENT_PAPERS);
1678
879
  stream.recentRunIds = [runId, ...stream.recentRunIds.filter((id) => id !== runId)].slice(0, MAX_RECENT_RUN_IDS);
1679
880
  stream.recentHypothesisIds = [
@@ -1682,18 +883,11 @@ export async function commitKnowledgeRun(input) {
1682
883
  ].slice(0, MAX_RECENT_HYPOTHESES);
1683
884
  stream.recentHypotheses = [...recentHypothesisSummaries, ...stream.recentHypotheses].slice(0, MAX_RECENT_HYPOTHESES);
1684
885
  stream.recentChangeStats = [changeStat, ...stream.recentChangeStats].slice(0, MAX_RECENT_CHANGE_STATS);
1685
- stream.triggerState = deriveTriggerState({
1686
- recentChangeStats: stream.recentChangeStats,
1687
- unreadCoreBacklog: qualityEval.unreadCorePaperIds.length,
1688
- nowMs,
1689
- });
1690
886
  root.streams[streamKey] = stream;
1691
887
  await saveStateAtomic(project.projectPath, root);
1692
888
  await appendFile(path.join(logDir, `day-${dayKey}-run-details.jsonl`), `${JSON.stringify({
1693
889
  ts: nowMs,
1694
- run_id: runId,
1695
890
  runId,
1696
- run_profile: inferredRunProfile,
1697
891
  scope: stream.scope,
1698
892
  topic: stream.topic,
1699
893
  streamKey,
@@ -1701,14 +895,9 @@ export async function commitKnowledgeRun(input) {
1701
895
  corePapers,
1702
896
  explorationPapers,
1703
897
  explorationTrace,
1704
- reflectionTasks,
1705
- submittedKnowledgeChanges,
1706
898
  knowledgeChanges,
1707
- droppedBridgeCount: changeSanitization.droppedBridgeCount,
1708
899
  knowledgeUpdates,
1709
- hypotheses: acceptedHypotheses,
1710
- submittedHypotheses,
1711
- hypothesisGate: hypothesisEval.gate,
900
+ hypotheses,
1712
901
  paperNoteFiles: runPaperNoteFiles,
1713
902
  quality: {
1714
903
  fullTextReadCount: fullTextStats.fullTextReadCount,
@@ -1724,9 +913,7 @@ export async function commitKnowledgeRun(input) {
1724
913
  })}\n`, "utf-8");
1725
914
  await appendEvent(project.projectPath, {
1726
915
  ts: nowMs,
1727
- run_id: runId,
1728
916
  runId,
1729
- run_profile: inferredRunProfile,
1730
917
  scope: stream.scope,
1731
918
  topic: stream.topic,
1732
919
  streamKey,
@@ -1742,14 +929,8 @@ export async function commitKnowledgeRun(input) {
1742
929
  qualityGate: qualityEval.qualityGate,
1743
930
  unreadCorePaperIds: qualityEval.unreadCorePaperIds,
1744
931
  downgradedHighConfidenceCount: qualityEval.downgradedHighConfidenceCount,
1745
- submittedChangeCount: submittedKnowledgeChanges.length,
1746
932
  changeCount: knowledgeChanges.length,
1747
- droppedBridgeCount: changeSanitization.droppedBridgeCount,
1748
933
  hypothesisCount: recentHypothesisSummaries.length,
1749
- submittedHypothesisCount: submittedHypotheses.length,
1750
- hypothesisGate: hypothesisEval.gate,
1751
- triggerState: stream.triggerState,
1752
- reflectionTasks,
1753
934
  corePapers,
1754
935
  explorationPapers,
1755
936
  note: input.note,