scientify 1.12.0 → 1.12.1

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 (30) hide show
  1. package/dist/src/hooks/research-mode.d.ts +1 -1
  2. package/dist/src/hooks/research-mode.d.ts.map +1 -1
  3. package/dist/src/hooks/research-mode.js +28 -2
  4. package/dist/src/hooks/research-mode.js.map +1 -1
  5. package/dist/src/knowledge-state/render.d.ts +8 -1
  6. package/dist/src/knowledge-state/render.d.ts.map +1 -1
  7. package/dist/src/knowledge-state/render.js +31 -0
  8. package/dist/src/knowledge-state/render.js.map +1 -1
  9. package/dist/src/knowledge-state/store.d.ts.map +1 -1
  10. package/dist/src/knowledge-state/store.js +340 -6
  11. package/dist/src/knowledge-state/store.js.map +1 -1
  12. package/dist/src/knowledge-state/types.d.ts +19 -0
  13. package/dist/src/knowledge-state/types.d.ts.map +1 -1
  14. package/dist/src/literature/subscription-state.d.ts +2 -0
  15. package/dist/src/literature/subscription-state.d.ts.map +1 -1
  16. package/dist/src/literature/subscription-state.js +7 -0
  17. package/dist/src/literature/subscription-state.js.map +1 -1
  18. package/dist/src/research-subscriptions/prompt.d.ts.map +1 -1
  19. package/dist/src/research-subscriptions/prompt.js +44 -16
  20. package/dist/src/research-subscriptions/prompt.js.map +1 -1
  21. package/dist/src/tools/scientify-cron.d.ts +2 -0
  22. package/dist/src/tools/scientify-cron.d.ts.map +1 -1
  23. package/dist/src/tools/scientify-cron.js +33 -1
  24. package/dist/src/tools/scientify-cron.js.map +1 -1
  25. package/dist/src/tools/scientify-literature-state.d.ts +4 -0
  26. package/dist/src/tools/scientify-literature-state.d.ts.map +1 -1
  27. package/dist/src/tools/scientify-literature-state.js +45 -0
  28. package/dist/src/tools/scientify-literature-state.js.map +1 -1
  29. package/package.json +1 -1
  30. package/skills/research-subscription/SKILL.md +16 -0
