0xray 4.0.41 → 4.0.42

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 (115) hide show
  1. package/.grok-plugin/plugin.json +1 -1
  2. package/.opencode/skills/orchestrator/SKILL.md +12 -0
  3. package/.xray/features.json +1 -1
  4. package/.xray/features.schema.json +1 -1
  5. package/AGENTS-consumer.md +2 -0
  6. package/AGENTS.md +2 -0
  7. package/README.md +52 -12
  8. package/SKILLS.md +2 -0
  9. package/dist/AGENTS.md +2 -0
  10. package/dist/CHANGELOG.md +13 -0
  11. package/dist/README.md +52 -12
  12. package/dist/SKILLS.md +2 -0
  13. package/dist/cli/commands/openclaw-install.js +1 -1
  14. package/dist/cli/index.js +12 -0
  15. package/dist/enforcement/index.d.ts +4 -0
  16. package/dist/enforcement/index.js +4 -0
  17. package/dist/enforcement/validators/code-quality-validators.js +6 -4
  18. package/dist/governance/llm-governance-provider.d.ts +7 -1
  19. package/dist/governance/llm-governance-provider.js +72 -14
  20. package/dist/governance/local-confer.d.ts +4 -1
  21. package/dist/governance/local-confer.js +22 -1
  22. package/dist/integrations/grok/grok-cli.js +10 -37
  23. package/dist/integrations/grok/hooks/grok-hook-utils.js +8 -1
  24. package/dist/integrations/hooks/confer-hook-runtime.mjs +23 -1
  25. package/dist/integrations/hooks/goggles-planes.json +4 -1
  26. package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  27. package/dist/mcps/architect-tools.server.d.ts +6 -0
  28. package/dist/mcps/architect-tools.server.js +19 -2
  29. package/dist/mcps/config/server-config-registry.js +8 -6
  30. package/dist/mcps/governance.server.d.ts +1 -0
  31. package/dist/mcps/governance.server.js +46 -0
  32. package/dist/mcps/knowledge-skills/code-review.server.js +7 -0
  33. package/dist/mcps/knowledge-skills/security-audit.server.js +5 -3
  34. package/dist/mcps/mcp-client.js +1 -1
  35. package/dist/mcps/orchestrator/handlers/complexity-handler.js +2 -2
  36. package/dist/mcps/researcher.server.js +12 -3
  37. package/dist/nucleus/autonomy-kernel.d.ts +2 -0
  38. package/dist/nucleus/autonomy-kernel.js +17 -13
  39. package/dist/nucleus/confer-host.d.ts +35 -0
  40. package/dist/nucleus/confer-host.js +55 -0
  41. package/dist/nucleus/confer.d.ts +19 -3
  42. package/dist/nucleus/confer.js +111 -55
  43. package/dist/nucleus/lead-dev-plan-persistence.js +4 -2
  44. package/dist/nucleus/suit-temperament.d.ts +1 -1
  45. package/dist/nucleus/suit-temperament.js +2 -2
  46. package/dist/nucleus/synthesis-consult-receipt.d.ts +7 -1
  47. package/dist/nucleus/synthesis-consult-receipt.js +86 -35
  48. package/dist/reporting/framework-reporting-system.js +2 -5
  49. package/dist/reporting/log-parser.d.ts +2 -0
  50. package/dist/reporting/log-parser.js +7 -6
  51. package/dist/skills/orchestrator/SKILL.md +12 -0
  52. package/docs-site/docs/plates/activity-log.md +1 -1
  53. package/docs-site/docs/plates/boot.md +1 -1
  54. package/docs-site/docs/plates/centrifuge.md +1 -1
  55. package/docs-site/docs/plates/cursor-compact.md +1 -1
  56. package/docs-site/docs/plates/glossary.md +1 -1
  57. package/docs-site/docs/plates/goggles.md +1 -1
  58. package/docs-site/docs/plates/governance.md +1 -1
  59. package/docs-site/docs/plates/grok-compact.md +1 -1
  60. package/docs-site/docs/plates/grokbot.md +1 -1
  61. package/docs-site/docs/plates/host-pack.md +1 -1
  62. package/docs-site/docs/plates/house.md +1 -1
  63. package/docs-site/docs/plates/index.md +1 -1
  64. package/docs-site/docs/plates/inference-files.md +1 -1
  65. package/docs-site/docs/plates/kept-line.md +1 -1
  66. package/docs-site/docs/plates/kits.md +1 -1
  67. package/docs-site/docs/plates/lens-gate.md +1 -1
  68. package/docs-site/docs/plates/lens-page.md +1 -1
  69. package/docs-site/docs/plates/live-mesh.md +1 -1
  70. package/docs-site/docs/plates/memory-recall.md +1 -1
  71. package/docs-site/docs/plates/notes-page.md +1 -1
  72. package/docs-site/docs/plates/orchestration.md +1 -1
  73. package/docs-site/docs/plates/package-face.md +1 -1
  74. package/docs-site/docs/plates/payload-heat.md +1 -1
  75. package/docs-site/docs/plates/pickup-stamp.md +1 -1
  76. package/docs-site/docs/plates/pre-tool.md +1 -1
  77. package/docs-site/docs/plates/processor.md +1 -1
  78. package/docs-site/docs/plates/record-map.md +1 -1
  79. package/docs-site/docs/plates/reflection-page.md +1 -1
  80. package/docs-site/docs/plates/reporting.md +1 -1
  81. package/docs-site/docs/plates/review.md +1 -1
  82. package/docs-site/docs/plates/routing.md +1 -1
  83. package/docs-site/docs/plates/session-capture.md +1 -1
  84. package/docs-site/docs/plates/site-manual.md +1 -1
  85. package/docs-site/docs/plates/stamp-plate.md +1 -1
  86. package/docs-site/docs/plates/station-card.md +1 -1
  87. package/docs-site/docs/plates/station-heat.md +1 -1
  88. package/docs-site/docs/plates/suit-organs.md +1 -1
  89. package/docs-site/docs/plates/suit-settings.md +1 -1
  90. package/docs-site/docs/plates/suit-wear.md +1 -1
  91. package/docs-site/docs/plates/suit.md +1 -1
  92. package/docs-site/docs/plates/trail-state.md +1 -1
  93. package/docs-site/docs/plates/work-fresh.md +1 -1
  94. package/docs-site/docs/plates/write-home.md +1 -1
  95. package/llms.txt +2 -0
  96. package/package.json +2 -2
  97. package/scripts/mjs/verify-confer-core.mjs +153 -29
  98. package/scripts/mjs/verify-user-aside-core.mjs +3 -4
  99. package/scripts/node/bridge-mcp-wiring.cjs +236 -5
  100. package/scripts/node/confer-switch.cjs +66 -0
  101. package/scripts/node/install-bridges.cjs +16 -41
  102. package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
  103. package/src/mcps/architect-tools.server.ts +26 -2
  104. package/src/mcps/config/__tests__/server-config-registry.test.ts +15 -6
  105. package/src/mcps/config/server-config-registry.ts +9 -6
  106. package/src/mcps/governance.server.ts +49 -0
  107. package/src/mcps/knowledge-skills/code-review.server.ts +8 -0
  108. package/src/mcps/knowledge-skills/security-audit.server.ts +11 -12
  109. package/src/mcps/mcp-client.ts +1 -1
  110. package/src/mcps/orchestrator/handlers/complexity-handler.ts +2 -1
  111. package/src/mcps/researcher.server.ts +19 -10
  112. package/src/skills/orchestrator/SKILL.md +12 -0
  113. package/vendor/@0xray/repertoire/scripts/run-live-synthesis-checkpoint.mjs +5 -51
  114. package/xray/features.json +1 -1
  115. package/xray/features.schema.json +1 -1
