sneakoscope 6.1.0 → 6.2.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 (246) hide show
  1. package/README.md +35 -11
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +18 -9
  5. package/dist/bin/sks-dispatch.js +2 -2
  6. package/dist/cli/args.js +2 -0
  7. package/dist/cli/command-manifest-lite.js +2 -3
  8. package/dist/cli/command-registry.js +3 -4
  9. package/dist/cli/help-fast.js +1 -1
  10. package/dist/cli/install-helpers.js +116 -306
  11. package/dist/cli/install-tool-helpers.js +287 -0
  12. package/dist/cli/router.js +40 -4
  13. package/dist/commands/codex-lb.js +51 -20
  14. package/dist/commands/codex.js +39 -1
  15. package/dist/commands/doctor.js +242 -20
  16. package/dist/commands/tmux.js +5 -1
  17. package/dist/commands/zellij-monitor-pane.js +2 -0
  18. package/dist/commands/zellij-viewport-pane.js +3 -1
  19. package/dist/commands/zellij.js +1 -1
  20. package/dist/config/skills-manifest.json +59 -59
  21. package/dist/core/agent-bridge/agent-manifest.js +48 -0
  22. package/dist/core/agents/agent-command-surface.js +2 -1
  23. package/dist/core/agents/agent-effort-policy.js +38 -35
  24. package/dist/core/agents/agent-plan.js +100 -21
  25. package/dist/core/agents/agent-role-config.js +43 -46
  26. package/dist/core/agents/agent-runner-codex-exec.js +16 -3
  27. package/dist/core/agents/agent-schema.js +3 -2
  28. package/dist/core/agents/native-cli-session-swarm.js +1 -1
  29. package/dist/core/agents/zellij-right-lane-cockpit.js +2 -2
  30. package/dist/core/codex/agent-config-file-repair.js +94 -193
  31. package/dist/core/codex/codex-cli-update.js +723 -0
  32. package/dist/core/codex/codex-config-guard.js +185 -9
  33. package/dist/core/codex/codex-config-readability.js +21 -8
  34. package/dist/core/codex/codex-config-toml.js +14 -11
  35. package/dist/core/codex/codex-project-config-policy.js +3 -6
  36. package/dist/core/codex/codex-startup-config-postcheck.js +52 -42
  37. package/dist/core/codex-adapter.js +35 -22
  38. package/dist/core/codex-app/codex-agent-role-sync.js +13 -54
  39. package/dist/core/codex-app/codex-app-launcher.js +48 -17
  40. package/dist/core/codex-app/codex-app-restart.js +23 -2
  41. package/dist/core/codex-app/mcp-manager.js +679 -0
  42. package/dist/core/codex-app/sks-menubar.js +533 -8
  43. package/dist/core/codex-control/codex-0139-image-path-real-probe.js +15 -4
  44. package/dist/core/codex-control/codex-0139-web-search-probe.js +17 -6
  45. package/dist/core/codex-control/codex-lb-launch-recovery.js +293 -0
  46. package/dist/core/codex-control/codex-reliability-shield.js +89 -29
  47. package/dist/core/codex-control/codex-sdk-adapter.js +44 -8
  48. package/dist/core/codex-control/codex-task-runner.js +62 -9
  49. package/dist/core/codex-control/python-codex-sdk-adapter.js +30 -3
  50. package/dist/core/codex-exec-output-schema.js +35 -6
  51. package/dist/core/codex-lb/codex-lb-env.js +1 -1
  52. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +291 -0
  53. package/dist/core/codex-native/codex-native-feature-broker.js +9 -16
  54. package/dist/core/codex-native/codex-native-repair-transaction.js +1 -1
  55. package/dist/core/codex-native/core-skill-manifest.js +8 -4
  56. package/dist/core/codex-runtime/codex-desktop-config-policy.js +2 -3
  57. package/dist/core/commands/agent-bridge-command.js +23 -2
  58. package/dist/core/commands/agent-command.js +19 -5
  59. package/dist/core/commands/basic-cli.js +47 -6
  60. package/dist/core/commands/command-utils.js +1 -1
  61. package/dist/core/commands/mad-sks-command.js +76 -10
  62. package/dist/core/commands/menubar-command.js +94 -0
  63. package/dist/core/commands/naruto-command.js +645 -1156
  64. package/dist/core/commands/research-command.js +293 -231
  65. package/dist/core/commands/run-command.js +100 -23
  66. package/dist/core/commands/team-command.js +2 -3
  67. package/dist/core/commands/team-legacy-observe-command.js +94 -359
  68. package/dist/core/commands/wiki-command.js +11 -5
  69. package/dist/core/db-safety.js +2 -2
  70. package/dist/core/decision-lattice.js +6 -6
  71. package/dist/core/doctor/codex-startup-config-repair.js +8 -3
  72. package/dist/core/doctor/doctor-codex-startup-repair.js +6 -141
  73. package/dist/core/feature-fixture-executor.js +71 -7
  74. package/dist/core/feature-fixture-runner.js +53 -12
  75. package/dist/core/feature-fixtures.js +47 -14
  76. package/dist/core/feature-registry.js +28 -27
  77. package/dist/core/fsx.js +1 -0
  78. package/dist/core/harness-conflicts.js +18 -9
  79. package/dist/core/hooks-runtime/code-pack-freshness-preflight.js +14 -8
  80. package/dist/core/hooks-runtime/hook-io.js +8 -4
  81. package/dist/core/hooks-runtime/light-turn.js +70 -0
  82. package/dist/core/hooks-runtime/naruto-decision-gate.js +184 -0
  83. package/dist/core/hooks-runtime/payload-signals.js +270 -0
  84. package/dist/core/hooks-runtime/stop-repeat-guard.js +89 -0
  85. package/dist/core/hooks-runtime/team-digest.js +0 -1
  86. package/dist/core/hooks-runtime/tool-output-quarantine.js +93 -0
  87. package/dist/core/hooks-runtime.js +438 -354
  88. package/dist/core/init/skills.js +16 -30
  89. package/dist/core/init.js +144 -36
  90. package/dist/core/managed-assets/managed-assets-manifest.js +566 -10
  91. package/dist/core/mission.js +24 -1
  92. package/dist/core/pipeline-internals/runtime-core.js +570 -177
  93. package/dist/core/pipeline-internals/runtime-gates.js +174 -48
  94. package/dist/core/preflight/parallel-preflight-engine.js +66 -4
  95. package/dist/core/proof/route-adapter.js +1 -1
  96. package/dist/core/proof/route-proof-gate.js +7 -2
  97. package/dist/core/proof/selftest-proof-fixtures.js +3 -5
  98. package/dist/core/proof-field.js +1 -1
  99. package/dist/core/provider/model-router.js +42 -31
  100. package/dist/core/recallpulse/policy.js +12 -28
  101. package/dist/core/recallpulse.js +11 -6
  102. package/dist/core/release/npm-pack-proof.js +247 -0
  103. package/dist/core/release/package-dist-snapshot.js +151 -0
  104. package/dist/core/release/package-size-budget.js +4 -1
  105. package/dist/core/release/release-authorization-snapshot.js +115 -0
  106. package/dist/core/release/release-gate-affected-selector.js +14 -2
  107. package/dist/core/release/release-gate-cache-v2.js +30 -0
  108. package/dist/core/release/release-gate-contract.js +161 -0
  109. package/dist/core/release/release-gate-dag.js +1 -0
  110. package/dist/core/release/release-real-contract.js +90 -10
  111. package/dist/core/release-parallel-full-coverage.js +31 -143
  112. package/dist/core/research/claim-evidence-matrix.js +41 -6
  113. package/dist/core/research/experiment-plan.js +14 -10
  114. package/dist/core/research/falsification.js +9 -2
  115. package/dist/core/research/implementation-blueprint-densifier.js +82 -60
  116. package/dist/core/research/implementation-blueprint.js +32 -26
  117. package/dist/core/research/mock-result.js +122 -11
  118. package/dist/core/research/replication-pack.js +15 -8
  119. package/dist/core/research/research-adversarial-review.js +1068 -0
  120. package/dist/core/research/research-claim-builder.js +69 -17
  121. package/dist/core/research/research-claim-synthesizer.js +343 -0
  122. package/dist/core/research/research-cycle-runner.js +53 -3
  123. package/dist/core/research/research-falsification-runner.js +176 -0
  124. package/dist/core/research/research-final-reviewer.js +44 -125
  125. package/dist/core/research/research-plan-markdown.js +123 -0
  126. package/dist/core/research/research-realistic-report.js +14 -6
  127. package/dist/core/research/research-review-artifact-digest.js +66 -0
  128. package/dist/core/research/research-source-evidence.js +144 -0
  129. package/dist/core/research/research-source-layer-catalog.js +68 -0
  130. package/dist/core/research/research-source-ledger-merge.js +250 -12
  131. package/dist/core/research/research-source-shards.js +26 -70
  132. package/dist/core/research/research-stage-runner.js +237 -247
  133. package/dist/core/research/research-super-search.js +188 -0
  134. package/dist/core/research/research-synthesis-prompt.js +20 -1
  135. package/dist/core/research/research-synthesis-writer.js +86 -5
  136. package/dist/core/research/research-work-graph.js +25 -18
  137. package/dist/core/research/source-quality-report.js +21 -0
  138. package/dist/core/research.js +424 -390
  139. package/dist/core/retention.js +70 -2
  140. package/dist/core/routes/constants.js +1 -1
  141. package/dist/core/routes/dollar-manifest-lite.js +11 -11
  142. package/dist/core/routes.js +149 -69
  143. package/dist/core/runtime/task-profile.js +66 -0
  144. package/dist/core/runtime/verification-budget.js +12 -0
  145. package/dist/core/security/high-risk-contracts.js +6 -6
  146. package/dist/core/stop-gate/stop-gate-check.js +49 -12
  147. package/dist/core/subagents/agent-catalog.js +287 -0
  148. package/dist/core/subagents/model-policy.js +259 -0
  149. package/dist/core/subagents/naruto-help-contract.js +47 -0
  150. package/dist/core/subagents/official-subagent-config.js +488 -0
  151. package/dist/core/subagents/official-subagent-preparation.js +297 -0
  152. package/dist/core/subagents/official-subagent-prompt.js +159 -0
  153. package/dist/core/subagents/official-subagent-runner.js +129 -0
  154. package/dist/core/subagents/subagent-evidence.js +663 -0
  155. package/dist/core/subagents/terminology.js +16 -0
  156. package/dist/core/subagents/thread-budget.js +21 -0
  157. package/dist/core/subagents/triwiki-attention.js +168 -0
  158. package/dist/core/super-search/runtime-helpers.js +173 -19
  159. package/dist/core/super-search/runtime.js +140 -15
  160. package/dist/core/super-search/source-records.js +49 -8
  161. package/dist/core/team-review-policy.js +15 -0
  162. package/dist/core/triwiki/code-pack-head-freshness.js +291 -0
  163. package/dist/core/triwiki/triwiki-module-card.js +1 -1
  164. package/dist/core/triwiki-wrongness/wrongness-ledger.js +9 -2
  165. package/dist/core/version-manager.js +51 -1
  166. package/dist/core/version.js +1 -1
  167. package/dist/core/work-order-ledger.js +40 -1
  168. package/dist/core/zellij/zellij-fake-adapter.js +3 -1
  169. package/dist/core/zellij/zellij-launcher.js +49 -10
  170. package/dist/core/zellij/zellij-layout-builder.js +14 -5
  171. package/dist/core/zellij/zellij-official-subagent-activity.js +477 -0
  172. package/dist/core/zellij/zellij-official-subagent-telemetry.js +264 -0
  173. package/dist/core/zellij/zellij-pane-proof.js +9 -1
  174. package/dist/core/zellij/zellij-slot-pane-renderer.js +17 -5
  175. package/dist/core/zellij/zellij-slot-telemetry.js +15 -3
  176. package/dist/core/zellij/zellij-ui-mode.js +1 -1
  177. package/dist/core/zellij/zellij-update.js +14 -1
  178. package/dist/core/zellij/zellij-viewport-binder.js +3 -0
  179. package/dist/scripts/agent-role-config-repair-check.js +14 -16
  180. package/dist/scripts/architecture-guard-check.js +8 -1
  181. package/dist/scripts/canonical-test-runner.js +7 -1
  182. package/dist/scripts/check-route-modularity.js +0 -1
  183. package/dist/scripts/check-ts-contracts.js +1 -1
  184. package/dist/scripts/codex-control-all-pipelines-check.js +7 -3
  185. package/dist/scripts/codex-control-tool-output-continuity-audit-check.js +27 -0
  186. package/dist/scripts/codex-lb-fast-mode-truth-check.js +11 -1
  187. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +9 -2
  188. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +13 -2
  189. package/dist/scripts/codex-native-agent-role-content-check.js +27 -7
  190. package/dist/scripts/codex-native-repair-transaction-check.js +3 -1
  191. package/dist/scripts/codex-sdk-all-pipelines-check.js +7 -4
  192. package/dist/scripts/codex-sdk-backend-router-check.js +2 -0
  193. package/dist/scripts/codex-sdk-research-pipeline-check.js +30 -8
  194. package/dist/scripts/db-route-materialization-check.js +33 -0
  195. package/dist/scripts/docs-truthfulness-check.js +3 -1
  196. package/dist/scripts/doctor-codex-startup-repair-check.js +12 -15
  197. package/dist/scripts/doctor-fix-production-blackbox.js +12 -2
  198. package/dist/scripts/doctor-startup-config-repair-blackbox.js +8 -2
  199. package/dist/scripts/doctor-startup-config-repair-check.js +12 -2
  200. package/dist/scripts/install-update-preserves-config-check.js +6 -1
  201. package/dist/scripts/lib/codex-sdk-gate-lib.js +4 -0
  202. package/dist/scripts/mad-sks-app-ui-no-mutation-check.js +0 -1
  203. package/dist/scripts/managed-role-manifest-parity-check.js +5 -4
  204. package/dist/scripts/naruto-codex-e2e-check.js +14 -7
  205. package/dist/scripts/naruto-shadow-clone-swarm-check.js +4 -201
  206. package/dist/scripts/npm-publish-performance-check.js +20 -12
  207. package/dist/scripts/official-subagent-workflow-check.js +145 -0
  208. package/dist/scripts/package-published-contract-check.js +6 -29
  209. package/dist/scripts/packlist-performance-check.js +18 -21
  210. package/dist/scripts/parallel-verification-engine-check.js +2 -2
  211. package/dist/scripts/prepublish-release-check-or-fast.js +23 -86
  212. package/dist/scripts/python-codex-sdk-all-pipelines-check.js +8 -0
  213. package/dist/scripts/release-affected-selector-check.js +74 -1
  214. package/dist/scripts/release-check-stamp.js +153 -248
  215. package/dist/scripts/release-dag-full-coverage-check.js +7 -15
  216. package/dist/scripts/release-dynamic-presets-check.js +2 -1
  217. package/dist/scripts/release-gate-dag-runner-check.js +0 -1
  218. package/dist/scripts/release-gate-dag-runner.js +12 -1
  219. package/dist/scripts/release-gate-existence-audit.js +1 -2
  220. package/dist/scripts/release-parallel-full-coverage-check.js +12 -6
  221. package/dist/scripts/release-parallel-speed-budget-check.js +18 -2
  222. package/dist/scripts/release-real-check.js +26 -4
  223. package/dist/scripts/release-registry-check.js +61 -16
  224. package/dist/scripts/research-blueprint-densifier-check.js +2 -2
  225. package/dist/scripts/research-real-cycle-no-legacy-final-md-check.js +11 -7
  226. package/dist/scripts/sizecheck.js +2 -2
  227. package/dist/scripts/sks-3-1-4-directive-check-lib.js +8 -2
  228. package/dist/scripts/sks-3-1-5-directive-check-lib.js +3 -2
  229. package/dist/scripts/sks-3-1-6-directive-check-lib.js +5 -3
  230. package/dist/scripts/sks-menubar-install-check.js +14 -1
  231. package/dist/scripts/super-search-provider-interface-check.js +31 -18
  232. package/dist/scripts/trust-fixture-check.js +32 -10
  233. package/dist/scripts/wrongness-fixture-check.js +1 -1
  234. package/dist/scripts/zellij-layout-valid-check.js +5 -5
  235. package/dist/scripts/zellij-spawn-on-demand-layout-check.js +3 -3
  236. package/docs/demo.tape +1 -1
  237. package/infra-harness-gates.json +1486 -0
  238. package/package.json +15 -10
  239. package/release-gates.v2.json +4060 -0
  240. package/runtime-required-scripts.json +9 -0
  241. package/dist/commands/db.js +0 -6
  242. package/dist/core/commands/db-command.js +0 -146
  243. package/dist/core/research/prompt.js +0 -15
  244. package/dist/scripts/codex-0139-feature-probes-check.js +0 -30
  245. package/dist/scripts/codex-0139-marketplace-source-check.js +0 -13
  246. package/dist/scripts/codex-control-tool-call-sequence-repair-check.js +0 -14