@@ -2,18 +2,23 @@ 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, renderTopicUpdateMarkdown, slugifyTopic, } from "./render.js";
5
+ import { dayKeyFromTimestamp, renderDailyChangesMarkdown, renderExplorationLogMarkdown, renderHypothesisMarkdown, renderIngestLogMarkdown, renderKnowledgeIndexMarkdown, renderPaperNoteHeaderMarkdown, renderPaperNoteRunMarkdown, renderReflectionLogMarkdown, 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;
12
13
  const MAX_RECENT_PAPERS = 50;
13
14
  const MAX_PAPER_NOTES = 800;
15
+ const MAX_HYPOTHESIS_REJECTION_REASONS = 24;
14
16
  const MIN_CORE_FULLTEXT_COVERAGE = 0.8;
15
17
  const MIN_EVIDENCE_BINDING_RATE = 0.9;
16
18
  const MAX_CITATION_ERROR_RATE = 0.02;
19
+ const MIN_HYPOTHESIS_EVIDENCE = 2;
20
+ const MIN_HYPOTHESIS_DEPENDENCY_STEPS = 2;
21
+ const MIN_HYPOTHESIS_STATEMENT_CHARS = 48;
17
22
  function defaultQualityGateState() {
18
23
  return {
19
24
  passed: false,
@@ -23,6 +28,13 @@ function defaultQualityGateState() {
23
28
  reasons: ["quality gate not evaluated"],
24
29
  };
25
30
  }
31
+ function defaultHypothesisGateState() {
32
+ return {
33
+ accepted: 0,
34
+ rejected: 0,
35
+ rejectionReasons: [],
36
+ };
37
+ }
26
38
  function normalizeText(raw) {
27
39
  return raw.trim().replace(/\s+/g, " ");
28
40
  }
@@ -178,6 +190,39 @@ async function loadState(projectPath) {
178
190
  .map(normalizeTrace)
179
191
  .filter((item) => Boolean(item))
180
192
  : [],
193
+ lastReflectionTasks: Array.isArray(rawStream.lastReflectionTasks)
194
+ ? rawStream.lastReflectionTasks
195
+ .filter((item) => !!item && typeof item === "object")
196
+ .map((item) => ({
197
+ id: sanitizeId(item.id ?? "task"),
198
+ trigger: ["BRIDGE", "TREND", "CONTRADICTION", "UNREAD_CORE"].includes(item.trigger)
199
+ ? item.trigger
200
+ : "TREND",
201
+ reason: normalizeText(item.reason ?? ""),
202
+ query: normalizeText(item.query ?? ""),
203
+ priority: ["high", "medium", "low"].includes(item.priority) ? item.priority : "medium",
204
+ status: (item.status === "executed" ? "executed" : "planned"),
205
+ }))
206
+ .filter((item) => item.reason.length > 0 && item.query.length > 0)
207
+ : [],
208
+ lastHypothesisGate: rawStream.lastHypothesisGate &&
209
+ typeof rawStream.lastHypothesisGate === "object" &&
210
+ !Array.isArray(rawStream.lastHypothesisGate)
211
+ ? {
212
+ accepted: typeof rawStream.lastHypothesisGate.accepted === "number"
213
+ ? Math.max(0, Math.floor(rawStream.lastHypothesisGate.accepted))
214
+ : 0,
215
+ rejected: typeof rawStream.lastHypothesisGate.rejected === "number"
216
+ ? Math.max(0, Math.floor(rawStream.lastHypothesisGate.rejected))
217
+ : 0,
218
+ rejectionReasons: Array.isArray(rawStream.lastHypothesisGate.rejectionReasons)
219
+ ? rawStream.lastHypothesisGate.rejectionReasons
220
+ .filter((item) => typeof item === "string")
221
+ .map((item) => normalizeText(item))
222
+ .filter((item) => item.length > 0)
223
+ : [],
224
+ }
225
+ : defaultHypothesisGateState(),
181
226
  };
182
227
  }
183
228
  return {
@@ -522,15 +567,35 @@ function applyQualityGates(args) {
522
567
  }
523
568
  }
524
569
  const reasons = [];
570
+ if (typeof args.requiredCorePapers === "number" &&
571
+ Number.isFinite(args.requiredCorePapers) &&
572
+ args.requiredCorePapers > 0 &&
573
+ coreCount < args.requiredCorePapers) {
574
+ reasons.push(`core_paper_count_below_required(${coreCount} < ${Math.floor(args.requiredCorePapers)})`);
575
+ }
525
576
  if (fullTextCoverage < MIN_CORE_FULLTEXT_COVERAGE) {
526
577
  reasons.push(`core_fulltext_coverage_below_threshold(${fullTextCoveragePct}% < ${Number((MIN_CORE_FULLTEXT_COVERAGE * 100).toFixed(0))}%)`);
527
578
  }
579
+ if (typeof args.requiredFullTextCoveragePct === "number" &&
580
+ Number.isFinite(args.requiredFullTextCoveragePct) &&
581
+ args.requiredFullTextCoveragePct > 0 &&
582
+ fullTextCoveragePct < args.requiredFullTextCoveragePct) {
583
+ reasons.push(`core_fulltext_coverage_below_required(${fullTextCoveragePct}% < ${Number(args.requiredFullTextCoveragePct.toFixed(2))}%)`);
584
+ }
528
585
  if (evidenceBindingRate < MIN_EVIDENCE_BINDING_RATE) {
529
586
  reasons.push(`evidence_binding_rate_below_threshold(${evidenceBindingRatePct}% < ${Number((MIN_EVIDENCE_BINDING_RATE * 100).toFixed(0))}%)`);
530
587
  }
531
588
  if (citationErrorRate >= MAX_CITATION_ERROR_RATE) {
532
589
  reasons.push(`citation_error_rate_above_threshold(${citationErrorRatePct}% >= ${Number((MAX_CITATION_ERROR_RATE * 100).toFixed(0))}%)`);
533
590
  }
591
+ const bridgeChangeCount = args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length;
592
+ const executedReflectionCount = args.reflectionTasks.filter((task) => task.status === "executed").length;
593
+ if (bridgeChangeCount > 0 && executedReflectionCount === 0) {
594
+ reasons.push(`reflection_missing_for_bridge(bridge_count=${bridgeChangeCount})`);
595
+ }
596
+ if (args.hypothesisGate.rejected > 0 && args.hypothesisGate.accepted === 0 && args.hypotheses.length > 0) {
597
+ reasons.push(`hypothesis_gate_rejected_all(${args.hypothesisGate.rejected})`);
598
+ }
534
599
  if (downgradedHighConfidenceCount > 0) {
535
600
  reasons.push(`high_confidence_downgraded(${downgradedHighConfidenceCount})`);
536
601
  }
@@ -656,6 +721,8 @@ function toSummary(stream) {
656
721
  recentHypotheses: stream.recentHypotheses,
657
722
  recentChangeStats: stream.recentChangeStats,
658
723
  lastExplorationTrace: stream.lastExplorationTrace,
724
+ lastReflectionTasks: stream.lastReflectionTasks,
725
+ hypothesisGate: stream.lastHypothesisGate,
659
726
  };
660
727
  }