@@ -7,7 +7,7 @@
7
7
 
8
8
  const fs = require("fs");
9
9
  const path = require("path");
10
- const { execFileSync, execSync } = require("child_process");
10
+ const { execFileSync } = require("child_process");
11
11
  const {
12
12
  wantsCostume,
13
13
  isIsolatedHome,
@@ -22,18 +22,19 @@ const {
22
22
  wireOpencodeBridge,
23
23
  wireOpenClawBridge,
24
24
  deployPortableProjectMcpJson,
25
- scopedMcpLaunch,
26
25
  mergeMcpMap,
27
26
  mergeNamedRecords,
28
27
  jsonDeepEqual,
29
28
  copyHermesFindProjectRootHelper,
30
29
  copyHermesHookRuntimes,
31
30
  installOpenClawHostWear,
31
+ recordOpenClawProjectPlugin,
32
32
  maybeWriteOpenClawCliBackend,
33
33
  isEphemeralInstallRoot,
34
34
  enableMemoryRoutingIfResolves,
35
35
  pinLauncherPackageRoot,
36
- XRAY_MCP_SERVERS,
36
+ writeProjectSuitMcp,
37
+ trustGrokFolder,
37
38
  } = require("./bridge-mcp-wiring.cjs");
38
39
 
39
40
  const wearIo = { writes: 0, skips: 0 };
@@ -484,42 +485,11 @@ function findGrokPluginSource(packageRoot) {
484
485
  return candidates.find((p) => fs.existsSync(p));
485
486
  }
486
487
 
487
- function registerGrokMcpServers(targetDir, log, pluginDirs) {
488
- try {
489
- execSync("which grok", { stdio: "ignore" });
490
- } catch {
491
- log("grok-bridge", "grok CLI not on PATH — plugin .mcp.json still configured", "info");
492
- return;
493
- }
494
-
495
- for (const s of XRAY_MCP_SERVERS) {
496
- try {
497
- const launch = scopedMcpLaunch(targetDir, s.mcpCmd, s.env);
498
- const argv = ["mcp", "add", s.name, "--command", launch.command, "--args", launch.args[0], ...launch.args.slice(1)];
499
- for (const [key, value] of Object.entries(launch.env)) {
500
- argv.push("--env", `${key}=${value}`);
501
- }
502
- execFileSync("grok", argv, { stdio: "pipe" });
503
- log("grok-bridge", `registered ${s.name} (mcp-launch)`, "info");
504
- } catch {
505
- // already registered or grok config conflict — non-blocking
506
- }
507
- }
508
-
509
- const dirs =
510
- Array.isArray(pluginDirs) && pluginDirs.length > 0
511
- ? pluginDirs
512
- : [path.join(targetDir, ".grok", "plugins", "0xray")];
513
- for (const pluginDir of dirs) {
514
- if (!fs.existsSync(pluginDir)) continue;
515
- try {
516
- execSync(`grok plugins trust "${pluginDir}"`, { stdio: "ignore" });
517
- log("grok-bridge", "plugin trusted", "info", { path: pluginDir });
518
- break;
519
- } catch {
520
- // best-effort
521
- }
522
- }
488
+ function registerGrokMcpServers(targetDir, log, env) {
489
+ const tomlPath = writeProjectSuitMcp(targetDir);
490
+ log("grok-bridge", "project mcp config written", "info", { path: tomlPath });
491
+ const trustPath = trustGrokFolder(targetDir, env || process.env);
492
+ log("grok-bridge", "folder trusted", "info", { path: trustPath });
523
493
  }
524
494
 
525
495
 
@@ -742,8 +712,8 @@ function installGrokBridge(targetDir, packageRoot, log, opts) {
742
712
  if (globalCopied > 0) {
743
713
  log("grok-bridge", `global skills synced (${globalCopied})`, "info", { path: grokGlobalSkills });
744
714
  }
745
- registerGrokMcpServers(targetDir, log, targets);
746
715
  }
716
+ registerGrokMcpServers(targetDir, log, env);
747
717
  }
748
718
 
749
719
  function installHermesBridge(targetDir, packageRoot, log) {
@@ -881,7 +851,7 @@ function installOpenclawBridge(targetDir, packageRoot, log) {
881
851
  }
882
852
 
883
853
  if (!ephemeralOpenclaw && !isolatedOpenclaw) {
884
- const hook = installOpenClawHostWear(packageRoot);
854
+ const hook = installOpenClawHostWear(packageRoot, targetDir);
885
855
  if (hook) log("openclaw-bridge", "PreToolUse hook installed", "info", { path: hook });
886
856
  if (maybeWriteOpenClawCliBackend()) {
887
857
  log("openclaw-bridge", "opencode-cli backend written", "info");
@@ -895,6 +865,11 @@ function installOpenclawBridge(targetDir, packageRoot, log) {
895
865
  "info",
896
866
  );
897
867
  }
868
+
869
+ const recorded = recordOpenClawProjectPlugin(targetDir, packageRoot);
870
+ if (recorded) {
871
+ log("openclaw-bridge", "project plugin path recorded", "info", { path: recorded });
872
+ }
898
873
  }
899
874
 
900
875
  const XRAY_CONFIG_FILES = ["codex.json", "features.json", "features.schema.json", "config.json"];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xray-pre-tool",
3
- "version": "4.0.41",
3
+ "version": "4.0.42",
4
4
  "private": true,
5
5
  "type": "module",
6
6
  "description": "0xRay constitution + temperament PreToolUse for OpenClaw",
@@ -10,14 +10,38 @@ import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
10
10
  import * as fs from "fs";
11
11
  import * as path from "path";
12
12
  import { frameworkLogger } from "../core/framework-logger.js";
13
+ import { type GovernanceVote } from "../governance/llm-governance-provider.js";
13
14
  import {
14
15
  contextAnalysis as architectContextAnalysis,
15
16
  codebaseStructure as architectCodebaseStructure,
16
17
  dependencyAnalysis as architectDependencyAnalysis,
17
18
  architectureAssessment as architectArchitectureAssessment,
18
- formatConferArchitectureText,
19
+ type ArchitectureAssessment,
19
20
  } from "../architect/architect-tools.js";
20
21
 
22
+ export type ConferModelVerdict = "PASS" | "CONDITIONAL" | "FAIL" | "UNREVIEWED";
23
+
24
+ /** A confer verdict counts only when a model vote exists. Metrics alone are UNREVIEWED. */
25
+ export function conferVerdictFromModelVote(
26
+ vote: Pick<GovernanceVote, "decision"> | null,
27
+ ): ConferModelVerdict {
28
+ if (!vote) return "UNREVIEWED";
29
+ if (vote.decision === "reject") return "FAIL";
30
+ if (vote.decision === "approve") return "PASS";
31
+ return "CONDITIONAL";
32
+ }
33
+
34
+ export function formatArchitectureAssessmentConferText(
35
+ conferPrompt: string,
36
+ assessment: ArchitectureAssessment,
37
+ verdict: ConferModelVerdict = "UNREVIEWED",
38
+ ): string {
39
+ const assessmentJson = JSON.stringify(assessment, null, 2);
40
+ const trimmed = conferPrompt.trim();
41
+ if (!trimmed) return assessmentJson;
42
+ return `${trimmed}\n\n## Architecture assessment\n${assessmentJson}\n\nVerdict: ${verdict}\nTop risks: review assessment metrics above\nHardening: address high-complexity or coupling findings before resuming`;
43
+ }
44
+
21
45
  interface DirectoryNode {
22
46
  name: string;
23
47
  path: string;
@@ -48,7 +72,7 @@ export async function renderArchitectureAssessment(
48
72
  content: [
49
73
  {
50
74
  type: "text",
51
- text: formatConferArchitectureText(conferPrompt, result),
75
+ text: formatArchitectureAssessmentConferText(conferPrompt, result, "UNREVIEWED"),
52
76
  },
53
77
  ],
54
78
  };
@@ -83,25 +83,25 @@ describe('ServerConfigRegistry', () => {
83
83
  expect(registry.has('code-review')).toBe(true);
84
84
  const config = registry.get('code-review');
85
85
  expect(config?.command).toBe('node');
86
- expect(config?.timeout).toBe(30000);
86
+ expect(config?.timeout).toBe(150000);
87
87
  });
88
88
 
89
89
  it('should have security-audit server registered by default', () => {
90
90
  expect(registry.has('security-audit')).toBe(true);
91
91
  const config = registry.get('security-audit');
92
- expect(config?.timeout).toBe(45000);
92
+ expect(config?.timeout).toBe(150000);
93
93
  });
94
94
 
95
95
  it('should have security-auditor alias registered by default', () => {
96
96
  expect(registry.has('security-auditor')).toBe(true);
97
97
  const config = registry.get('security-auditor');
98
- expect(config?.timeout).toBe(45000);
98
+ expect(config?.timeout).toBe(150000);
99
99
  });
100
100
 
101
101
  it('should have researcher server registered by default', () => {
102
102
  expect(registry.has('researcher')).toBe(true);
103
103
  const config = registry.get('researcher');
104
- expect(config?.timeout).toBe(60000);
104
+ expect(config?.timeout).toBe(150000);
105
105
  });
106
106
 
107
107
  it('should have framework-help server registered by default', () => {
@@ -119,7 +119,7 @@ describe('ServerConfigRegistry', () => {
119
119
  it('should have architect server registered by default', () => {
120
120
  expect(registry.has('architect')).toBe(true);
121
121
  const config = registry.get('architect');
122
- expect(config?.timeout).toBe(45000);
122
+ expect(config?.timeout).toBe(150000);
123
123
  });
124
124
 
125
125
  it('should have enforcer server registered by default', () => {
@@ -242,7 +242,16 @@ describe('ServerConfigRegistry', () => {
242
242
 
243
243
  // Governance (Dynamo Solar SSOT orchestrator) is allowed higher timeout
244
244
  // because it coordinates multiple skill MCPs + required external filter.
245
- const maxTimeout = config.serverName === 'governance' ? 120000 : 60000;
245
+ const modelServers = new Set([
246
+ 'governance',
247
+ 'architect',
248
+ 'code-review',
249
+ 'code-reviewer',
250
+ 'researcher',
251
+ 'security-audit',
252
+ 'security-auditor',
253
+ ]);
254
+ const maxTimeout = modelServers.has(config.serverName) ? 150000 : 60000;
246
255
  expect(config.timeout).toBeLessThanOrEqual(maxTimeout);
247
256
  }
248
257
  });
@@ -13,6 +13,9 @@ import { fileURLToPath } from 'url';
13
13
  import { dirname, join, resolve } from 'path';
14
14
  import fs from 'fs';
15
15
 
16
+ /** Must outlast the 120s governance model call so a real approve can return. */
17
+ const GOVERNANCE_MODEL_TIMEOUT_MS = 150_000;
18
+
16
19
  /**
17
20
  * Registry for managing MCP server configurations
18
21
  */
@@ -95,7 +98,7 @@ export class ServerConfigRegistry {
95
98
  serverName: 'code-review',
96
99
  command: 'node',
97
100
  args: [`${basePath}/knowledge-skills/code-review.server.js`],
98
- timeout: 30000,
101
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
99
102
  });
100
103
 
101
104
  // Security Audit Server
@@ -103,7 +106,7 @@ export class ServerConfigRegistry {
103
106
  serverName: 'security-audit',
104
107
  command: 'node',
105
108
  args: [`${basePath}/knowledge-skills/security-audit.server.js`],
106
- timeout: 45000,
109
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
107
110
  });
108
111
 
109
112
  // Performance Optimization Server
@@ -127,7 +130,7 @@ export class ServerConfigRegistry {
127
130
  serverName: 'researcher',
128
131
  command: 'node',
129
132
  args: [`${basePath}/researcher.server.js`],
130
- timeout: 60000,
133
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
131
134
  });
132
135
 
133
136
  // Governance Service (meta-MCP that orchestrates the three skill servers + required external Dynamo)
@@ -199,7 +202,7 @@ export class ServerConfigRegistry {
199
202
  serverName: 'architect',
200
203
  command: 'node',
201
204
  args: [`${basePath}/architect-tools.server.js`],
202
- timeout: 45000,
205
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
203
206
  });
204
207
 
205
208
  // Bug Triage Specialist Server
@@ -224,7 +227,7 @@ export class ServerConfigRegistry {
224
227
  serverName: 'code-reviewer',
225
228
  command: 'node',
226
229
  args: [`${basePath}/knowledge-skills/code-review.server.js`],
227
- timeout: 30000,
230
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
228
231
  });
229
232
 
230
233
  // Security Auditor (alias for security-audit)
@@ -232,7 +235,7 @@ export class ServerConfigRegistry {
232
235
  serverName: 'security-auditor',
233
236
  command: 'node',
234
237
  args: [`${basePath}/knowledge-skills/security-audit.server.js`],
235
- timeout: 45000,
238
+ timeout: GOVERNANCE_MODEL_TIMEOUT_MS,
236
239
  });
237
240
 
238
241
  // Refactorer Server
@@ -29,6 +29,7 @@ import * as fs from "fs";
29
29
  import * as path from "path";
30
30
  import { fileURLToPath } from "url";
31
31
  import { randomUUID } from "crypto";
32
+ import { applyConferConsultResult } from "../nucleus/confer.js";
32
33
  import { getGovernanceService } from "../governance/governance-service.js";
33
34
  import { getCodexPolicyService } from "../governance/codex-policy.service.js";
34
35
  import { initializeGovernanceIntegration, shutdownGovernanceIntegration } from "../integrations/governance/index.js";
@@ -136,6 +137,24 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
136
137
  required: [],
137
138
  },
138
139
  },