@@ -0,0 +1,1068 @@
1
+ import fsp from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { nowIso, randomId, readJson, readText, sha256, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
4
+ import { buildOfficialSubagentPrompt } from '../subagents/official-subagent-prompt.js';
5
+ import { codexAppSessionKey, detectCodexAppSession, runOfficialSubagentWorkflow } from '../subagents/official-subagent-runner.js';
6
+ import { readOfficialSubagentConfig } from '../subagents/official-subagent-config.js';
7
+ import { SUBAGENT_EVENT_LOG_FILENAME, SUBAGENT_EVIDENCE_FILENAME, SUBAGENT_PARENT_SUMMARY_FILENAME, bindTrustworthySubagentParentSummaryToRun, normalizeSubagentParentSummary, persistOrReuseTrustworthySubagentParentSummary, readSubagentEvents, writeSubagentEvidence } from '../subagents/subagent-evidence.js';
8
+ import { THINKING_SUBAGENT_MODEL, SUBAGENT_EFFORT } from '../subagents/model-policy.js';
9
+ import { RESEARCH_AGENT_COUNCIL, RESEARCH_GENIUS_SUMMARY_ARTIFACT, RESEARCH_REVIEWER_CONFIG_ARTIFACT, RESEARCH_REVIEWER_CUSTOM_AGENT, researchAgentAgentName, researchPaperArtifactForPlan } from '../research.js';
10
+ import { normalizeResearchSynthesisOutput } from './research-synthesis-writer.js';
11
+ import { buildResearchReviewArtifactDigest, validateResearchReviewArtifactDigest } from './research-review-artifact-digest.js';
12
+ import { eligibleResearchSourceIdSet } from './research-source-evidence.js';
13
+ export const RESEARCH_ADVERSARIAL_PLAN_ARTIFACT = 'research-adversarial-plan.json';
14
+ export const RESEARCH_ADVERSARIAL_REVIEW_ARTIFACT = 'research-adversarial-review.json';
15
+ export const RESEARCH_REVISION_LEDGER_ARTIFACT = 'research-revision-ledger.json';
16
+ export const RESEARCH_CONVERGENCE_GATE_ARTIFACT = 'research-adversarial-convergence.json';
17
+ export const RESEARCH_HONEST_MODE_ARTIFACT = 'research-honest-mode.json';
18
+ export async function runResearchAdversarialReviewLoop(input) {
19
+ const maxCycles = Math.max(1, Math.min(3, Math.floor(Number(input.maxCycles || 3))));
20
+ const config = input.mock ? null : await readOfficialSubagentConfig(input.root);
21
+ const configuredThreads = Math.max(1, Math.floor(Number(input.maxThreads || config?.maxThreads || RESEARCH_AGENT_COUNCIL.length)));
22
+ const maxThreads = Math.min(RESEARCH_AGENT_COUNCIL.length, configuredThreads);
23
+ const executionClass = input.mock ? 'mock_fixture' : 'real';
24
+ const modelPolicyEvidence = input.mock ? mockResearchModelPolicyEvidence() : await verifyResearchReviewerRoleConfig(input.root);
25
+ const plan = buildResearchAdversarialPlan(input.plan, maxCycles, maxThreads, config, modelPolicyEvidence);
26
+ await writeJsonAtomic(path.join(input.dir, RESEARCH_ADVERSARIAL_PLAN_ARTIFACT), plan);
27
+ const reviewCycles = [];
28
+ const revisions = [];
29
+ const deadlineMs = Number.isFinite(Number(input.deadlineMs))
30
+ ? Number(input.deadlineMs)
31
+ : Date.now() + Math.max(1, Number(input.timeoutMs || 1));
32
+ const runtimeBlockers = [];
33
+ const configBlockers = (config?.blockers || []).map((blocker) => `official_subagent_config:${blocker}`);
34
+ const preliminaryBlockers = unique([...configBlockers, ...modelPolicyEvidence.blockers, ...normalizeStrings(input.preliminaryBlockers)]);
35
+ if (preliminaryBlockers.length) {
36
+ const gate = await finalizeResearchAdversarialArtifacts(input, plan, reviewCycles, revisions, executionClass, preliminaryBlockers);
37
+ return { ok: false, gate, plan, review_cycles: reviewCycles, revisions };
38
+ }
39
+ for (let cycle = 1; cycle <= maxCycles; cycle += 1) {
40
+ if (remainingTimeoutMs(deadlineMs) <= 0) {
41
+ runtimeBlockers.push('research_cycle_timeout_exceeded');
42
+ break;
43
+ }
44
+ const reviewArtifacts = await buildResearchReviewArtifactDigest(input.dir, input.plan);
45
+ const review = input.mock
46
+ ? await mockReviewCycle(input, cycle, reviewArtifacts)
47
+ : await runOfficialReviewCycle({ ...input, timeoutMs: remainingTimeoutMs(deadlineMs), deadlineMs }, cycle, maxThreads, reviewArtifacts);
48
+ reviewCycles.push(review);
49
+ await writeJsonAtomic(path.join(input.dir, 'research', 'adversarial', `cycle-${cycle}`, 'review.json'), review);
50
+ const currentReviewArtifacts = await buildResearchReviewArtifactDigest(input.dir, input.plan);
51
+ const convergence = evaluateReviewCycle(review, await sourceIdSet(input.dir, executionClass), currentReviewArtifacts);
52
+ if (remainingTimeoutMs(deadlineMs) <= 0)
53
+ runtimeBlockers.push('research_cycle_timeout_exceeded');
54
+ if (convergence.ok)
55
+ break;
56
+ if (cycle >= maxCycles || !convergence.revisable)
57
+ break;
58
+ if (remainingTimeoutMs(deadlineMs) <= 0)
59
+ break;
60
+ const revision = input.mock
61
+ ? mockRevisionCycle(cycle, convergence.open_objection_ids)
62
+ : await runOfficialRevisionCycle({ ...input, timeoutMs: remainingTimeoutMs(deadlineMs), deadlineMs }, cycle, maxThreads, convergence.open_objection_ids);
63
+ revisions.push(revision);
64
+ await writeJsonAtomic(path.join(input.dir, 'research', 'adversarial', `cycle-${cycle}`, 'revision.json'), revision);
65
+ if (revision.ok !== true)
66
+ break;
67
+ await syncSynthesisAfterRevision(input.dir, input.plan);
68
+ }
69
+ const gate = await finalizeResearchAdversarialArtifacts(input, plan, reviewCycles, revisions, executionClass, runtimeBlockers);
70
+ return { ok: gate.passed === true, gate, plan, review_cycles: reviewCycles, revisions };
71
+ }
72
+ export function parseOfficialReviewParentSummary(raw) {
73
+ const normalizedParent = normalizeSubagentParentSummary(raw);
74
+ const blockers = [...normalizedParent.blockers];
75
+ const parent = normalizedParent.raw;
76
+ if (!parent || !normalizedParent.trustworthy) {
77
+ return {
78
+ ok: false,
79
+ parent: parent || null,
80
+ reviewers: [],
81
+ blockers: unique(['official_subagent_parent_summary_invalid', ...blockers])
82
+ };
83
+ }
84
+ if (normalizedParent.status !== 'completed')
85
+ blockers.push(`official_subagent_parent_status:${String(parent.status || 'missing')}`);
86
+ const rows = Array.isArray(parent.thread_outcomes) ? parent.thread_outcomes : [];
87
+ if (!rows.length)
88
+ blockers.push('official_subagent_thread_outcomes_missing');
89
+ const reviewers = [];
90
+ for (const row of rows) {
91
+ const parsed = parseJsonObject(row?.summary);
92
+ if (!parsed) {
93
+ blockers.push(`reviewer_outcome_unstructured:${String(row?.thread_id || 'unknown')}`);
94
+ continue;
95
+ }
96
+ const shapeBlockers = validateReviewerOutcomeShape(parsed, row);
97
+ if (shapeBlockers.length) {
98
+ blockers.push(...shapeBlockers);
99
+ continue;
100
+ }
101
+ const normalized = normalizeReviewerOutcome(parsed, row);
102
+ reviewers.push(normalized);
103
+ }
104
+ for (const duplicate of duplicates(reviewers.map((reviewer) => reviewer.thread_id))) {
105
+ blockers.push(`reviewer_thread_duplicate:${duplicate || 'missing'}`);
106
+ }
107
+ return { ok: blockers.length === 0, parent, reviewers, blockers: unique(blockers) };
108
+ }
109
+ export function evaluateReviewCycle(review, knownSourceIds = new Set(), currentReviewArtifacts = null) {
110
+ const reviewers = Array.isArray(review?.reviewers) ? review.reviewers : [];
111
+ const blockers = [...normalizeStrings(review?.blockers)];
112
+ const expectedIds = RESEARCH_AGENT_COUNCIL.map((agent) => String(agent.id));
113
+ const personaIds = reviewers.map((reviewer) => reviewer.persona_id);
114
+ const threadIds = reviewers.map((reviewer) => reviewer.thread_id);
115
+ const recordedReviewArtifacts = review?.review_artifacts;
116
+ const expectedArtifactBundle = String(recordedReviewArtifacts?.bundle_sha256 || '');
117
+ if (currentReviewArtifacts)
118
+ blockers.push(...validateResearchReviewArtifactDigest(recordedReviewArtifacts, currentReviewArtifacts));
119
+ else if (!expectedArtifactBundle)
120
+ blockers.push('research_review_artifact_bundle_sha256_missing');
121
+ for (const id of expectedIds) {
122
+ if (!personaIds.includes(id))
123
+ blockers.push(`structured_reviewer_missing:${id}`);
124
+ }
125
+ for (const duplicate of duplicates(personaIds))
126
+ blockers.push(`structured_reviewer_duplicate:${duplicate}`);
127
+ for (const duplicate of duplicates(threadIds))
128
+ blockers.push(`reviewer_thread_duplicate:${duplicate || 'missing'}`);
129
+ for (const reviewer of reviewers) {
130
+ if (reviewer.schema !== 'sks.research-adversarial-reviewer-outcome.v1')
131
+ blockers.push(`reviewer_schema_invalid:${reviewer.persona_id || 'unknown'}`);
132
+ if (!reviewer.thread_id)
133
+ blockers.push(`reviewer_thread_id_missing:${reviewer.persona_id || 'unknown'}`);
134
+ if (reviewer.thread_status !== 'completed')
135
+ blockers.push(`reviewer_thread_not_completed:${reviewer.persona_id}`);
136
+ if (!reviewer.strongest_challenge.trim())
137
+ blockers.push(`reviewer_challenge_missing:${reviewer.persona_id}`);
138
+ if (!reviewer.eureka.idea.trim() || reviewer.eureka.exclamation !== 'Eureka!')
139
+ blockers.push(`reviewer_eureka_missing:${reviewer.persona_id}`);
140
+ if (!reviewer.eureka.source_ids.length)
141
+ blockers.push(`reviewer_eureka_evidence_missing:${reviewer.persona_id}`);
142
+ if (!reviewer.evidence_source_ids.length)
143
+ blockers.push(`reviewer_evidence_missing:${reviewer.persona_id}`);
144
+ if (!reviewer.falsifiers.length)
145
+ blockers.push(`reviewer_falsifiers_missing:${reviewer.persona_id}`);
146
+ if (!reviewer.cheap_probes.length)
147
+ blockers.push(`reviewer_cheap_probes_missing:${reviewer.persona_id}`);
148
+ if (!reviewer.review_artifact_bundle_sha256)
149
+ blockers.push(`reviewer_artifact_bundle_sha256_missing:${reviewer.persona_id}`);
150
+ if (expectedArtifactBundle && reviewer.review_artifact_bundle_sha256 !== expectedArtifactBundle)
151
+ blockers.push(`reviewer_artifact_bundle_sha256_mismatch:${reviewer.persona_id}`);
152
+ for (const sourceId of reviewer.evidence_source_ids) {
153
+ if (!knownSourceIds.has(sourceId))
154
+ blockers.push(`reviewer_evidence_source_unknown:${reviewer.persona_id}:${sourceId}`);
155
+ }
156
+ for (const sourceId of reviewer.eureka.source_ids) {
157
+ if (!knownSourceIds.has(sourceId))
158
+ blockers.push(`reviewer_eureka_source_unknown:${reviewer.persona_id}:${sourceId}`);
159
+ }
160
+ const objections = [...reviewer.critical_objections, ...reviewer.major_objections, ...reviewer.minor_objections];
161
+ for (const objection of objections) {
162
+ if (!objection.id || !objection.reason || !objection.required_revision)
163
+ blockers.push(`reviewer_objection_invalid:${reviewer.persona_id}`);
164
+ for (const sourceId of objection.source_ids) {
165
+ if (!knownSourceIds.has(sourceId))
166
+ blockers.push(`reviewer_objection_source_unknown:${reviewer.persona_id}:${sourceId}`);
167
+ }
168
+ }
169
+ if (reviewer.verdict === 'approve' && (objections.length || reviewer.required_revisions.length)) {
170
+ blockers.push(`reviewer_approve_with_unresolved_revision:${reviewer.persona_id}`);
171
+ }
172
+ if (reviewer.verdict !== 'approve' && !objections.length && !reviewer.required_revisions.length) {
173
+ blockers.push(`reviewer_nonapproval_without_objection:${reviewer.persona_id}`);
174
+ }
175
+ }
176
+ const openObjections = reviewers.flatMap((reviewer) => [
177
+ ...reviewer.critical_objections,
178
+ ...reviewer.major_objections,
179
+ ...reviewer.minor_objections,
180
+ ...reviewer.required_revisions.map((revision, index) => ({
181
+ id: `${reviewer.persona_id}-required-${index + 1}`,
182
+ severity: 'major',
183
+ claim_ids: [],
184
+ source_ids: reviewer.evidence_source_ids,
185
+ reason: revision,
186
+ required_revision: revision
187
+ }))
188
+ ]);
189
+ const allApproved = reviewers.length === expectedIds.length && reviewers.every((reviewer) => reviewer.verdict === 'approve');
190
+ if (!allApproved)
191
+ blockers.push('adversarial_review_not_unanimously_approved');
192
+ if (openObjections.some((objection) => objection.severity === 'critical'))
193
+ blockers.push('critical_objections_unresolved');
194
+ if (openObjections.some((objection) => objection.severity === 'major'))
195
+ blockers.push('major_objections_unresolved');
196
+ if (openObjections.some((objection) => objection.severity === 'minor'))
197
+ blockers.push('minor_objections_unresolved');
198
+ if (openObjections.length)
199
+ blockers.push('open_objections_unresolved');
200
+ const convergenceOnly = new Set([
201
+ 'adversarial_review_not_unanimously_approved',
202
+ 'critical_objections_unresolved',
203
+ 'major_objections_unresolved',
204
+ 'minor_objections_unresolved',
205
+ 'open_objections_unresolved'
206
+ ]);
207
+ const structuralBlockers = unique(blockers).filter((blocker) => !convergenceOnly.has(blocker) && !blocker.startsWith('reviewer_approve_with_unresolved_revision:'));
208
+ return {
209
+ ok: unique(blockers).length === 0,
210
+ blockers: unique(blockers),
211
+ reviewers: reviewers.length,
212
+ all_approved: allApproved,
213
+ critical_objections: openObjections.filter((objection) => objection.severity === 'critical').length,
214
+ open_objections: openObjections.length,
215
+ open_objection_ids: unique(openObjections.map((objection) => objection.id)),
216
+ revisable: structuralBlockers.length === 0
217
+ && reviewers.length === expectedIds.length
218
+ && new Set(threadIds).size === expectedIds.length
219
+ && reviewers.every((reviewer) => reviewer.thread_status === 'completed')
220
+ && openObjections.length > 0
221
+ };
222
+ }
223
+ export function buildResearchAdversarialPlan(plan, maxCycles = 3, maxThreads = RESEARCH_AGENT_COUNCIL.length, config = null, modelPolicyEvidence = null) {
224
+ return {
225
+ schema: 'sks.research-adversarial-plan.v1',
226
+ mission_id: String(plan?.mission_id || ''),
227
+ generated_at: nowIso(),
228
+ workflow: 'official_codex_subagent',
229
+ max_depth: 1,
230
+ max_threads: maxThreads,
231
+ max_review_cycles: maxCycles,
232
+ reviewer_count: RESEARCH_AGENT_COUNCIL.length,
233
+ reviewers: RESEARCH_AGENT_COUNCIL.map((agent) => ({
234
+ id: agent.id,
235
+ display_name: researchAgentAgentName(agent),
236
+ persona: agent.persona,
237
+ persona_boundary: agent.persona_boundary,
238
+ custom_agent: RESEARCH_REVIEWER_CUSTOM_AGENT,
239
+ model_policy: `${THINKING_SUBAGENT_MODEL} ${SUBAGENT_EFFORT}`,
240
+ model_policy_source: modelPolicyEvidence?.source || RESEARCH_REVIEWER_CONFIG_ARTIFACT,
241
+ model_policy_sha256: modelPolicyEvidence?.sha256 || null
242
+ })),
243
+ convergence_policy: 'three distinct evidence-correlated official threads completed; every exact-schema verdict approve; zero critical, major, minor, or required revisions',
244
+ revision_policy: 'bounded mission-artifact-only revision followed by a fresh independent review cycle',
245
+ guarantees: {
246
+ genius_level: false,
247
+ novelty: false,
248
+ publication_acceptance: false
249
+ },
250
+ config_warnings: config?.warnings || [],
251
+ config_blockers: config?.blockers || [],
252
+ model_policy_evidence: modelPolicyEvidence || null
253
+ };
254
+ }
255
+ async function runOfficialReviewCycle(input, cycle, maxThreads, reviewArtifacts) {
256
+ const slices = RESEARCH_AGENT_COUNCIL.map((agent) => ({
257
+ id: String(agent.id),
258
+ title: `${researchAgentAgentName(agent)} adversarial paper review`,
259
+ kind: 'expert',
260
+ agent: RESEARCH_REVIEWER_CUSTOM_AGENT,
261
+ readOnly: true,
262
+ paths: researchReviewArtifacts(input.plan),
263
+ description: reviewerTaskDescription(agent, cycle, reviewArtifacts.bundle_sha256)
264
+ }));
265
+ const prompt = buildOfficialSubagentPrompt({
266
+ goal: reviewGoal(input, cycle, reviewArtifacts),
267
+ slices,
268
+ maxThreads,
269
+ requestedSubagents: slices.length,
270
+ decompositionStatus: 'ready'
271
+ });
272
+ const lifecyclePlan = await prepareResearchSubagentRun(input, {
273
+ phase: 'review',
274
+ cycle,
275
+ requested_subagents: slices.length,
276
+ max_threads: maxThreads,
277
+ slices,
278
+ review_artifacts: reviewArtifacts
279
+ });
280
+ const run = await runWorkflow(input, {
281
+ root: input.root,
282
+ prompt,
283
+ requestedSubagents: slices.length,
284
+ maxThreads,
285
+ appSession: input.appSession ?? detectCodexAppSession(),
286
+ missionId: String(input.plan?.mission_id || ''),
287
+ sessionKey: input.sessionKey ?? codexAppSessionKey(),
288
+ timeoutMs: input.timeoutMs,
289
+ env: process.env
290
+ });
291
+ const lifecycle = await finalizeResearchSubagentRun(input, lifecyclePlan, run);
292
+ const parsed = parseOfficialReviewParentSummary(lifecycle.parent_summary);
293
+ const parsedThreadIds = parsed.reviewers.map((reviewer) => reviewer.thread_id).sort();
294
+ const completedThreadIds = [...(lifecycle.evidence?.completed_thread_ids || [])].sort();
295
+ const lifecycleThreadCorrelation = parsedThreadIds.length === completedThreadIds.length
296
+ && parsedThreadIds.every((threadId, index) => threadId === completedThreadIds[index]);
297
+ return {
298
+ schema: 'sks.research-adversarial-review-cycle.v1',
299
+ cycle,
300
+ execution_class: 'real',
301
+ reviewed_at: nowIso(),
302
+ workflow: sanitizeWorkflowRun(run),
303
+ workflow_run_id: lifecyclePlan.workflow_run_id,
304
+ review_artifacts: reviewArtifacts,
305
+ subagent_evidence: lifecycle.evidence,
306
+ reviewers: parsed.reviewers,
307
+ blockers: unique([
308
+ ...(run?.status === 'parent_completed' ? [] : [`official_subagent_review_status:${String(run?.status || 'missing')}`]),
309
+ ...(run?.prepared === true ? ['official_subagent_review_preparation_only'] : []),
310
+ ...(lifecycle.evidence?.ok === true ? [] : (lifecycle.evidence?.blockers || ['official_subagent_review_evidence_missing'])),
311
+ ...(lifecycleThreadCorrelation ? [] : ['official_subagent_review_thread_correlation_failed']),
312
+ ...parsed.blockers
313
+ ])
314
+ };
315
+ }
316
+ async function mockReviewCycle(input, cycle, reviewArtifacts) {
317
+ const sourceIds = [...await sourceIdSet(input.dir, 'mock_fixture')];
318
+ const reviewers = RESEARCH_AGENT_COUNCIL.map((agent, index) => ({
319
+ schema: 'sks.research-adversarial-reviewer-outcome.v1',
320
+ persona_id: String(agent.id),
321
+ verdict: 'approve',
322
+ strongest_challenge: `${researchAgentAgentName(agent)} fixture attempts to falsify unsupported synthesis, source gaps, and replication weakness.`,
323
+ evidence_source_ids: sourceIds.slice(index, index + 2).length ? sourceIds.slice(index, index + 2) : sourceIds.slice(0, 1),
324
+ critical_objections: [],
325
+ major_objections: [],
326
+ minor_objections: [],
327
+ required_revisions: [],
328
+ eureka: {
329
+ exclamation: 'Eureka!',
330
+ idea: `${researchAgentAgentName(agent)} fixture insight remains explicitly mock-only and source-bound.`,
331
+ source_ids: sourceIds.slice(index, index + 1).length ? sourceIds.slice(index, index + 1) : sourceIds.slice(0, 1)
332
+ },
333
+ falsifiers: ['Remove cited source rows or leave a critical objection unresolved.'],
334
+ cheap_probes: ['Run the adversarial convergence unit test.'],
335
+ confidence: 'high',
336
+ review_artifact_bundle_sha256: reviewArtifacts.bundle_sha256,
337
+ thread_id: `mock-review-${cycle}-${agent.id}`,
338
+ thread_status: 'completed'
339
+ }));
340
+ return {
341
+ schema: 'sks.research-adversarial-review-cycle.v1',
342
+ cycle,
343
+ execution_class: 'mock_fixture',
344
+ reviewed_at: nowIso(),
345
+ workflow: { status: 'mock_fixture', workflow: 'official_codex_subagent_contract_fixture' },
346
+ review_artifacts: reviewArtifacts,
347
+ reviewers,
348
+ blockers: []
349
+ };
350
+ }
351
+ async function runOfficialRevisionCycle(input, cycle, maxThreads, objectionIds) {
352
+ const before = await manuscriptHashes(input.dir, input.plan);
353
+ const slice = {
354
+ id: `research_revision_${cycle}`,
355
+ title: `Evidence-bound manuscript revision cycle ${cycle}`,
356
+ kind: 'expert',
357
+ agent: 'research_synthesizer',
358
+ readOnly: false,
359
+ paths: researchReviewArtifacts(input.plan),
360
+ description: [
361
+ `Resolve only the objections recorded for review cycle ${cycle}: ${objectionIds.join(', ')}.`,
362
+ 'Edit only mission-local research-report.md, the dated research paper artifact, and research-synthesis-output.json.',
363
+ 'Do not invent evidence. If an objection needs unavailable evidence, downgrade/remove the claim or return blocked.',
364
+ 'Your thread outcome summary must itself be JSON: {"schema":"sks.research-revision-outcome.v1","status":"revised|blocked","addressed_objection_ids":[],"changed_artifacts":[],"remaining_blockers":[]}.'
365
+ ].join(' ')
366
+ };
367
+ const prompt = buildOfficialSubagentPrompt({
368
+ goal: `Revise the Research manuscript in .sneakoscope/missions/${input.plan?.mission_id || ''}/ using review cycle ${cycle}. Preserve source IDs and falsifiability; never claim guaranteed genius, novelty, breakthrough, or publication acceptance.`,
369
+ slices: [slice],
370
+ maxThreads: Math.min(1, maxThreads),
371
+ requestedSubagents: 1,
372
+ decompositionStatus: 'ready'
373
+ });
374
+ const lifecyclePlan = await prepareResearchSubagentRun(input, {
375
+ phase: 'revision',
376
+ cycle,
377
+ requested_subagents: 1,
378
+ max_threads: 1,
379
+ slices: [slice]
380
+ });
381
+ const run = await runWorkflow(input, {
382
+ root: input.root,
383
+ prompt,
384
+ requestedSubagents: 1,
385
+ maxThreads: 1,
386
+ appSession: input.appSession ?? detectCodexAppSession(),
387
+ missionId: String(input.plan?.mission_id || ''),
388
+ sessionKey: input.sessionKey ?? codexAppSessionKey(),
389
+ timeoutMs: input.timeoutMs,
390
+ env: process.env
391
+ });
392
+ const lifecycle = await finalizeResearchSubagentRun(input, lifecyclePlan, run);
393
+ const parent = parseJsonObject(lifecycle.parent_summary);
394
+ const outcomeRow = Array.isArray(parent?.thread_outcomes) ? parent.thread_outcomes[0] : null;
395
+ const outcome = parseJsonObject(outcomeRow?.summary);
396
+ const after = await manuscriptHashes(input.dir, input.plan);
397
+ const changedArtifacts = Object.keys(after).filter((artifact) => after[artifact] !== before[artifact]);
398
+ const blockers = [
399
+ ...(run?.status === 'parent_completed' ? [] : [`official_subagent_revision_status:${String(run?.status || 'missing')}`]),
400
+ ...(run?.prepared === true ? ['official_subagent_revision_preparation_only'] : []),
401
+ ...(lifecycle.evidence?.ok === true ? [] : (lifecycle.evidence?.blockers || ['official_subagent_revision_evidence_missing'])),
402
+ ...(parent?.status === 'completed' ? [] : ['official_subagent_revision_parent_not_completed']),
403
+ ...(outcome?.schema === 'sks.research-revision-outcome.v1' ? [] : ['research_revision_outcome_unstructured']),
404
+ ...(outcome?.status === 'revised' ? [] : normalizeStrings(outcome?.remaining_blockers).length ? normalizeStrings(outcome.remaining_blockers) : ['research_revision_not_completed']),
405
+ ...(changedArtifacts.length ? [] : ['research_revision_artifacts_unchanged'])
406
+ ];
407
+ return {
408
+ schema: 'sks.research-revision-cycle.v1',
409
+ cycle,
410
+ revised_at: nowIso(),
411
+ ok: unique(blockers).length === 0,
412
+ objection_ids: objectionIds,
413
+ addressed_objection_ids: normalizeStrings(outcome?.addressed_objection_ids),
414
+ changed_artifacts: changedArtifacts,
415
+ before,
416
+ after,
417
+ workflow: sanitizeWorkflowRun(run),
418
+ workflow_run_id: lifecyclePlan.workflow_run_id,
419
+ subagent_evidence: lifecycle.evidence,
420
+ blockers: unique(blockers)
421
+ };
422
+ }
423
+ function mockRevisionCycle(cycle, objectionIds) {
424
+ return {
425
+ schema: 'sks.research-revision-cycle.v1',
426
+ cycle,
427
+ revised_at: nowIso(),
428
+ ok: false,
429
+ objection_ids: objectionIds,
430
+ addressed_objection_ids: [],
431
+ changed_artifacts: [],
432
+ workflow: { status: 'mock_fixture_no_revision_needed' },
433
+ blockers: ['mock_revision_not_expected']
434
+ };
435
+ }
436
+ async function finalizeResearchAdversarialArtifacts(input, plan, reviewCycles, revisions, executionClass, initialBlockers) {
437
+ const finalReview = reviewCycles.at(-1) || null;
438
+ const currentReviewArtifacts = await buildResearchReviewArtifactDigest(input.dir, input.plan);
439
+ const convergence = evaluateReviewCycle(finalReview, await sourceIdSet(input.dir, executionClass), currentReviewArtifacts);
440
+ const honest = await buildResearchHonestMode(input.dir, input.plan, executionClass);
441
+ const blockers = unique([
442
+ ...initialBlockers,
443
+ ...(finalReview ? [] : ['adversarial_review_cycle_missing']),
444
+ ...convergence.blockers,
445
+ ...(honest.ok ? [] : honest.blockers),
446
+ ...(reviewCycles.length > plan.max_review_cycles ? ['adversarial_review_cycle_budget_exceeded'] : [])
447
+ ]);
448
+ const gate = {
449
+ schema: 'sks.research-adversarial-convergence.v1',
450
+ checked_at: nowIso(),
451
+ execution_class: executionClass,
452
+ passed: blockers.length === 0,
453
+ official_subagent_workflow: true,
454
+ official_subagent_evidence_ok: executionClass === 'mock_fixture' ? true : finalReview?.subagent_evidence?.ok === true,
455
+ workflow_run_id: finalReview?.workflow_run_id || null,
456
+ reviewer_count_required: RESEARCH_AGENT_COUNCIL.length,
457
+ reviewer_count_observed: convergence.reviewers,
458
+ review_cycles: reviewCycles.length,
459
+ revision_cycles: revisions.length,
460
+ all_reviewers_approved: convergence.all_approved,
461
+ review_artifacts: finalReview?.review_artifacts || null,
462
+ review_artifact_bundle_sha256: finalReview?.review_artifacts?.bundle_sha256 || null,
463
+ current_artifact_bundle_sha256: currentReviewArtifacts.bundle_sha256,
464
+ review_artifact_hashes_ok: !convergence.blockers.some((blocker) => blocker.includes('artifact')),
465
+ unresolved_critical_objections: convergence.critical_objections,
466
+ unresolved_objections: convergence.open_objections,
467
+ honest_mode_ok: honest.ok,
468
+ genius_level_guaranteed: false,
469
+ novelty_guaranteed: false,
470
+ publication_acceptance_guaranteed: false,
471
+ reviewer_model_policy: {
472
+ custom_agent: RESEARCH_REVIEWER_CUSTOM_AGENT,
473
+ model: THINKING_SUBAGENT_MODEL,
474
+ reasoning_effort: SUBAGENT_EFFORT,
475
+ enforcement_source: plan?.model_policy_evidence?.source || RESEARCH_REVIEWER_CONFIG_ARTIFACT,
476
+ config_sha256: plan?.model_policy_evidence?.sha256 || null,
477
+ observed_model_exposed_by_hook: false
478
+ },
479
+ blockers
480
+ };
481
+ const ledger = {
482
+ schema: 'sks.research-adversarial-review-ledger.v1',
483
+ generated_at: nowIso(),
484
+ execution_class: executionClass,
485
+ review_cycles: reviewCycles,
486
+ final_cycle: finalReview?.cycle || null,
487
+ convergence_artifact: RESEARCH_CONVERGENCE_GATE_ARTIFACT,
488
+ blockers
489
+ };
490
+ const revisionLedger = {
491
+ schema: 'sks.research-revision-ledger.v1',
492
+ generated_at: nowIso(),
493
+ bounded_max_cycles: plan.max_review_cycles,
494
+ revisions,
495
+ blockers: revisions.flatMap((revision) => normalizeStrings(revision?.blockers))
496
+ };
497
+ await writeJsonAtomic(path.join(input.dir, RESEARCH_ADVERSARIAL_REVIEW_ARTIFACT), ledger);
498
+ await writeJsonAtomic(path.join(input.dir, RESEARCH_REVISION_LEDGER_ARTIFACT), revisionLedger);
499
+ await writeJsonAtomic(path.join(input.dir, RESEARCH_CONVERGENCE_GATE_ARTIFACT), gate);
500
+ await writeJsonAtomic(path.join(input.dir, RESEARCH_HONEST_MODE_ARTIFACT), honest);
501
+ await writeCompatibilityCouncilArtifacts(input.dir, input.plan, finalReview, gate);
502
+ return gate;
503
+ }
504
+ async function writeCompatibilityCouncilArtifacts(dir, plan, finalReview, gate) {
505
+ const reviews = Array.isArray(finalReview?.reviewers) ? finalReview.reviewers : [];
506
+ const byPersona = new Map(reviews.map((review) => [review.persona_id, review]));
507
+ const agents = RESEARCH_AGENT_COUNCIL.map((agent) => {
508
+ const review = byPersona.get(String(agent.id));
509
+ if (!review)
510
+ return null;
511
+ return {
512
+ id: agent.id,
513
+ agent_name: researchAgentAgentName(agent),
514
+ display_name: agent.display_name,
515
+ historical_inspiration: agent.historical_inspiration,
516
+ persona: agent.persona,
517
+ persona_boundary: agent.persona_boundary,
518
+ role: agent.role,
519
+ mandate: agent.mandate,
520
+ model_policy: {
521
+ custom_agent: RESEARCH_REVIEWER_CUSTOM_AGENT,
522
+ model: THINKING_SUBAGENT_MODEL,
523
+ reasoning_effort: SUBAGENT_EFFORT,
524
+ enforcement_source: gate?.reviewer_model_policy?.enforcement_source || RESEARCH_REVIEWER_CONFIG_ARTIFACT,
525
+ config_sha256: gate?.reviewer_model_policy?.config_sha256 || null
526
+ },
527
+ observed_model: null,
528
+ observed_reasoning_effort: null,
529
+ model_observation_status: 'official_hook_schema_does_not_expose_model; enforced by verified research-reviewer.toml',
530
+ official_subagent_thread_id: review.thread_id,
531
+ review_verdict: review.verdict,
532
+ eureka: review.eureka,
533
+ findings: [{
534
+ id: `${agent.id}-adversarial-review`,
535
+ claim: review.strongest_challenge,
536
+ source_ids: review.evidence_source_ids,
537
+ status: review.verdict === 'approve' ? 'survived_adversarial_review' : 'revision_required'
538
+ }],
539
+ falsifiers: review.falsifiers,
540
+ cheap_probes: review.cheap_probes,
541
+ challenge_or_response: review.strongest_challenge
542
+ };
543
+ }).filter(Boolean);
544
+ await writeJsonAtomic(path.join(dir, 'agent-ledger.json'), {
545
+ schema_version: 1,
546
+ council_mode: 'official_subagent_independent_adversarial_review',
547
+ created_at: nowIso(),
548
+ agents,
549
+ synthesis: {
550
+ surviving_claims: gate.passed ? ['manuscript_survived_all_structured_reviews'] : [],
551
+ downgraded_claims: [],
552
+ unresolved_conflicts: reviews.flatMap((review) => [...review.critical_objections, ...review.major_objections, ...review.minor_objections].map((objection) => objection.id))
553
+ }
554
+ });
555
+ await writeJsonAtomic(path.join(dir, 'debate-ledger.json'), {
556
+ schema_version: 1,
557
+ created_at: nowIso(),
558
+ mode: 'independent_adversarial_reviews_with_bounded_revision_cycles',
559
+ required_participants: RESEARCH_AGENT_COUNCIL.map((agent) => agent.id),
560
+ participant_display_names: RESEARCH_AGENT_COUNCIL.map((agent) => researchAgentAgentName(agent)),
561
+ consensus_iterations: Math.max(1, Number(finalReview?.cycle || 0)),
562
+ unanimous_consensus: gate.passed === true,
563
+ agent_agreements: reviews.map((review) => ({
564
+ agent_id: review.persona_id,
565
+ agent_name: researchAgentAgentName(RESEARCH_AGENT_COUNCIL.find((agent) => agent.id === review.persona_id)),
566
+ agrees: review.verdict === 'approve'
567
+ && review.critical_objections.length === 0
568
+ && review.major_objections.length === 0
569
+ && review.minor_objections.length === 0
570
+ && review.required_revisions.length === 0,
571
+ final_position: review.strongest_challenge,
572
+ source_ids: review.evidence_source_ids
573
+ })),
574
+ exchanges: reviews.map((review, index) => ({
575
+ id: `official-adversarial-review-${index + 1}`,
576
+ from: review.persona_id,
577
+ to: 'research_synthesis',
578
+ stance: 'challenge',
579
+ claim: review.strongest_challenge,
580
+ source_ids: review.evidence_source_ids,
581
+ verdict: review.verdict
582
+ })),
583
+ synthesis_pressure: {
584
+ strongest_disagreement: reviews.find((review) => review.verdict !== 'approve')?.strongest_challenge || 'All reviewers attempted falsification and approved the bounded evidence claims.',
585
+ changed_minds: [],
586
+ unresolved_conflicts: reviews.flatMap((review) => [...review.critical_objections, ...review.major_objections, ...review.minor_objections].map((objection) => objection.id))
587
+ }
588
+ });
589
+ const summary = [
590
+ '# Genius Opinion Summary',
591
+ '',
592
+ 'These are persona-inspired review lenses, not impersonations or evidence of genius-level performance.',
593
+ `Prompt: ${plan?.prompt || ''}`,
594
+ ''
595
+ ];
596
+ for (const agent of RESEARCH_AGENT_COUNCIL) {
597
+ const review = byPersona.get(String(agent.id));
598
+ summary.push(`## ${researchAgentAgentName(agent)} (${agent.id})`);
599
+ summary.push(`Final opinion: ${review?.verdict || 'missing structured outcome'}.`);
600
+ summary.push(`Strongest evidence/challenge: ${review?.strongest_challenge || 'missing'}`);
601
+ summary.push(`Unresolved objections: ${(review?.critical_objections.length || 0) + (review?.major_objections.length || 0) + (review?.minor_objections.length || 0) + (review?.required_revisions.length || 0)}`);
602
+ summary.push(`Eureka: ${review?.eureka.idea || 'missing'}`);
603
+ summary.push('');
604
+ }
605
+ await writeTextAtomic(path.join(dir, RESEARCH_GENIUS_SUMMARY_ARTIFACT), `${summary.join('\n').trim()}\n`);
606
+ }
607
+ export async function buildResearchHonestMode(dir, plan, executionClass) {
608
+ const report = await readText(path.join(dir, 'research-report.md'), '');
609
+ const paper = await readText(path.join(dir, researchPaperArtifactForPlan(plan)), '');
610
+ const claimMatrix = await readJson(path.join(dir, 'claim-evidence-matrix.json'), null);
611
+ const noveltyLedger = await readJson(path.join(dir, 'novelty-ledger.json'), null);
612
+ const sourceLedger = await readJson(path.join(dir, 'source-ledger.json'), null);
613
+ const text = `${report}\n${paper}`;
614
+ const overclaims = detectUnsupportedResearchOverclaims(text);
615
+ const structuredAudit = auditStructuredResearchClaims(text, claimMatrix, noveltyLedger, sourceLedger);
616
+ const blockers = unique([
617
+ ...overclaims.map((claim) => `unsupported_genius_or_novelty_claim:${claim}`),
618
+ ...structuredAudit.blockers
619
+ ]);
620
+ return {
621
+ schema: 'sks.research-honest-mode.v1',
622
+ checked_at: nowIso(),
623
+ execution_class: executionClass,
624
+ ok: blockers.length === 0,
625
+ guarantees: {
626
+ genius_level: false,
627
+ novelty: false,
628
+ breakthrough: false,
629
+ publication_acceptance: false
630
+ },
631
+ verified_claim: executionClass === 'mock_fixture'
632
+ ? 'Only artifact shape and fail-closed gate behavior were exercised.'
633
+ : 'Only source-linked claims that survived the recorded structured reviews may be presented as supported.',
634
+ unverified: ['live model intelligence level', 'scientific novelty absent an external prior-art study', 'peer-review or publication acceptance'],
635
+ overclaims,
636
+ claim_level_checks: structuredAudit.claim_level_checks,
637
+ prior_art_proof: structuredAudit.prior_art_proof,
638
+ blockers
639
+ };
640
+ }
641
+ function auditStructuredResearchClaims(text, claimMatrix, noveltyLedger, sourceLedger) {
642
+ const claims = Array.isArray(claimMatrix?.claims) ? claimMatrix.claims : [];
643
+ const entries = Array.isArray(noveltyLedger?.entries) ? noveltyLedger.entries : [];
644
+ const keyClaimIds = new Set((Array.isArray(claimMatrix?.key_claim_ids) ? claimMatrix.key_claim_ids : claims.map((claim) => claim?.id)).map(String).filter(Boolean));
645
+ const sourceRows = [
646
+ ...(Array.isArray(sourceLedger?.sources) ? sourceLedger.sources : []),
647
+ ...(Array.isArray(sourceLedger?.counterevidence_sources) ? sourceLedger.counterevidence_sources : [])
648
+ ];
649
+ const verifiedSourceIds = new Set(sourceRows
650
+ .filter((source) => String(source?.acquisition_verdict || '') === 'verified_content'
651
+ && Boolean(String(source?.content_artifact || '').trim())
652
+ && /^[a-f0-9]{64}$/i.test(String(source?.content_sha256 || ''))
653
+ && Number(source?.content_length || 0) > 0
654
+ && source?.super_search_provenance?.validated === true)
655
+ .map((source) => String(source?.id || source?.source_id || '')).filter(Boolean));
656
+ const entriesByClaim = new Map(entries.map((entry) => [String(entry?.claim_id || entry?.id || ''), entry]));
657
+ const claimLevelChecks = claims.map((claim) => {
658
+ const claimId = String(claim?.id || '');
659
+ const claimText = String(claim?.claim || claim?.text || '');
660
+ const entry = entriesByClaim.get(claimId);
661
+ const priorArtSourceIds = normalizeStrings([
662
+ ...(Array.isArray(entry?.prior_art_source_ids) ? entry.prior_art_source_ids : []),
663
+ ...(Array.isArray(entry?.prior_art_evidence_ids) ? entry.prior_art_evidence_ids : []),
664
+ ...(Array.isArray(entry?.prior_art_ids) ? entry.prior_art_ids : [])
665
+ ]);
666
+ const verifiedPriorArtSourceIds = priorArtSourceIds.filter((sourceId) => verifiedSourceIds.has(sourceId));
667
+ const noveltyAsserted = detectAssertiveNoveltyClaims(claimText).length > 0;
668
+ const guaranteeOverclaims = detectUnsupportedResearchOverclaims(claimText);
669
+ const blockers = [
670
+ ...guaranteeOverclaims.map(() => `unsupported_structured_claim:${claimId || 'unknown'}`),
671
+ ...(noveltyAsserted && verifiedPriorArtSourceIds.length === 0 ? [`novelty_claim_without_prior_art_proof:${claimId || 'unknown'}`] : [])
672
+ ];
673
+ return {
674
+ claim_id: claimId || null,
675
+ key_claim: keyClaimIds.has(claimId),
676
+ novelty_asserted: noveltyAsserted,
677
+ prior_art_source_ids: priorArtSourceIds,
678
+ verified_prior_art_source_ids: verifiedPriorArtSourceIds,
679
+ guarantee_overclaim_count: guaranteeOverclaims.length,
680
+ ok: blockers.length === 0,
681
+ blockers
682
+ };
683
+ });
684
+ const manuscriptNoveltyAssertions = detectAssertiveNoveltyClaims(text);
685
+ const keyClaimChecks = claimLevelChecks.filter((check) => check.key_claim);
686
+ const priorArtCoverageComplete = keyClaimChecks.length > 0
687
+ && keyClaimChecks.every((check) => check.verified_prior_art_source_ids.length > 0);
688
+ const blockers = unique([
689
+ ...claimLevelChecks.flatMap((check) => check.blockers),
690
+ ...(manuscriptNoveltyAssertions.length > 0 && !priorArtCoverageComplete
691
+ ? manuscriptNoveltyAssertions.map((sentence) => `novelty_claim_without_prior_art_proof:${sentence.slice(0, 180)}`)
692
+ : [])
693
+ ]);
694
+ return {
695
+ claim_level_checks: claimLevelChecks,
696
+ prior_art_proof: {
697
+ key_claim_count: keyClaimChecks.length,
698
+ key_claims_with_verified_prior_art: keyClaimChecks.filter((check) => check.verified_prior_art_source_ids.length > 0).length,
699
+ coverage_complete: priorArtCoverageComplete,
700
+ manuscript_novelty_assertions: manuscriptNoveltyAssertions
701
+ },
702
+ blockers
703
+ };
704
+ }
705
+ function detectAssertiveNoveltyClaims(text) {
706
+ const sentences = String(text || '').split(/(?<=[.!?。!?\n])\s*/u).map((row) => row.trim()).filter(Boolean);
707
+ const assertions = [
708
+ /\b(?:establish(?:es|ed)?|prov(?:e|es|ed)|demonstrat(?:e|es|ed)|confirm(?:s|ed)?|validat(?:e|es|ed))\b[^.!?\n]{0,80}\b(?:novelty|novel|world[- ]?first|unprecedented|original contribution)\b/i,
709
+ /\b(?:novelty|world[- ]?first status|original contribution)\b[^.!?\n]{0,60}\b(?:is|was|has been)\s+(?:established|proven|demonstrated|confirmed|validated)\b/i,
710
+ /(?:신규성|독창성|세계\s*최초)[^。!?\n]{0,60}(?:입증|확립|검증|증명)(?:했다|한다|되었다|됐다|합니다)?/i
711
+ ];
712
+ const negations = [
713
+ /\b(?:not|never|cannot|can't|unverified|unproven|not yet)\b/i,
714
+ /(?:입증되지\s*않|확립되지\s*않|검증되지\s*않|증명할\s*수\s*없|미검증)/i
715
+ ];
716
+ return unique(sentences.filter((sentence) => !negations.some((pattern) => pattern.test(sentence)) && assertions.some((pattern) => pattern.test(sentence))).map((sentence) => sentence.slice(0, 240)));
717
+ }
718
+ export function detectUnsupportedResearchOverclaims(text) {
719
+ const sentences = String(text || '').split(/(?<=[.!?。!?\n])\s*/u).map((row) => row.trim()).filter(Boolean);
720
+ const positivePatterns = [
721
+ /\bguarantee(?:d|s|ing)?\b[^.!?\n]{0,80}\b(?:genius(?:-level)?|novel(?:ty)?|breakthrough|world[- ]?first|publication acceptance|peer[- ]?review acceptance)\b/i,
722
+ /\b(?:einstein|von neumann)[-\s]+level\b[^.!?\n]{0,60}\b(?:intelligence|quality|genius|paper|research)\b/i,
723
+ /\b(?:proven|demonstrably|certainly|definitively)\b[^.!?\n]{0,60}\b(?:novel|breakthrough|revolutionary|world[- ]?first)\b/i,
724
+ /\bthis\s+is\s+(?:an?\s+)?(?:world[- ]?first|breakthrough|revolutionary|unprecedented|definitively\s+novel)\b[^.!?\n]{0,50}\b(?:paper|research|study|result|discovery|theory|method|achievement)\b/i,
725
+ /\b(?:this|our)\s+(?:world[- ]?first|breakthrough|revolutionary|unprecedented|definitively\s+novel)\s+(?:paper|research|study|result|discovery|theory|method|achievement)\b/i,
726
+ /\b(?:this|our)\s+(?:paper|research|study|result|work|discovery|method)\s+(?:is|represents|constitutes|delivers|establishes|proves|demonstrates)\s+(?:an?\s+)?(?:world[- ]?first|breakthrough|revolutionary|unprecedented|definitively\s+novel|novel\s+(?:theory|method|discovery))\b/i,
727
+ /\bpeer[- ]?reviewers?\s+(?:will|would|must)\s+(?:(?:certainly|definitely|inevitably)\s+)?(?:accept|approve|publish)\b/i,
728
+ /\b(?:publication|peer[- ]?review)\s+acceptance\s+(?:is|will\s+be)\s+(?:certain|assured|guaranteed|inevitable)\b/i,
729
+ /(?:아인슈타인급|폰\s*노이만급|천재급)[^。!?\n]{0,80}(?:지능|천재성|수준|품질|논문|연구)/i,
730
+ /(?:세계\s*최초|전례\s*없는|혁명적|돌파구)[^。!?\n]{0,80}(?:논문|연구|발견|성과|혁신)/i,
731
+ /(?:천재성|독창성|신규성|혁신성|세계\s*최초|출판|게재|채택)[^。!?\n]{0,80}(?:보장(?:한다|합니다|됨|된다)|입증(?:했다|합니다|됨|된다)|확실(?:하다|합니다))/i
732
+ ];
733
+ const negationPatterns = [
734
+ /\b(?:do|does|did)\s+not\s+guarantee\b/i,
735
+ /\b(?:cannot|can't|never|no)\s+(?:be\s+)?guarantee(?:d)?\b/i,
736
+ /\bno\s+guarantee\b/i,
737
+ /\b(?:not|never)\s+(?:proven|established|verified|claimed)\b/i,
738
+ /\b(?:is|are|was|were)\s+not\s+(?:an?\s+)?(?:world[- ]?first|breakthrough|revolutionary|unprecedented|novel)\b/i,
739
+ /\bpeer[- ]?reviewers?\s+(?:will|would)\s+not\s+(?:necessarily\s+)?(?:accept|approve|publish)\b/i,
740
+ /(?:보장하지\s*않|보장할\s*수\s*없|보장되지\s*않|주장하지\s*않|입증되지\s*않|확인되지\s*않|아니며|아니다|미보장)/i
741
+ ];
742
+ const overclaims = [];
743
+ for (const sentence of sentences) {
744
+ if (negationPatterns.some((pattern) => pattern.test(sentence)))
745
+ continue;
746
+ if (positivePatterns.some((pattern) => pattern.test(sentence)))
747
+ overclaims.push(sentence.slice(0, 240));
748
+ }
749
+ return unique(overclaims);
750
+ }
751
+ function reviewGoal(input, cycle, reviewArtifacts) {
752
+ return [
753
+ `Adversarially review Research mission ${input.plan?.mission_id || 'unknown'} cycle ${cycle}.`,
754
+ `Topic: ${input.plan?.prompt || ''}`,
755
+ 'Read source-ledger.json, claim-evidence-matrix.json, falsification-ledger.json, research-report.md, and the dated research paper artifact.',
756
+ `Review only the exact artifact bundle with SHA-256 ${reviewArtifacts.bundle_sha256}. The per-artifact hashes are ${JSON.stringify(reviewArtifacts.artifacts)}.`,
757
+ 'Attempt to reject the manuscript. Approve only if the strongest challenge fails against cited evidence and no required revision remains.',
758
+ 'Each subagent thread outcome summary must itself be one compact JSON object matching sks.research-adversarial-reviewer-outcome.v1.',
759
+ 'Do not claim to be a historical person. Do not guarantee genius, novelty, breakthrough status, peer review, or publication acceptance.'
760
+ ].join(' ');
761
+ }
762
+ function reviewerTaskDescription(agent, cycle, artifactBundleSha256) {
763
+ return [
764
+ `Cycle ${cycle}. Apply the ${agent.display_name} persona-inspired lens: ${agent.mandate}`,
765
+ agent.persona_boundary,
766
+ `The exact review artifact bundle SHA-256 is ${artifactBundleSha256}. Copy it unchanged into review_artifact_bundle_sha256.`,
767
+ 'Return your thread outcome summary as JSON with fields:',
768
+ '{"schema":"sks.research-adversarial-reviewer-outcome.v1","persona_id":"' + agent.id + '","verdict":"approve|revise|reject","strongest_challenge":"...","evidence_source_ids":[],"critical_objections":[{"id":"...","severity":"critical","claim_ids":[],"source_ids":[],"reason":"...","required_revision":"..."}],"major_objections":[],"minor_objections":[],"required_revisions":[],"eureka":{"exclamation":"Eureka!","idea":"...","source_ids":[]},"falsifiers":[],"cheap_probes":[],"confidence":"low|medium|high","review_artifact_bundle_sha256":"' + artifactBundleSha256 + '"}.',
769
+ 'Use only source IDs that exist in source-ledger.json. An approve verdict must have no critical, major, or minor objection and no required revision. Return nonempty falsifiers and cheap_probes.'
770
+ ].join(' ');
771
+ }
772
+ function researchReviewArtifacts(plan) {
773
+ const prefix = `.sneakoscope/missions/${plan?.mission_id || ''}/`;
774
+ return [
775
+ 'source-ledger.json',
776
+ 'claim-evidence-matrix.json',
777
+ 'falsification-ledger.json',
778
+ 'research-report.md',
779
+ researchPaperArtifactForPlan(plan),
780
+ RESEARCH_ADVERSARIAL_REVIEW_ARTIFACT
781
+ ].map((artifact) => `${prefix}${artifact}`);
782
+ }
783
+ async function runWorkflow(input, workflow) {
784
+ return (input.runWorkflowImpl || runOfficialSubagentWorkflow)(workflow);
785
+ }
786
+ function sanitizeWorkflowRun(run) {
787
+ return {
788
+ schema: run?.schema || null,
789
+ workflow: run?.workflow || null,
790
+ status: run?.status || null,
791
+ ok: run?.ok === true,
792
+ prepared: run?.prepared === true,
793
+ requested_subagents: Number(run?.requested_subagents || 0),
794
+ max_threads: Number(run?.max_threads || 0),
795
+ max_depth: Number(run?.max_depth || 0),
796
+ parent_model: run?.parent_model || null,
797
+ parent_reasoning_effort: run?.parent_reasoning_effort || null,
798
+ session_scope: run?.session_scope || null,
799
+ codex_exit_code: run?.codex_exit_code ?? null,
800
+ legacy_process_swarm_used: run?.legacy_process_swarm_used === true
801
+ };
802
+ }
803
+ function normalizeReviewerOutcome(value, threadRow) {
804
+ return {
805
+ schema: 'sks.research-adversarial-reviewer-outcome.v1',
806
+ persona_id: String(value?.persona_id || '').trim(),
807
+ verdict: ['approve', 'revise', 'reject'].includes(value?.verdict) ? value.verdict : 'reject',
808
+ strongest_challenge: String(value?.strongest_challenge || '').trim(),
809
+ evidence_source_ids: normalizeStrings(value?.evidence_source_ids),
810
+ critical_objections: normalizeObjections(value?.critical_objections, 'critical'),
811
+ major_objections: normalizeObjections(value?.major_objections, 'major'),
812
+ minor_objections: normalizeObjections(value?.minor_objections, 'minor'),
813
+ required_revisions: normalizeStrings(value?.required_revisions),
814
+ eureka: {
815
+ exclamation: value?.eureka?.exclamation === 'Eureka!' ? 'Eureka!' : 'Eureka!',
816
+ idea: value?.eureka?.exclamation === 'Eureka!' ? String(value?.eureka?.idea || '').trim() : '',
817
+ source_ids: normalizeStrings(value?.eureka?.source_ids)
818
+ },
819
+ falsifiers: normalizeStrings(value?.falsifiers),
820
+ cheap_probes: normalizeStrings(value?.cheap_probes),
821
+ confidence: ['low', 'medium', 'high'].includes(value?.confidence) ? value.confidence : 'low',
822
+ review_artifact_bundle_sha256: String(value?.review_artifact_bundle_sha256 || '').trim(),
823
+ thread_id: String(threadRow?.thread_id || '').trim(),
824
+ thread_status: ['completed', 'blocked', 'failed'].includes(threadRow?.status) ? threadRow.status : 'failed'
825
+ };
826
+ }
827
+ function validateReviewerOutcomeShape(value, threadRow) {
828
+ const threadId = String(threadRow?.thread_id || '').trim();
829
+ const personaId = String(value?.persona_id || '').trim();
830
+ const blockers = [];
831
+ const allowedKeys = new Set([
832
+ 'schema',
833
+ 'persona_id',
834
+ 'verdict',
835
+ 'strongest_challenge',
836
+ 'evidence_source_ids',
837
+ 'critical_objections',
838
+ 'major_objections',
839
+ 'minor_objections',
840
+ 'required_revisions',
841
+ 'eureka',
842
+ 'falsifiers',
843
+ 'cheap_probes',
844
+ 'confidence',
845
+ 'review_artifact_bundle_sha256'
846
+ ]);
847
+ if (value?.schema !== 'sks.research-adversarial-reviewer-outcome.v1')
848
+ blockers.push(`reviewer_schema_invalid:${threadId || 'unknown'}`);
849
+ if (!threadId)
850
+ blockers.push('reviewer_thread_id_missing:unknown');
851
+ if (threadRow?.status !== 'completed')
852
+ blockers.push(`reviewer_thread_not_completed:${threadId || 'unknown'}`);
853
+ if (!personaId)
854
+ blockers.push(`reviewer_persona_missing:${threadId || 'unknown'}`);
855
+ if (!['approve', 'revise', 'reject'].includes(value?.verdict))
856
+ blockers.push(`reviewer_verdict_invalid:${personaId || threadId || 'unknown'}`);
857
+ if (!String(value?.strongest_challenge || '').trim())
858
+ blockers.push(`reviewer_challenge_missing:${personaId || threadId || 'unknown'}`);
859
+ for (const key of ['evidence_source_ids', 'critical_objections', 'major_objections', 'minor_objections', 'required_revisions', 'falsifiers', 'cheap_probes']) {
860
+ if (!Array.isArray(value?.[key]))
861
+ blockers.push(`reviewer_field_not_array:${personaId || threadId || 'unknown'}:${key}`);
862
+ }
863
+ if (!value?.eureka || typeof value.eureka !== 'object' || Array.isArray(value.eureka))
864
+ blockers.push(`reviewer_eureka_invalid:${personaId || threadId || 'unknown'}`);
865
+ if (value?.eureka?.exclamation !== 'Eureka!')
866
+ blockers.push(`reviewer_eureka_exclamation_invalid:${personaId || threadId || 'unknown'}`);
867
+ if (!String(value?.eureka?.idea || '').trim())
868
+ blockers.push(`reviewer_eureka_missing:${personaId || threadId || 'unknown'}`);
869
+ if (!Array.isArray(value?.eureka?.source_ids))
870
+ blockers.push(`reviewer_eureka_sources_invalid:${personaId || threadId || 'unknown'}`);
871
+ if (!['low', 'medium', 'high'].includes(value?.confidence))
872
+ blockers.push(`reviewer_confidence_invalid:${personaId || threadId || 'unknown'}`);
873
+ if (!/^[a-f0-9]{64}$/i.test(String(value?.review_artifact_bundle_sha256 || '')))
874
+ blockers.push(`reviewer_artifact_bundle_sha256_invalid:${personaId || threadId || 'unknown'}`);
875
+ for (const key of Object.keys(value || {})) {
876
+ if (!allowedKeys.has(key))
877
+ blockers.push(`reviewer_unknown_field:${personaId || threadId || 'unknown'}:${key}`);
878
+ }
879
+ for (const [field, severity] of [['critical_objections', 'critical'], ['major_objections', 'major'], ['minor_objections', 'minor']]) {
880
+ for (const [index, objection] of (Array.isArray(value?.[field]) ? value[field] : []).entries()) {
881
+ if (!objection || typeof objection !== 'object' || Array.isArray(objection)) {
882
+ blockers.push(`reviewer_objection_invalid:${personaId || threadId || 'unknown'}:${field}:${index}`);
883
+ continue;
884
+ }
885
+ if (objection.severity !== severity)
886
+ blockers.push(`reviewer_objection_severity_invalid:${personaId || threadId || 'unknown'}:${field}:${index}`);
887
+ if (!String(objection.id || '').trim() || !String(objection.reason || '').trim() || !String(objection.required_revision || '').trim()) {
888
+ blockers.push(`reviewer_objection_invalid:${personaId || threadId || 'unknown'}:${field}:${index}`);
889
+ }
890
+ if (!Array.isArray(objection.claim_ids) || !Array.isArray(objection.source_ids))
891
+ blockers.push(`reviewer_objection_links_invalid:${personaId || threadId || 'unknown'}:${field}:${index}`);
892
+ }
893
+ }
894
+ return unique(blockers);
895
+ }
896
+ async function prepareResearchSubagentRun(input, opts) {
897
+ const plan = {
898
+ ...opts,
899
+ workflow_run_id: `research-${opts.phase}-${opts.cycle}-${randomId(12)}`
900
+ };
901
+ await Promise.all([
902
+ fsp.rm(path.join(input.dir, SUBAGENT_EVENT_LOG_FILENAME), { force: true }),
903
+ fsp.rm(path.join(input.dir, SUBAGENT_EVIDENCE_FILENAME), { force: true }),
904
+ fsp.rm(path.join(input.dir, SUBAGENT_PARENT_SUMMARY_FILENAME), { force: true })
905
+ ]);
906
+ await writeJsonAtomic(path.join(input.dir, 'subagent-plan.json'), {
907
+ schema: 'sks.subagent-plan.v1',
908
+ workflow: 'official_codex_subagent',
909
+ route: '$Research',
910
+ mission_id: String(input.plan?.mission_id || ''),
911
+ workflow_run_id: plan.workflow_run_id,
912
+ phase: plan.phase,
913
+ cycle: plan.cycle,
914
+ requested_subagents: plan.requested_subagents,
915
+ max_threads: plan.max_threads,
916
+ max_depth: 1,
917
+ ...(plan.review_artifacts ? { review_artifacts: plan.review_artifacts } : {}),
918
+ slices: plan.slices.map((slice) => ({
919
+ id: slice.id,
920
+ title: slice.title,
921
+ kind: slice.kind,
922
+ agent: slice.agent || null,
923
+ read_only: slice.readOnly === true,
924
+ paths: slice.paths || []
925
+ })),
926
+ model_policy: {
927
+ custom_agent: plan.phase === 'review' ? RESEARCH_REVIEWER_CUSTOM_AGENT : 'research_synthesizer',
928
+ model: THINKING_SUBAGENT_MODEL,
929
+ reasoning_effort: SUBAGENT_EFFORT,
930
+ config: plan.phase === 'review' ? RESEARCH_REVIEWER_CONFIG_ARTIFACT : '.codex/agents/research-synthesizer.toml'
931
+ }
932
+ });
933
+ await writeTextAtomic(path.join(input.dir, SUBAGENT_EVENT_LOG_FILENAME), '');
934
+ await writeJsonAtomic(path.join(input.dir, 'research', 'adversarial', `cycle-${plan.cycle}`, `${plan.phase}-subagent-plan.json`), plan);
935
+ return plan;
936
+ }
937
+ async function finalizeResearchSubagentRun(input, plan, run) {
938
+ const boundParentSummary = bindTrustworthySubagentParentSummaryToRun(run?.parent_summary, plan.workflow_run_id);
939
+ const parentSummary = await persistOrReuseTrustworthySubagentParentSummary(input.dir, boundParentSummary, {
940
+ workflowStatus: run?.status || null
941
+ });
942
+ const events = await readSubagentEvents(input.dir);
943
+ const evidence = await writeSubagentEvidence(input.dir, {
944
+ requestedSubagents: plan.requested_subagents,
945
+ events,
946
+ parentSummary,
947
+ parentSummaryPresent: parentSummary !== null && parentSummary !== undefined,
948
+ workflowStatus: run?.status || null,
949
+ preparationOnly: run?.prepared === true,
950
+ runId: plan.workflow_run_id,
951
+ additionalBlockers: [
952
+ ...(run?.status === 'parent_completed' ? [] : [`official_subagent_workflow_status:${String(run?.status || 'missing')}`]),
953
+ ...(run?.codex_exit_code === undefined || run?.codex_exit_code === null || run?.codex_exit_code === 0 ? [] : [`official_subagent_codex_exit:${run.codex_exit_code}`])
954
+ ]
955
+ });
956
+ const prefix = path.join(input.dir, 'research', 'adversarial', `cycle-${plan.cycle}`);
957
+ await writeJsonAtomic(path.join(prefix, `${plan.phase}-subagent-evidence.json`), evidence);
958
+ await writeTextAtomic(path.join(prefix, `${plan.phase}-subagent-events.jsonl`), await readText(path.join(input.dir, SUBAGENT_EVENT_LOG_FILENAME), ''));
959
+ const normalizedParent = normalizeSubagentParentSummary(parentSummary);
960
+ if (normalizedParent.raw)
961
+ await writeJsonAtomic(path.join(prefix, `${plan.phase}-subagent-parent-summary.json`), normalizedParent.raw);
962
+ return { parent_summary: parentSummary, evidence };
963
+ }
964
+ async function verifyResearchReviewerRoleConfig(root) {
965
+ const source = RESEARCH_REVIEWER_CONFIG_ARTIFACT;
966
+ const text = await readText(path.join(root, source), '');
967
+ const name = /^\s*name\s*=\s*"([^"]+)"\s*$/m.exec(text)?.[1] || '';
968
+ const model = /^\s*model\s*=\s*"([^"]+)"\s*$/m.exec(text)?.[1] || '';
969
+ const effort = /^\s*model_reasoning_effort\s*=\s*"([^"]+)"\s*$/m.exec(text)?.[1] || '';
970
+ const sandbox = /^\s*sandbox_mode\s*=\s*"([^"]+)"\s*$/m.exec(text)?.[1] || '';
971
+ const blockers = [
972
+ ...(text.trim() ? [] : ['research_reviewer_agent_config_missing']),
973
+ ...(name === RESEARCH_REVIEWER_CUSTOM_AGENT ? [] : [`research_reviewer_name_mismatch:${name || 'missing'}`]),
974
+ ...(model === THINKING_SUBAGENT_MODEL ? [] : [`research_reviewer_model_mismatch:${model || 'missing'}`]),
975
+ ...(effort === SUBAGENT_EFFORT ? [] : [`research_reviewer_effort_mismatch:${effort || 'missing'}`]),
976
+ ...(sandbox === 'read-only' ? [] : [`research_reviewer_sandbox_mismatch:${sandbox || 'missing'}`])
977
+ ];
978
+ return {
979
+ ok: blockers.length === 0,
980
+ source,
981
+ name,
982
+ model,
983
+ reasoning_effort: effort,
984
+ sandbox_mode: sandbox,
985
+ sha256: text ? sha256(text) : null,
986
+ observed_model: null,
987
+ observed_reasoning_effort: null,
988
+ blockers
989
+ };
990
+ }
991
+ function mockResearchModelPolicyEvidence() {
992
+ return {
993
+ ok: true,
994
+ source: 'mock_fixture:research_reviewer',
995
+ name: RESEARCH_REVIEWER_CUSTOM_AGENT,
996
+ model: THINKING_SUBAGENT_MODEL,
997
+ reasoning_effort: SUBAGENT_EFFORT,
998
+ sandbox_mode: 'read-only',
999
+ sha256: null,
1000
+ observed_model: null,
1001
+ observed_reasoning_effort: null,
1002
+ blockers: []
1003
+ };
1004
+ }
1005
+ function normalizeObjections(value, severity) {
1006
+ return (Array.isArray(value) ? value : []).map((row, index) => ({
1007
+ id: String(row?.id || `${severity}-objection-${index + 1}`).trim(),
1008
+ severity,
1009
+ claim_ids: normalizeStrings(row?.claim_ids),
1010
+ source_ids: normalizeStrings(row?.source_ids),
1011
+ reason: String(row?.reason || '').trim(),
1012
+ required_revision: String(row?.required_revision || '').trim()
1013
+ }));
1014
+ }
1015
+ function parseJsonObject(value) {
1016
+ if (value && typeof value === 'object' && !Array.isArray(value))
1017
+ return value;
1018
+ const text = String(value || '').trim();
1019
+ if (!text)
1020
+ return null;
1021
+ const fencedMatch = /^```(?:json)?\s*([\s\S]*?)\s*```$/i.exec(text);
1022
+ const unfenced = (fencedMatch?.[1] || text).trim();
1023
+ try {
1024
+ const parsed = JSON.parse(unfenced);
1025
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
1026
+ }
1027
+ catch {
1028
+ return null;
1029
+ }
1030
+ }
1031
+ async function sourceIdSet(dir, executionClass) {
1032
+ const ledger = await readJson(path.join(dir, 'source-ledger.json'), null);
1033
+ return eligibleResearchSourceIdSet(dir, ledger, executionClass);
1034
+ }
1035
+ async function manuscriptHashes(dir, plan) {
1036
+ const artifacts = ['research-report.md', researchPaperArtifactForPlan(plan), 'research-synthesis-output.json'];
1037
+ const rows = await Promise.all(artifacts.map(async (artifact) => [artifact, sha256(await readText(path.join(dir, artifact), ''))]));
1038
+ return Object.fromEntries(rows);
1039
+ }
1040
+ async function syncSynthesisAfterRevision(dir, plan) {
1041
+ const existing = await readJson(path.join(dir, 'research-synthesis-output.json'), null);
1042
+ if (!existing)
1043
+ return;
1044
+ const report = await readText(path.join(dir, 'research-report.md'), '');
1045
+ const paper = await readText(path.join(dir, researchPaperArtifactForPlan(plan)), '');
1046
+ const normalized = normalizeResearchSynthesisOutput({ ...existing, report_markdown: report, paper_markdown: paper });
1047
+ await writeJsonAtomic(path.join(dir, 'research-synthesis-output.json'), normalized);
1048
+ }
1049
+ function remainingTimeoutMs(deadlineMs) {
1050
+ return Math.max(0, Math.floor(deadlineMs - Date.now()));
1051
+ }
1052
+ function normalizeStrings(value) {
1053
+ return unique((Array.isArray(value) ? value : value == null ? [] : [value]).map((item) => String(item || '').trim()).filter(Boolean));
1054
+ }
1055
+ function duplicates(values) {
1056
+ const seen = new Set();
1057
+ const duplicates = new Set();
1058
+ for (const value of values) {
1059
+ if (seen.has(value))
1060
+ duplicates.add(value);
1061
+ seen.add(value);
1062
+ }
1063
+ return [...duplicates];
1064
+ }
1065
+ function unique(values) {
1066
+ return [...new Set(values)];
1067
+ }
1068
+ //# sourceMappingURL=research-adversarial-review.js.map