661
728
  function countChangeStats(day, runId, changes) {
@@ -682,6 +749,229 @@ function countChangeStats(day, runId, changes) {
682
749
  bridgeCount,
683
750
  };
684
751
  }
752
+ function tokenizeForQuery(raw) {
753
+ return normalizeText(raw)
754
+ .toLowerCase()
755
+ .replace(/[^a-z0-9\u4e00-\u9fff\s_-]+/g, " ")
756
+ .split(/\s+/)
757
+ .map((item) => item.trim())
758
+ .filter((item) => item.length >= 3);
759
+ }
760
+ function uniqueText(values) {
761
+ return [...new Set(values.map((item) => normalizeText(item)).filter((item) => item.length > 0))];
762
+ }
763
+ function buildReflectionQuery(topic, statement, fallbackHint) {
764
+ const topicTokens = tokenizeForQuery(topic).slice(0, 4);
765
+ const stmtTokens = tokenizeForQuery(statement).slice(0, 6);
766
+ const merged = uniqueText([...topicTokens, ...stmtTokens]);
767
+ if (merged.length === 0)
768
+ return `${topic} ${fallbackHint}`.trim();
769
+ return merged.join(" ");
770
+ }
771
+ function queryMatchesTrace(query, trace) {
772
+ const tokens = tokenizeForQuery(query).slice(0, 4);
773
+ if (tokens.length === 0)
774
+ return false;
775
+ return trace.some((step) => {
776
+ const hay = normalizeText(step.query).toLowerCase();
777
+ let hit = 0;
778
+ for (const token of tokens) {
779
+ if (hay.includes(token))
780
+ hit += 1;
781
+ if (hit >= Math.min(2, tokens.length))
782
+ return true;
783
+ }
784
+ return false;
785
+ });
786
+ }
787
+ function deriveReflectionTasks(args) {
788
+ const tasks = [];
789
+ const bridge = args.changes.filter((item) => item.type === "BRIDGE");
790
+ const revise = args.changes.filter((item) => item.type === "REVISE");
791
+ const confirm = args.changes.filter((item) => item.type === "CONFIRM");
792
+ const newly = args.changes.filter((item) => item.type === "NEW");
793
+ for (const [idx, change] of bridge.slice(0, 3).entries()) {
794
+ const query = buildReflectionQuery(args.topic, change.statement, "cross-domain mechanism");
795
+ tasks.push({
796
+ id: sanitizeId(`bridge-${idx + 1}-${query}`),
797
+ trigger: "BRIDGE",
798
+ reason: `Bridge signal requires cross-domain follow-up: ${change.statement}`,
799
+ query,
800
+ priority: "high",
801
+ status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
802
+ });
803
+ }
804
+ if (newly.length >= 3) {
805
+ const query = buildReflectionQuery(args.topic, newly.map((item) => item.statement).join(" "), "trend synthesis");
806
+ tasks.push({
807
+ id: sanitizeId(`trend-${query}`),
808
+ trigger: "TREND",
809
+ reason: `New findings accumulated (${newly.length}); run trend synthesis and gap scan.`,
810
+ query,
811
+ priority: "medium",
812
+ status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
813
+ });
814
+ }
815
+ if (revise.length > 0 && confirm.length > 0) {
816
+ const query = buildReflectionQuery(args.topic, `${revise[0]?.statement ?? ""} ${confirm[0]?.statement ?? ""}`, "contradiction resolution");
817
+ tasks.push({
818
+ id: sanitizeId(`contradiction-${query}`),
819
+ trigger: "CONTRADICTION",
820
+ reason: `Revise and confirm signals co-exist; verify contradiction boundaries.`,
821
+ query,
822
+ priority: "high",
823
+ status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
824
+ });
825
+ }
826
+ const unreadCore = args.corePapers.filter((paper) => !isFullTextRead(paper));
827
+ if (unreadCore.length > 0) {
828
+ const topId = unreadCore[0]?.id ?? unreadCore[0]?.title ?? "core-paper";
829
+ const query = buildReflectionQuery(args.topic, String(topId), "full text retrieval");
830
+ tasks.push({
831
+ id: sanitizeId(`unread-core-${query}`),
832
+ trigger: "UNREAD_CORE",
833
+ reason: `${unreadCore.length} core paper(s) were not fully read; prioritize retrieval and verification.`,
834
+ query,
835
+ priority: "medium",
836
+ status: queryMatchesTrace(query, args.trace) ? "executed" : "planned",
837
+ });
838
+ }
839
+ const dedup = new Map();
840
+ for (const task of tasks) {
841
+ const key = normalizeText(task.query).toLowerCase();
842
+ if (!key)
843
+ continue;
844
+ const existing = dedup.get(key);
845
+ if (!existing) {
846
+ dedup.set(key, task);
847
+ continue;
848
+ }
849
+ // Keep higher priority / executed status when duplicates collide.
850
+ const priorityRank = { high: 3, medium: 2, low: 1 };
851
+ const pick = (existing.status !== "executed" && task.status === "executed") ||
852
+ priorityRank[task.priority] > priorityRank[existing.priority]
853
+ ? task
854
+ : existing;
855
+ dedup.set(key, pick);
856
+ }
857
+ return [...dedup.values()].slice(0, MAX_LAST_REFLECTION_TASKS);
858
+ }
859
+ function sanitizeKnowledgeChanges(args) {
860
+ if (args.changes.length === 0) {
861
+ return {
862
+ changes: [],
863
+ droppedBridgeCount: 0,
864
+ };
865
+ }
866
+ const paperLookup = buildPaperLookup(args.allRunPapers);
867
+ const next = [];
868
+ let droppedBridgeCount = 0;
869
+ for (const change of args.changes) {
870
+ if (change.type !== "BRIDGE") {
871
+ next.push(change);
872
+ continue;
873
+ }
874
+ const evidenceIds = (change.evidenceIds ?? []).map((id) => normalizedCitationToken(id)).filter((id) => id.length > 0);
875
+ if (evidenceIds.length === 0) {
876
+ droppedBridgeCount += 1;
877
+ continue;
878
+ }
879
+ let hasResolvedEvidence = false;
880
+ let hasFullTextEvidence = false;
881
+ for (const evidenceId of evidenceIds) {
882
+ const paper = paperLookup.get(evidenceId);
883
+ if (!paper)
884
+ continue;
885
+ hasResolvedEvidence = true;
886
+ if (isFullTextRead(paper))
887
+ hasFullTextEvidence = true;
888
+ }
889
+ // Guard against speculative bridge signals with no grounded full-text evidence.
890
+ if (!hasResolvedEvidence || !hasFullTextEvidence) {
891
+ droppedBridgeCount += 1;
892
+ continue;
893
+ }
894
+ next.push(change);
895
+ }
896
+ return {
897
+ changes: next,
898
+ droppedBridgeCount,
899
+ };
900
+ }
901
+ function applyHypothesisGate(args) {
902
+ const acceptedHypotheses = [];
903
+ const rejectionReasonSet = new Set();
904
+ const paperLookup = buildPaperLookup(args.allRunPapers);
905
+ const changeCounts = {
906
+ NEW: args.knowledgeChanges.filter((item) => item.type === "NEW").length,
907
+ CONFIRM: args.knowledgeChanges.filter((item) => item.type === "CONFIRM").length,
908
+ REVISE: args.knowledgeChanges.filter((item) => item.type === "REVISE").length,
909
+ BRIDGE: args.knowledgeChanges.filter((item) => item.type === "BRIDGE").length,
910
+ };
911
+ for (const hypothesis of args.hypotheses) {
912
+ const reasons = [];
913
+ const statementLen = normalizeText(hypothesis.statement).length;
914
+ if (statementLen < MIN_HYPOTHESIS_STATEMENT_CHARS) {
915
+ reasons.push(`statement_too_short(${statementLen}<${MIN_HYPOTHESIS_STATEMENT_CHARS})`);
916
+ }
917
+ const evidenceIds = uniqueText((hypothesis.evidenceIds ?? []).map((id) => normalizedCitationToken(id)));
918
+ if (evidenceIds.length < MIN_HYPOTHESIS_EVIDENCE) {
919
+ reasons.push(`insufficient_evidence_ids(${evidenceIds.length}<${MIN_HYPOTHESIS_EVIDENCE})`);
920
+ }
921
+ let resolvedEvidence = 0;
922
+ let fullTextSupported = 0;
923
+ for (const evidenceId of evidenceIds) {
924
+ const paper = paperLookup.get(evidenceId);
925
+ if (!paper)
926
+ continue;
927
+ resolvedEvidence += 1;
928
+ if (isFullTextRead(paper))
929
+ fullTextSupported += 1;
930
+ }
931
+ if (resolvedEvidence < evidenceIds.length) {
932
+ reasons.push(`unresolved_evidence_ids(${evidenceIds.length - resolvedEvidence})`);
933
+ }
934
+ if (fullTextSupported === 0) {
935
+ reasons.push("no_fulltext_backed_evidence");
936
+ }
937
+ const dependencyPathLength = hypothesis.dependencyPath?.length ?? 0;
938
+ if (dependencyPathLength < MIN_HYPOTHESIS_DEPENDENCY_STEPS) {
939
+ reasons.push(`dependency_path_too_short(${dependencyPathLength}<${MIN_HYPOTHESIS_DEPENDENCY_STEPS})`);
940
+ }
941
+ const hasScore = typeof hypothesis.novelty === "number" &&
942
+ typeof hypothesis.feasibility === "number" &&
943
+ typeof hypothesis.impact === "number";
944
+ if (!hasScore) {
945
+ reasons.push("missing_self_assessment_scores");
946
+ }
947
+ if (hypothesis.trigger === "BRIDGE" && changeCounts.BRIDGE === 0) {
948
+ reasons.push("trigger_bridge_without_bridge_change");
949
+ }
950
+ if (hypothesis.trigger === "TREND" && changeCounts.NEW < 2) {
951
+ reasons.push("trigger_trend_without_new_accumulation");
952
+ }
953
+ if (hypothesis.trigger === "CONTRADICTION" && !(changeCounts.REVISE > 0 && changeCounts.CONFIRM > 0)) {
954
+ reasons.push("trigger_contradiction_without_revise_confirm_pair");
955
+ }
956
+ if (hypothesis.trigger === "GAP" && changeCounts.NEW + changeCounts.REVISE < 2) {
957
+ reasons.push("trigger_gap_without_gap_signal");
958
+ }
959
+ if (reasons.length > 0) {
960
+ for (const reason of reasons)
961
+ rejectionReasonSet.add(reason);
962
+ continue;
963
+ }
964
+ acceptedHypotheses.push(hypothesis);
965
+ }
966
+ return {
967
+ acceptedHypotheses,
968
+ gate: {
969
+ accepted: acceptedHypotheses.length,
970
+ rejected: Math.max(0, args.hypotheses.length - acceptedHypotheses.length),
971
+ rejectionReasons: [...rejectionReasonSet].slice(0, MAX_HYPOTHESIS_REJECTION_REASONS),
972
+ },
973
+ };
974
+ }
685
975
  export async function commitKnowledgeRun(input) {
686
976
  const project = await resolveProjectContext({
687
977
  projectId: input.projectId,
@@ -706,7 +996,7 @@ export async function commitKnowledgeRun(input) {
706
996
  const explorationTrace = (knowledgeState.explorationTrace ?? [])
707
997
  .map(normalizeTrace)
708
998
  .filter((item) => Boolean(item));
709
- const knowledgeChanges = (knowledgeState.knowledgeChanges ?? [])
999
+ const submittedKnowledgeChanges = (knowledgeState.knowledgeChanges ?? [])
710
1000
  .map(normalizeChange)
711
1001
  .filter((item) => Boolean(item));
712
1002
  const knowledgeUpdates = (knowledgeState.knowledgeUpdates ?? [])
@@ -747,6 +1037,8 @@ export async function commitKnowledgeRun(input) {
747
1037
  recentHypotheses: [],
748
1038
  recentChangeStats: [],
749
1039
  lastExplorationTrace: [],
1040
+ lastReflectionTasks: [],
1041
+ lastHypothesisGate: defaultHypothesisGateState(),
750
1042
  };
751
1043
  const paperIds = mergePapers(corePapers, explorationPapers)
752
1044
  .map((paper) => paper.id || paper.url || paper.title || "")
@@ -785,14 +1077,42 @@ export async function commitKnowledgeRun(input) {
785
1077
  trace: explorationTrace,
786
1078
  papers: explorationPapers,
787
1079
  }));
788
- await appendMarkdown(path.join(dailyDir, `day-${dayKey}.md`), renderDailyChangesMarkdown({ now: nowIso, runId, topic: stream.topic, changes: knowledgeChanges }));
789
1080
  const mergedRunPapers = mergePapers(corePapers, explorationPapers);
1081
+ const changeSanitization = sanitizeKnowledgeChanges({
1082
+ changes: submittedKnowledgeChanges,
1083
+ allRunPapers: mergedRunPapers,
1084
+ });
1085
+ const knowledgeChanges = changeSanitization.changes;
1086
+ await appendMarkdown(path.join(dailyDir, `day-${dayKey}.md`), renderDailyChangesMarkdown({ now: nowIso, runId, topic: stream.topic, changes: knowledgeChanges }));
1087
+ const reflectionTasks = deriveReflectionTasks({
1088
+ topic: stream.topic,
1089
+ changes: knowledgeChanges,
1090
+ trace: explorationTrace,
1091
+ corePapers,
1092
+ });
1093
+ await appendMarkdown(path.join(logDir, `day-${dayKey}-reflection.md`), renderReflectionLogMarkdown({
1094
+ now: nowIso,
1095
+ runId,
1096
+ tasks: reflectionTasks,
1097
+ }));
1098
+ const submittedHypotheses = hypotheses;
1099
+ const hypothesisEval = applyHypothesisGate({
1100
+ hypotheses: submittedHypotheses,
1101
+ allRunPapers: mergedRunPapers,
1102
+ knowledgeChanges,
1103
+ });
1104
+ const acceptedHypotheses = hypothesisEval.acceptedHypotheses;
790
1105
  const qualityEval = applyQualityGates({
791
1106
  corePapers,
792
1107
  allRunPapers: mergedRunPapers,
1108
+ explorationTrace,
1109
+ reflectionTasks,
793
1110
  knowledgeChanges,
794
1111
  knowledgeUpdates,
795
- hypotheses,
1112
+ hypotheses: acceptedHypotheses,
1113
+ hypothesisGate: hypothesisEval.gate,
1114
+ requiredCorePapers: input.knowledgeState?.runLog?.requiredCorePapers,
1115
+ requiredFullTextCoveragePct: input.knowledgeState?.runLog?.requiredFullTextCoveragePct,
796
1116
  });
797
1117
  const requestedStatus = normalizeText(input.status ?? "ok");
798
1118
  const qualitySensitiveStatus = requestedStatus === "ok" || requestedStatus === "fallback_representative";
@@ -836,7 +1156,7 @@ export async function commitKnowledgeRun(input) {
836
1156
  const recentHypothesisSummaries = [];
837
1157
  let seq = stream.totalHypotheses;
838
1158
  const dayToken = dayKey.replace(/-/g, "");
839
- for (const hypothesis of hypotheses) {
1159
+ for (const hypothesis of acceptedHypotheses) {
840
1160
  seq += 1;
841
1161
  const hypothesisId = hypothesis.id && hypothesis.id.length > 0 ? sanitizeId(hypothesis.id) : `hyp-${dayToken}-${String(seq).padStart(4, "0")}`;
842
1162
  const file = `${hypothesisId}.md`;
@@ -862,6 +1182,8 @@ export async function commitKnowledgeRun(input) {
862
1182
  notFullTextReadCount: fullTextStats.notFullTextReadCount,
863
1183
  qualityGate: qualityEval.qualityGate,
864
1184
  unreadCorePaperIds: qualityEval.unreadCorePaperIds,
1185
+ reflectionTasks,
1186
+ hypothesisGate: hypothesisEval.gate,
865
1187
  lastStatus: effectiveStatus,
866
1188
  }), "utf-8");
867
1189
  const changeStat = countChangeStats(dayKey, runId, knowledgeChanges);
@@ -875,6 +1197,8 @@ export async function commitKnowledgeRun(input) {
875
1197
  stream.lastQualityGate = qualityEval.qualityGate;
876
1198
  stream.lastUnreadCorePaperIds = qualityEval.unreadCorePaperIds;
877
1199
  stream.lastExplorationTrace = explorationTrace.slice(0, MAX_LAST_TRACE);
1200
+ stream.lastReflectionTasks = reflectionTasks.slice(0, MAX_LAST_REFLECTION_TASKS);
1201
+ stream.lastHypothesisGate = hypothesisEval.gate;
878
1202
  stream.recentPapers = mergePapers(mergedRunPapers, stream.recentPapers).slice(0, MAX_RECENT_PAPERS);
879
1203
  stream.recentRunIds = [runId, ...stream.recentRunIds.filter((id) => id !== runId)].slice(0, MAX_RECENT_RUN_IDS);
880
1204
  stream.recentHypothesisIds = [
@@ -895,9 +1219,14 @@ export async function commitKnowledgeRun(input) {
895
1219
  corePapers,
896
1220
  explorationPapers,
897
1221
  explorationTrace,
1222
+ reflectionTasks,
1223
+ submittedKnowledgeChanges,
898
1224
  knowledgeChanges,
1225
+ droppedBridgeCount: changeSanitization.droppedBridgeCount,
899
1226
  knowledgeUpdates,
900
- hypotheses,
1227
+ hypotheses: acceptedHypotheses,
1228
+ submittedHypotheses,
1229
+ hypothesisGate: hypothesisEval.gate,
901
1230
  paperNoteFiles: runPaperNoteFiles,
902
1231
  quality: {
903
1232
  fullTextReadCount: fullTextStats.fullTextReadCount,
@@ -929,8 +1258,13 @@ export async function commitKnowledgeRun(input) {
929
1258
  qualityGate: qualityEval.qualityGate,
930
1259
  unreadCorePaperIds: qualityEval.unreadCorePaperIds,
931
1260
  downgradedHighConfidenceCount: qualityEval.downgradedHighConfidenceCount,
1261
+ submittedChangeCount: submittedKnowledgeChanges.length,
932
1262
  changeCount: knowledgeChanges.length,
1263
+ droppedBridgeCount: changeSanitization.droppedBridgeCount,
933
1264
  hypothesisCount: recentHypothesisSummaries.length,
1265
+ submittedHypothesisCount: submittedHypotheses.length,
1266
+ hypothesisGate: hypothesisEval.gate,
1267
+ reflectionTasks,
934
1268
  corePapers,
935
1269
  explorationPapers,
936
1270
  note: input.note,