140
+ {
141
+ name: "record_confer_receipt",
142
+ description:
143
+ "Record a confer verdict written by the wearing TUI. " +
144
+ "The TUI already called the seat's MCP server. This tool stores the receipt. " +
145
+ "Only Verdict: PASS completes the consult todo.",
146
+ inputSchema: {
147
+ type: "object",
148
+ properties: {
149
+ todoId: { type: "string", description: "Consult todo id, such as s.1" },
150
+ subagent: { type: "string", description: "researcher, architect-tools, or code-review" },
151
+ sessionId: { type: "string", description: "Plan session id" },
152
+ outputText: { type: "string", description: "Host verdict text, including a Verdict line" },
153
+ projectRoot: { type: "string", description: "Checkout root. Defaults to XRAY_ROOT or the process cwd." },
154
+ },
155
+ required: ["todoId", "subagent", "sessionId", "outputText"],
156
+ },
157
+ },
139
158
  {
140
159
  name: "get_active_codex",
141
160
  description:
@@ -159,6 +178,7 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
159
178
  "govern_proposals": async (args) => this.handleGovernProposals(this.validateGovernProposalsArgs(args)),
160
179
  "govern_reflection": async (args) => this.handleGovernReflection(this.validateGovernReflectionArgs(args)),
161
180
  "get_active_codex": async (args) => this.handleGetActiveCodex(args as { includeRaw?: boolean }),
181
+ "record_confer_receipt": async (args) => this.handleRecordConferReceipt(args),
162
182
  };
163
183
  this.setupToolHandlers();
164
184
  }
@@ -210,6 +230,8 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
210
230
  return await this.handleGovernReflection(this.validateGovernReflectionArgs(args));
211
231
  case "get_active_codex":
212
232
  return await this.handleGetActiveCodex(args as { includeRaw?: boolean });
233
+ case "record_confer_receipt":
234
+ return await this.handleRecordConferReceipt(args);
213
235
  default:
214
236
  throw new Error(`Unknown tool: ${name}`);
215
237
  }
@@ -334,6 +356,33 @@ class GovernanceServer extends XrayKnowledgeSkillBase {
334
356
  });
335
357
  }
336
358
 
359
+ private async handleRecordConferReceipt(args: unknown): Promise<CallToolResult> {
360
+ if (!args || typeof args !== "object") {
361
+ throw new Error("record_confer_receipt requires an object argument");
362
+ }
363
+ const obj = args as Record<string, unknown>;
364
+ const todoId = typeof obj.todoId === "string" ? obj.todoId.trim() : "";
365
+ const subagent = typeof obj.subagent === "string" ? obj.subagent.trim() : "";
366
+ const sessionId = typeof obj.sessionId === "string" ? obj.sessionId.trim() : "";
367
+ const outputText = typeof obj.outputText === "string" ? obj.outputText : "";
368
+ if (!todoId || !subagent || !sessionId || !outputText.trim()) {
369
+ throw new Error("record_confer_receipt requires todoId, subagent, sessionId, and outputText");
370
+ }
371
+ const projectRoot =
372
+ typeof obj.projectRoot === "string" && obj.projectRoot.trim()
373
+ ? obj.projectRoot
374
+ : process.env.XRAY_ROOT || process.cwd();
375
+ const result = applyConferConsultResult(todoId, subagent, sessionId, outputText, projectRoot);
376
+ return {
377
+ content: [
378
+ {
379
+ type: "text",
380
+ text: JSON.stringify(result),
381
+ },
382
+ ],
383
+ };
384
+ }
385
+
337
386
  private async handleGetActiveCodex(args: { includeRaw?: boolean }): Promise<CallToolResult> {
338
387
  const includeRaw = args?.includeRaw === true;
339
388
  const snapshot = await getCodexPolicyService().getCurrentCodex(includeRaw);
@@ -14,6 +14,7 @@ import {
14
14
  tryLLMGovernance,
15
15
  } from "../../governance/llm-governance-provider.js";
16
16
  import { formatGovernanceVoteText, localConferVote } from "../../governance/local-confer.js";
17
+ import { buildHostConferEvidence } from "../../nucleus/confer-host.js";
17
18
 
18
19
  interface CodeReviewResult {
19
20
  file: string;
@@ -439,6 +440,13 @@ class XrayCodeReviewServer extends XrayKnowledgeSkillBase {
439
440
  async analyzeProposal(args: AnalyzeProposalArgs) {
440
441
  const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args;
441
442
 
443
+ if (proposalType === "synthesis-confer") {
444
+ const packet = [proposalTitle, proposalDescription, ...(evidence || [])].filter(Boolean).join("\n\n");
445
+ return {
446
+ content: [{ type: "text", text: buildHostConferEvidence("code-review", packet) }],
447
+ };
448
+ }
449
+
442
450
  const vote =
443
451
  (await tryLLMGovernance(
444
452
  "code-review",
@@ -8,10 +8,7 @@
8
8
  import * as fs from "fs";
9
9
  import * as path from "path";
10
10
  import { XrayKnowledgeSkillBase } from "../shared/knowledge-skill-base.js";
11
- import {
12
- isGovernanceLlmConfigured,
13
- tryLLMGovernance,
14
- } from "../../governance/llm-governance-provider.js";
11
+ import { attemptLLMGovernance } from "../../governance/llm-governance-provider.js";
15
12
  import { formatGovernanceVoteText, localConferVote } from "../../governance/local-confer.js";
16
13
 
17
14
  interface SecurityVulnerability {
@@ -999,17 +996,19 @@ class XraySecurityAuditServer extends XrayKnowledgeSkillBase {
999
996
  async analyzeProposal(args: AnalyzeProposalArgs) {
1000
997
  const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args;
1001
998
 
999
+ const attempt = await attemptLLMGovernance(
1000
+ "security-audit",
1001
+ proposalTitle,
1002
+ proposalDescription,
1003
+ evidence,
1004
+ proposalType,
1005
+ );
1002
1006
  const vote =
1003
- (await tryLLMGovernance(
1004
- "security-audit",
1005
- proposalTitle,
1006
- proposalDescription,
1007
- evidence,
1008
- proposalType,
1009
- )) ??
1007
+ attempt.vote ??
1010
1008
  localConferVote({
1011
1009
  role: "security-audit",
1012
- llmConfigured: isGovernanceLlmConfigured(),
1010
+ llmConfigured: attempt.miss !== "not-configured",
1011
+ ...(attempt.miss ? { miss: attempt.miss } : {}),
1013
1012
  });
1014
1013
 
1015
1014
  return {
@@ -400,7 +400,7 @@ export class MCPClient extends EventEmitter {
400
400
  this.emit('tool.before', beforeEvent);
401
401
 
402
402
  const serverName = this.config.serverName;
403
- const isGovernanceServer = ['code-review', 'security-audit', 'researcher', 'governance'].includes(serverName);
403
+ const isGovernanceServer = ['code-review', 'security-audit', 'researcher', 'architect', 'governance'].includes(serverName);
404
404
  const isGovernanceTool = toolName === 'analyze_proposal';
405
405
  const preferReal = this.isPureMcpMode || isGovernanceServer || isGovernanceTool;
406
406
 
@@ -12,6 +12,7 @@ import {
12
12
  persistLeadDevPlan,
13
13
  } from '../../../nucleus/autonomy-kernel.js';
14
14
  import {
15
+ conferFixtureAllowed,
15
16
  formatConferQuorumReport,
16
17
  isConferEnabled,
17
18
  runConferQuorum,
@@ -286,7 +287,7 @@ export class ComplexityHandler {
286
287
  collocatedText?: string;
287
288
  } = {
288
289
  dueReason: args.synthesisDueReason ?? null,
289
- fixture: args.conferFixture === true,
290
+ fixture: conferFixtureAllowed() && args.conferFixture === true,
290
291
  };
291
292
  if (args.collocatedText) conferOpts.collocatedText = args.collocatedText;
292
293
  const conferResult = await runConferQuorum(process.cwd(), args.sessionId, conferOpts);
@@ -13,8 +13,7 @@ import * as fs from "fs";
13
13
  import * as path from "path";
14
14
  import { frameworkLogger } from "../core/framework-logger.js";
15
15
  import {
16
- isGovernanceLlmConfigured,
17
- tryLLMGovernance,
16
+ attemptLLMGovernance,
18
17
  } from "../governance/llm-governance-provider.js";
19
18
  import { formatGovernanceVoteText, localConferVote } from "../governance/local-confer.js";
20
19
  import { initializeMemoryRouting } from "../memory-routing/index.js";
@@ -23,6 +22,7 @@ import {
23
22
  formatMemoryRoutingBlock,
24
23
  resolveResearcherMemoryContext,
25
24
  } from "./researcher-confidence.js";
25
+ import { buildHostConferEvidence } from "../nucleus/confer-host.js";
26
26
  import { XrayKnowledgeSkillBase } from "./shared/knowledge-skill-base.js";
27
27
 
28
28
  interface SearchResult {
@@ -465,6 +465,13 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
465
465
  async analyzeProposal(args: AnalyzeProposalArgs): Promise<CallToolResult> {
466
466
  const { proposalTitle = "", proposalDescription = "", evidence = [], proposalType = "" } = args || {};
467
467
 
468
+ if (proposalType === "synthesis-confer") {
469
+ const packet = [proposalTitle, proposalDescription, ...(evidence || [])].filter(Boolean).join("\n\n");
470
+ return {
471
+ content: [{ type: "text", text: buildHostConferEvidence("researcher", packet) }],
472
+ };
473
+ }
474
+
468
475
  const memoryContext = await resolveResearcherMemoryContext({
469
476
  proposalTitle,
470
477
  proposalDescription,
@@ -476,19 +483,21 @@ class XrayLibrarianServer extends XrayKnowledgeSkillBase {
476
483
  enrichedEvidence.push(...buildMemoryRoutingEvidence(memoryContext));
477
484
  }
478
485
 
486
+ const attempt = await attemptLLMGovernance(
487
+ "researcher",
488
+ proposalTitle || "",
489
+ proposalDescription || "",
490
+ enrichedEvidence,
491
+ proposalType || "",
492
+ );
479
493
  const vote =
480
- (await tryLLMGovernance(
481
- "researcher",
482
- proposalTitle || "",
483
- proposalDescription || "",
484
- enrichedEvidence,
485
- proposalType || "",
486
- )) ??
494
+ attempt.vote ??
487
495
  localConferVote({
488
496
  role: "researcher",
489
- llmConfigured: isGovernanceLlmConfigured(),
497
+ llmConfigured: attempt.miss !== "not-configured",
490
498
  highConfidenceTrapPresent: memoryContext?.confidence.highConfidenceTrapPresent,
491
499
  recommendedAgent: memoryContext?.recommendedAgent,
500
+ ...(attempt.miss ? { miss: attempt.miss } : {}),
492
501
  });
493
502
 
494
503
  const memoryRoutingBlock = memoryContext
@@ -72,6 +72,18 @@ The hold lasts for the work that named the plane. A sentence that does not say l
72
72
 
73
73
  Config: `features.json` → `multi_agent_orchestration.lead_dev_mode`
74
74
 
75
+ ## Confer
76
+
77
+ The TUI that is wearing the suit already spawned the MCP servers. Confer uses those servers. It does not start a private server and it does not run `hermes -z`.
78
+
79
+ When `.xray/state/confer-ask-s.1.json`, `confer-ask-s.2.json`, and `confer-ask-s.3.json` are on disk:
80
+
81
+ 1. Call the server and tool named in each ask. Researcher and code-review return evidence. Architect returns the assessment with `Verdict: UNREVIEWED`.
82
+ 2. Write one verdict line from this TUI's model: `Verdict: PASS`, `Verdict: CONDITIONAL`, or `Verdict: FAIL`, plus top risks and a hardening note.
83
+ 3. Call `record_confer_receipt` on `xray-governance` with `todoId`, `subagent`, `sessionId`, and that text.
84
+
85
+ Only `Verdict: PASS` completes the todo. FAIL, CONDITIONAL, and UNREVIEWED stay open. A session id that does not match the plan does not complete the todo. Grok, OpenCode, Hermes, and OpenClaw use these same three steps. Hermes is a voter only when Hermes is the TUI, and then only as that TUI's own model.
86
+
75
87
  ## Lead cadence (syncopation)
76
88
 
77
89
  The seven rules are the engine. The cadence is the time signature. Disk SSOT: `grok-bot/OP-PROC.md` (team specifics live in `house/`).
@@ -5,7 +5,8 @@
5
5
  * Usage:
6
6
  * node scripts/run-live-synthesis-checkpoint.mjs seed [--session-id=ID]
7
7
  * node scripts/run-live-synthesis-checkpoint.mjs status
8
- * node scripts/run-live-synthesis-checkpoint.mjs complete-todo --id=s.1
8
+ * node scripts/run-live-synthesis-checkpoint.mjs complete-todo
9
+ * (refuses: a CLI flag must not mint PASS or clear consult todos)
9
10
  * node scripts/run-live-synthesis-checkpoint.mjs finish
10
11
  */
11
12
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
@@ -132,56 +133,9 @@ async function cmdStatus() {
132
133
  }
133
134
 
134
135
  async function cmdCompleteTodo() {
135
- const todoId = process.argv.find((a) => a.startsWith('--id='))?.split('=')[1];
136
- const verdict = process.argv.find((a) => a.startsWith('--verdict='))?.split('=')[1];
137
- if (!todoId) {
138
- err('Usage: complete-todo --id=s.1 --verdict=PASS|CONDITIONAL|FAIL');
139
- process.exit(1);
140
- }
141
- if (!verdict || !['PASS', 'CONDITIONAL', 'FAIL'].includes(verdict)) {
142
- err('❌ --verdict=PASS|CONDITIONAL|FAIL required (consult receipt gate)');
143
- process.exit(1);
144
- }
145
-
146
- const sessionId = parseSessionId();
147
- const { persistence } = await loadXray();
148
- const { writeSynthesisConsultReceipt } = await import(
149
- join(xrayRoot, 'dist/nucleus/synthesis-consult-receipt.js'),
150
- );
151
-
152
- const plan = persistence.loadPersistedLeadDevPlan(root);
153
- const consultTodo = plan
154
- ? persistence.getSynthesisConsultTodos(plan).find((t) => t.id === todoId)
155
- : null;
156
- if (!consultTodo) {
157
- err(`❌ Consult todo ${todoId} not found in lead-dev plan`);
158
- process.exit(1);
159
- }
160
-
161
- writeSynthesisConsultReceipt(
162
- todoId,
163
- {
164
- sessionId: plan?.sessionId ?? sessionId,
165
- subagent: consultTodo.subagent,
166
- verdict,
167
- topRisks: [],
168
- hardeningNote: process.argv.find((a) => a.startsWith('--note='))?.split('=')[1] ?? '',
169
- },
170
- root,
171
- );
172
-
173
- const updated = persistence.updatePlanTodoStatus(todoId, 'completed', root);
174
- if (!updated) {
175
- err(`❌ Receipt gate blocked completion for ${todoId}`);
176
- process.exit(1);
177
- }
178
-
179
- const refreshed = persistence.loadPersistedLeadDevPlan(root);
180
- const todos = refreshed ? persistence.getSynthesisConsultTodos(refreshed) : [];
181
- const allDone = todos.length > 0 && todos.every((t) => t.status === 'completed');
182
- log(`✅ Marked ${todoId} completed (receipt: ${verdict})`);
183
- log(`📊 consult todos: ${todos.map((t) => `${t.id}:${t.status}`).join(', ')}`);
184
- if (allDone) log('🎉 All consult todos complete — checkpoint should clear on next gate eval');
136
+ err('❌ complete-todo cannot mint a PASS receipt or clear consult todos.');
137
+ err('A consult todo completes only when Confer records a real model approve.');
138
+ process.exit(1);
185
139
  }
186
140
 
187
141
  async function cmdFinish() {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./features.schema.json",
3
- "version": "4.0.41",
3
+ "version": "4.0.42",
4
4
  "description": "0xRay Framework - Unified Feature Configuration",
5
5
  "suit_temperament": {
6
6
  "profile": "auto",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "confer_on_synthesis": {
47
47
  "type": "boolean",
48
- "description": "Auto-run 3-agent confer quorum at synthesis checkpoint (default true)"
48
+ "description": "Opt in to the 3-agent confer quorum. A missing key stays off."
49
49
  },
50
50
  "confer": {
51
51
  "type": "object",