0xray 3.5.3 → 3.5.5

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 (76) hide show
  1. package/.xray/features.json +5 -1
  2. package/.xray/features.schema.json +56 -0
  3. package/AGENTS-consumer.md +1 -1
  4. package/AGENTS.md +11 -1
  5. package/README.md +1 -1
  6. package/SKILLS.md +1 -1
  7. package/dist/AGENTS.md +11 -1
  8. package/dist/CHANGELOG.md +54 -0
  9. package/dist/README.md +1 -1
  10. package/dist/SKILLS.md +1 -1
  11. package/dist/cli/commands/hermes-install.js +3 -1
  12. package/dist/cli/index.js +0 -0
  13. package/dist/core/features-config.d.ts +15 -0
  14. package/dist/governance/governance-service.d.ts +1 -0
  15. package/dist/governance/governance-service.js +81 -6
  16. package/dist/integrations/enforcement-gate.js +15 -5
  17. package/dist/integrations/grok/grok-cli.js +1 -10
  18. package/dist/integrations/grok/hooks/grok-hook-utils.js +46 -2
  19. package/dist/integrations/grok/hooks/post-tool-use.js +48 -14
  20. package/dist/integrations/grok/hooks/pre-tool-use.js +20 -0
  21. package/dist/integrations/grok/hooks/session-start.js +28 -0
  22. package/dist/integrations/hermes-agent/__init__.py +11 -1
  23. package/dist/integrations/hermes-agent/__pycache__/__init__.cpython-313.pyc +0 -0
  24. package/dist/integrations/hermes-agent/__pycache__/schemas.cpython-313.pyc +0 -0
  25. package/dist/integrations/hermes-agent/__pycache__/tools.cpython-313.pyc +0 -0
  26. package/dist/integrations/hermes-agent/bridge.mjs +73 -10
  27. package/dist/integrations/hooks/confer-hook-runtime.mjs +49 -0
  28. package/dist/integrations/hooks/delegation-gate-runtime.mjs +6 -0
  29. package/dist/integrations/hooks/pipeline-hook-runtime.mjs +211 -0
  30. package/dist/integrations/hooks/plan-hook-runtime.mjs +68 -0
  31. package/dist/integrations/hooks/user-aside-hook-runtime.mjs +49 -0
  32. package/dist/mcps/architect-tools.server.js +6 -1
  33. package/dist/mcps/orchestrator/handlers/complexity-handler.d.ts +7 -0
  34. package/dist/mcps/orchestrator/handlers/complexity-handler.js +106 -7
  35. package/dist/mcps/orchestrator/handlers/task-handler.d.ts +6 -0
  36. package/dist/mcps/orchestrator/handlers/task-handler.js +75 -0
  37. package/dist/mcps/orchestrator/server.js +54 -5
  38. package/dist/memory-routing/provider-loader.js +30 -2
  39. package/dist/nucleus/aside-worktree.d.ts +41 -0
  40. package/dist/nucleus/aside-worktree.js +164 -0
  41. package/dist/nucleus/confer.d.ts +57 -0
  42. package/dist/nucleus/confer.js +308 -0
  43. package/dist/nucleus/delegation-gate.d.ts +12 -1
  44. package/dist/nucleus/delegation-gate.js +113 -13
  45. package/dist/nucleus/lead-dev-plan-persistence.d.ts +27 -3
  46. package/dist/nucleus/lead-dev-plan-persistence.js +222 -18
  47. package/dist/nucleus/spawn-plan-resolution.d.ts +20 -0
  48. package/dist/nucleus/spawn-plan-resolution.js +42 -0
  49. package/dist/nucleus/synthesis-consult-receipt.d.ts +32 -0
  50. package/dist/nucleus/synthesis-consult-receipt.js +171 -0
  51. package/dist/nucleus/synthesis-context.js +1 -1
  52. package/dist/nucleus/user-aside.d.ts +70 -0
  53. package/dist/nucleus/user-aside.js +393 -0
  54. package/dist/plugin/xray-codex-injection.js +1 -1
  55. package/dist/postprocessor/PostProcessor.d.ts +1 -0
  56. package/dist/postprocessor/PostProcessor.js +11 -0
  57. package/dist/processors/implementations/session-summary-processor.d.ts +1 -1
  58. package/dist/processors/implementations/session-summary-processor.js +9 -6
  59. package/package.json +14 -5
  60. package/scripts/hooks/install-hooks.cjs +3 -3
  61. package/scripts/hooks/run-hook.js +171 -86
  62. package/scripts/mjs/verify-confer-core.mjs +103 -0
  63. package/scripts/mjs/verify-delegation-gate-core.mjs +48 -2
  64. package/scripts/mjs/verify-hermes-session-start.mjs +137 -0
  65. package/scripts/mjs/verify-pre-push-diff-core.mjs +87 -0
  66. package/scripts/mjs/verify-user-aside-core.mjs +137 -0
  67. package/scripts/node/bridge-mcp-wiring.cjs +10 -0
  68. package/scripts/node/install-bridges.cjs +3 -11
  69. package/scripts/node/load-reflection-config.mjs +2 -2
  70. package/scripts/node/release-gate.mjs +8 -4
  71. package/src/mcps/architect-tools.server.ts +6 -1
  72. package/src/mcps/orchestrator/handlers/complexity-handler.ts +178 -20
  73. package/src/mcps/orchestrator/handlers/task-handler.ts +99 -0
  74. package/src/mcps/orchestrator/server.ts +56 -5
  75. package/xray/features.json +5 -1
  76. package/xray/features.schema.json +56 -0
@@ -36,7 +36,11 @@
36
36
  "phased_plan_threshold": 25,
37
37
  "auto_consult_major_work": true,
38
38
  "no_new_surface": true,
39
- "auto_chain_delegations": true
39
+ "auto_chain_delegations": true,
40
+ "user_asides": {
41
+ "enabled": true,
42
+ "auto_provision_worktree": false
43
+ }
40
44
  },
41
45
  "autonomous_reporting": {
42
46
  "enabled": true,
@@ -28,6 +28,62 @@
28
28
  "required": ["module_path"]
29
29
  }
30
30
  },
31
+ "multi_agent_orchestration": {
32
+ "type": "object",
33
+ "properties": {
34
+ "enabled": { "type": "boolean" },
35
+ "lead_dev_mode": { "type": "boolean" },
36
+ "plan_stale_hours": {
37
+ "type": "number",
38
+ "minimum": 1,
39
+ "description": "Archive lead-dev-plan when all todos still pending after N hours"
40
+ },
41
+ "plan_archive_marker_hours": {
42
+ "type": "number",
43
+ "minimum": 1,
44
+ "description": "How long spawn-plan-stale persists after a stale plan was archived (hours)"
45
+ },
46
+ "confer_on_synthesis": {
47
+ "type": "boolean",
48
+ "description": "Auto-run 3-agent confer quorum at synthesis checkpoint (default true)"
49
+ },
50
+ "confer": {
51
+ "type": "object",
52
+ "properties": {
53
+ "enabled": { "type": "boolean" },
54
+ "on_synthesis": { "type": "boolean" }
55
+ },
56
+ "description": "Mandatory researcher/architect-tools/code-review quorum"
57
+ },
58
+ "sibling_repos": {
59
+ "type": "array",
60
+ "items": {
61
+ "oneOf": [
62
+ { "type": "string" },
63
+ {
64
+ "type": "object",
65
+ "properties": {
66
+ "path": { "type": "string" },
67
+ "label": { "type": "string" }
68
+ },
69
+ "required": ["path"]
70
+ }
71
+ ]
72
+ },
73
+ "description": "Sibling workspace roots (e.g. UI repo when MCP host is cwd)"
74
+ },
75
+ "user_asides": {
76
+ "type": "object",
77
+ "properties": {
78
+ "enabled": { "type": "boolean" },
79
+ "auto_provision_worktree": {
80
+ "type": "boolean",
81
+ "description": "Run guarded git worktree add on analyze-complexity aside intake when worktree+branch set"
82
+ }
83
+ }
84
+ }
85
+ }
86
+ },
31
87
  "synthesis": {
32
88
  "type": "object",
33
89
  "properties": {
@@ -4,7 +4,7 @@
4
4
 
5
5
  Quick reference for the 0xRay AI orchestration framework (shipped to your project on `npm install 0xray`).
6
6
 
7
- **v3.5.3** — 7 MCP servers · 45 skills · 68 codex terms · 4 platform bridges
7
+ **v3.5.5** — 7 MCP servers · 45 skills · 68 codex terms · 4 platform bridges
8
8
 
9
9
  ## Postinstall (automatic)
10
10
 
package/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # xray Agents
2
2
 
3
- Quick reference for the xray AI orchestration framework (**v3.5.3**).
3
+ Quick reference for the xray AI orchestration framework (**v3.5.5**).
4
4
 
5
5
  **42 agents** · **45 skills** · **7 consumer MCP servers** · **68 codex terms** · **3,226 tests**
6
6
 
@@ -77,6 +77,16 @@ Governance deliberation: **code-review**, **security-audit**, **researcher** wit
77
77
  - Observation extractors: governance, orchestration, complexity
78
78
  - Repertoire context flows via `ExecutionPlan.memoryContext`
79
79
 
80
+ ## User asides (parallel work tracks)
81
+
82
+ **Not** AsideContext — user-declared parallel tracks (worktree-like):
83
+
84
+ - SSOT: `.xray/state/asides/{id}.json` + `_active.json`; todos use `{asideId}.a.*` (namespaced)
85
+ - Intake: `analyze-complexity` with `userAsideId` (+ optional `worktree`, `branch`)
86
+ - Activate / resume main: `orchestrate-task` with `userAsideId` or `clearActiveAside: true`
87
+ - Spawn gate routes to active aside plan; session-boot exposes `activeAside`
88
+ - Config: `features.json` → `multi_agent_orchestration.user_asides.enabled`
89
+
80
90
  ## Confidence gate (v3.3.1)
81
91
 
82
92
  `ExecutionPlanner.calculateTaskComplexity()` + `getTaskConfidence()` — complexity boost and ontological-trap routing.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # xray — MCP-Centric AI Governance OS
2
2
 
3
- **v3.5.3** — 42 agents · 45 skills · 7 MCP servers · 68 codex terms · 3,226 tests
3
+ **v3.5.5** — 42 agents · 45 skills · 7 MCP servers · 68 codex terms · 3,226 tests
4
4
 
5
5
  ## Your AI Power Suit
6
6
 
package/SKILLS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 0xray Skills
2
2
 
3
- **v3.5.3** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
3
+ **v3.5.5** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
4
4
 
5
5
  xray ships 45 knowledge skills (`src/skills/<name>/SKILL.md`) across the full software lifecycle. Lead-dev operating model lives in **`orchestrator`** skill + `multi_agent_orchestration.lead_dev_mode` in features.json. Skills load on demand via the **xray-skills** MCP server (`npx -y 0xray mcp skills`), agent `@mentions`, or platform skill directories after postinstall.
6
6
 
package/dist/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # xray Agents
2
2
 
3
- Quick reference for the xray AI orchestration framework (**v3.5.3**).
3
+ Quick reference for the xray AI orchestration framework (**v3.5.5**).
4
4
 
5
5
  **42 agents** · **45 skills** · **7 consumer MCP servers** · **68 codex terms** · **3,226 tests**
6
6
 
@@ -77,6 +77,16 @@ Governance deliberation: **code-review**, **security-audit**, **researcher** wit
77
77
  - Observation extractors: governance, orchestration, complexity
78
78
  - Repertoire context flows via `ExecutionPlan.memoryContext`
79
79
 
80
+ ## User asides (parallel work tracks)
81
+
82
+ **Not** AsideContext — user-declared parallel tracks (worktree-like):
83
+
84
+ - SSOT: `.xray/state/asides/{id}.json` + `_active.json`; todos use `{asideId}.a.*` (namespaced)
85
+ - Intake: `analyze-complexity` with `userAsideId` (+ optional `worktree`, `branch`)
86
+ - Activate / resume main: `orchestrate-task` with `userAsideId` or `clearActiveAside: true`
87
+ - Spawn gate routes to active aside plan; session-boot exposes `activeAside`
88
+ - Config: `features.json` → `multi_agent_orchestration.user_asides.enabled`
89
+
80
90
  ## Confidence gate (v3.3.1)
81
91
 
82
92
  `ExecutionPlanner.calculateTaskComplexity()` + `getTaskConfidence()` — complexity boost and ontological-trap routing.
package/dist/CHANGELOG.md CHANGED
@@ -4,6 +4,60 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Conventional Commits](https://www.conventionalcommits.org/).
6
6
 
7
+ ## [3.5.5] - 2026-06-20
8
+
9
+ ### ✨ Features
10
+ - **User-asides P0** — PreToolUse **denies** aside spawns without `cwd` / `working_directory` matching `aside.worktree`; `aside-worktree.ts` SSOT; synthesis-gate parity
11
+ - **Worktree auto-provision** — `analyze-complexity` runs guarded `git worktree add` when `user_asides.auto_provision_worktree: true`
12
+ - **Pipeline P3** — `verify:pipeline-facets` script; consumer `confirm:suit:full --pipeline-audit` wiring
13
+
14
+ ### 🐛 Fixes
15
+ - **Worktree cwd enforce** — `auto_chain_delegations: false` no longer bypasses aside worktree cwd check; synthesis-gate parity retained
16
+ - **`isAutoProvisionWorktreeEnabled`** — requires `user_asides.enabled` before honoring `auto_provision_worktree`
17
+ - **`provisionGitWorktree`** — macOS `/var` vs `/private/var` path resolution for idempotent worktree detection
18
+
19
+ ### 🔄 Changes
20
+ - **`verify:pipeline-facets --package-only`** — release-gate probes shipped artifacts without consumer feature matrix
21
+ - **`release-gate.mjs`** — wires pipeline facet verify (package-only mode)
22
+
23
+ ### ✅ Tests
24
+ - `aside-worktree.test.ts`; delegation-gate cwd deny/allow; auto_chain-off + synthesis worktree deny; `provisionGitWorktree` guards; `verify-user-aside-core` 10/10
25
+
26
+ ---
27
+
28
+ ## [3.5.4] - 2026-06-19
29
+
30
+ ### ✨ Features
31
+ - **Synthesis consult receipt gate** — s.1–s.3 todos require `.xray/state/synthesis-consult-{id}.json` before completion; `evaluatePostToolSpawn` records receipts from subagent output; Grok post-tool hook wired
32
+ - **User asides** — parallel work tracks (worktree-like) via `.xray/state/asides/{id}.json`; namespaced `{asideId}.a.*` todo routing; `analyze-complexity` + `orchestrate-task` rewire (no new MCP); session-boot hints; synthesis/pending bypass for aside spawns; `verify:user-aside` 9/9; docs `guides/user-asides.md`
33
+ - **Confer quorum** — `confer.ts` SSOT; auto-runs at synthesis `analyze-complexity` (researcher → architect-tools → code-review); `verify:confer` 7/7; config `confer_on_synthesis` (default true); session-boot `conferPending` hint; emoji agent panel in quorum report
34
+ - **Pipeline integration (P0–P2)** — enforcement-gate `{ proposals: [...] }` shape; `applyDecisionMatrix` + `calculateMetamorphosisScore` in `GovernanceService`; Grok `pipeline-hook-runtime.mjs` (reflection stubs, routing outcomes, reporting, inference-improvement light); `SelfProposalEngine` wired in PostProcessor; aside worktree cwd warn in delegation-gate; `autonomous_reporting` config key fix in SessionSummaryProcessor
35
+ - **Stale lead-dev plan** — archive after `plan_stale_hours`; `spawn-plan-stale` persists after archival via recent archive marker; synthesis realignment (s.1–s.3) exempt until consult todos complete
36
+ - **Hermes session-start parity** — `bridge.mjs` `session-start` command archives stale plans; lazy `plan-hook-runtime.mjs`; `verify-hermes-session-start.mjs` (4/4)
37
+ - **Pre-commit / pre-push codex** — diff-hunk and commit-range scope verifiers (`verify:pre-commit-diff`, `verify:pre-push-diff`)
38
+ - **Reflection noise** — minimal defaults 50 commits / `autoGenerate: false`; shell fallbacks synced
39
+ - **Multi-repo boot** — `sibling_repos` in `session-boot.json`
40
+
41
+ ### 🔄 Changes
42
+ - **autonomy-command.md** — SSOT is `analyze-complexity` + `multi_agent_orchestration`; removed stale `autonomy-intake` / `autonomy_kernel` references
43
+
44
+ ### ✅ Tests
45
+ - `synthesis-consult-receipt.test.ts`; E2E/outcome verifiers write fixture receipts
46
+ - `governance-service-metamorphosis.test.ts`; `confer.test.ts` emoji panel
47
+
48
+ ---
49
+
50
+ ## [Unreleased]
51
+
52
+ ### Planned (iterate before next release)
53
+ - **Configurable archive marker** — `plan_archive_marker_hours` controls `spawn-plan-stale` persistence after archival
54
+ - **Verifier coverage** — `spawn-plan-stale` in `verify-delegation-gate-core` (5/5); wired into `release-gate`
55
+ - **Post-commit log maintenance** — consumer path fix (`node_modules/0xray/dist`)
56
+ - **grok_postprocessor_light** — opt-in marker + activity.log on Grok write tools (full PP subset deferred)
57
+ - **P2 session capture** — git post-commit → `captureSessionInference` when threshold met
58
+
59
+ ---
60
+
7
61
  ## [3.5.3] - 2026-06-19
8
62
 
9
63
  ### 🔄 Changes
package/dist/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # xray — MCP-Centric AI Governance OS
2
2
 
3
- **v3.5.3** — 42 agents · 45 skills · 7 MCP servers · 68 codex terms · 3,226 tests
3
+ **v3.5.5** — 42 agents · 45 skills · 7 MCP servers · 68 codex terms · 3,226 tests
4
4
 
5
5
  ## Your AI Power Suit
6
6
 
package/dist/SKILLS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 0xray Skills
2
2
 
3
- **v3.5.3** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
3
+ **v3.5.5** — **45 skills** · invoked via `xray-skills` MCP · synced on postinstall
4
4
 
5
5
  xray ships 45 knowledge skills (`src/skills/<name>/SKILL.md`) across the full software lifecycle. Lead-dev operating model lives in **`orchestrator`** skill + `multi_agent_orchestration.lead_dev_mode` in features.json. Skills load on demand via the **xray-skills** MCP server (`npx -y 0xray mcp skills`), agent `@mentions`, or platform skill directories after postinstall.
6
6
 
@@ -6,7 +6,8 @@ import { frameworkLogger } from '../../core/framework-logger.js';
6
6
  import { syncBuiltinSkills } from './skill-install.js';
7
7
  const __dirname = path.dirname(new URL(import.meta.url).pathname);
8
8
  const require = createRequire(import.meta.url);
9
- const wiring = require(path.join(__dirname, '..', '..', '..', 'scripts', 'node', 'bridge-mcp-wiring.cjs'));
9
+ const packageRoot = path.join(__dirname, '..', '..', '..');
10
+ const wiring = require(path.join(packageRoot, 'scripts', 'node', 'bridge-mcp-wiring.cjs'));
10
11
  export function registerHermesCommands(hermesCmd) {
11
12
  hermesCmd
12
13
  .command('install')
@@ -62,6 +63,7 @@ async function installForHermes(options = {}) {
62
63
  }
63
64
  frameworkLogger.log('hermes-integration', 'skills-synced', 'info', { count: skillsCopied });
64
65
  }
66
+ wiring.copyHermesFindProjectRootHelper(packageRoot, targetPluginDir);
65
67
  const wired = wiring.wireHermesBridge(targetDir);
66
68
  console.log(`\x1b[32m✓ Wired Hermes mcp_servers (${wired.count} servers)\x1b[0m`);
67
69
  console.log(`\x1b[32m✓ Consumer root → ${targetDir}\x1b[0m`);
package/dist/cli/index.js CHANGED
File without changes
@@ -65,6 +65,21 @@ export interface MultiAgentOrchestrationConfig {
65
65
  no_new_surface?: boolean;
66
66
  /** 3.5.0 — PreToolUse gate after delegate-deferred (default true when lead_dev_mode) */
67
67
  auto_chain_delegations?: boolean;
68
+ /** Hours before unstarted lead-dev plan is archived (default 8) */
69
+ plan_stale_hours?: number;
70
+ /** Hours spawn-plan-stale persists after archival (default 24) */
71
+ plan_archive_marker_hours?: number;
72
+ /** Sibling workspace roots for multi-repo session boot metadata */
73
+ sibling_repos?: Array<string | {
74
+ path: string;
75
+ label?: string;
76
+ }>;
77
+ /** Auto-run 3-agent confer at synthesis checkpoint */
78
+ confer_on_synthesis?: boolean;
79
+ confer?: {
80
+ enabled?: boolean;
81
+ on_synthesis?: boolean;
82
+ };
68
83
  }
69
84
  /** Reflection triggers merged from legacy auto_reflection when synthesis is configured */
70
85
  export interface SynthesisReflectionConfig {
@@ -29,6 +29,7 @@ export declare class GovernanceService {
29
29
  * Dynamo Solar SSOT is treated as a hard requirement by default.
30
30
  */
31
31
  govern(request: GovernanceRequest): Promise<GovernanceResponse>;
32
+ private resolveRequireExternalDynamo;
32
33
  private runGovernanceWithTimeout;
33
34
  private callSkillServer;
34
35
  private callExternalDynamo;
@@ -18,8 +18,9 @@
18
18
  import { mcpClientManager } from '../mcps/mcp-client.js';
19
19
  import { callInProcessSkill } from '../mcps/in-process-skill-registry.js';
20
20
  import { getGovernanceIntegration, } from '../integrations/governance/index.js';
21
- import { mergeVotes } from './governance-core.js';
21
+ import { applyDecisionMatrix, calculateMetamorphosisScore, mergeVotes, } from './governance-core.js';
22
22
  import { frameworkLogger } from '../core/framework-logger.js';
23
+ import { featuresConfigLoader } from '../core/features-config.js';
23
24
  export class GovernanceService {
24
25
  constructor() {
25
26
  // We deliberately do *not* cache the integration here.
@@ -39,7 +40,7 @@ export class GovernanceService {
39
40
  */
40
41
  async govern(request) {
41
42
  const { proposals, context, options } = request;
42
- const requireExternal = options?.requireExternalDynamo ?? true;
43
+ const requireExternal = this.resolveRequireExternalDynamo(options?.requireExternalDynamo);
43
44
  const timeoutMs = options?.timeoutMs ?? 90000;
44
45
  const maxAbstentionThreshold = options?.maxAbstentionThreshold ?? 1.0;
45
46
  frameworkLogger.log('governance-service', 'govern-start', 'info', {
@@ -59,7 +60,7 @@ export class GovernanceService {
59
60
  }
60
61
  }
61
62
  // Run governance with end-to-end timeout
62
- const result = await this.runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs);
63
+ const result = await this.runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs, options?.metamorphosisThreshold);
63
64
  // Check abstention threshold
64
65
  if (result.results.length > 0 && maxAbstentionThreshold < 1.0) {
65
66
  const totalVotes = result.results.reduce((sum, r) => sum + r.votes.length, 0);
@@ -91,7 +92,26 @@ export class GovernanceService {
91
92
  },
92
93
  };
93
94
  }
94
- async runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs) {
95
+ resolveRequireExternalDynamo(explicit) {
96
+ if (explicit !== undefined)
97
+ return explicit;
98
+ if (process.env.XRAY_LOCAL_MODE === '1' || process.env.XRAY_LOCAL_MODE === 'true') {
99
+ return false;
100
+ }
101
+ try {
102
+ const config = featuresConfigLoader.loadConfig();
103
+ const ig = config.inference_governance;
104
+ if (ig?.require_external_dynamo === false)
105
+ return false;
106
+ if (ig?.local_mode === true)
107
+ return false;
108
+ }
109
+ catch {
110
+ /* default strict */
111
+ }
112
+ return true;
113
+ }
114
+ async runGovernanceWithTimeout(proposals, context, requireExternal, timeoutMs, metamorphosisThreshold = 0.7) {
95
115
  const controller = new AbortController();
96
116
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
97
117
  try {
@@ -112,12 +132,67 @@ export class GovernanceService {
112
132
  ...(externalVotes[index] || []),
113
133
  ];
114
134
  const merged = mergeVotes(votes);
135
+ const externalVote = votes.find((v) => v.server === 'external-dynamo');
136
+ let finalDecision = merged.finalDecision;
137
+ let averageConfidence = merged.averageConfidence;
138
+ let moralOverride = 'none';
139
+ if (externalVote?.moralTension ||
140
+ externalVote?.moralScore != null ||
141
+ externalVote?.moralFusion != null) {
142
+ const matrixInput = {
143
+ resonance: averageConfidence,
144
+ isotopicRatio: externalVote.moralFusion ?? 0.5,
145
+ };
146
+ if (externalVote.moralTension != null) {
147
+ matrixInput.moralTension = externalVote.moralTension;
148
+ }
149
+ if (externalVote.moralScore != null) {
150
+ matrixInput.moralScore = externalVote.moralScore;
151
+ }
152
+ if (externalVote.moralFusion != null) {
153
+ matrixInput.moralFusion = externalVote.moralFusion;
154
+ }
155
+ const matrix = applyDecisionMatrix(matrixInput);
156
+ moralOverride = matrix.moralOverride ?? 'none';
157
+ if (matrix.recommendation === 'REJECT') {
158
+ finalDecision = 'reject';
159
+ }
160
+ else if (matrix.recommendation === 'NEEDS_REVISION' && finalDecision === 'approve') {
161
+ finalDecision = 'needs_revision';
162
+ }
163
+ else if (matrix.recommendation === 'PASS' && finalDecision !== 'reject') {
164
+ finalDecision = 'approve';
165
+ }
166
+ averageConfidence = matrix.confidence;
167
+ }
168
+ let metamorphosisScore;
169
+ if (proposal.type === 'metamorphosis' || proposal.source === 'metamorphosis') {
170
+ const proposalType = proposal.type === 'compliance'
171
+ ? 'compliance'
172
+ : proposal.type === 'automate' || proposal.type === 'codify'
173
+ ? proposal.type
174
+ : 'fix';
175
+ const metamorphosisInput = {
176
+ averageConfidence,
177
+ proposalType,
178
+ resonanceScore: averageConfidence,
179
+ };
180
+ if (externalVote?.moralFusion != null) {
181
+ metamorphosisInput.historicalCoherence = externalVote.moralFusion;
182
+ }
183
+ metamorphosisScore = calculateMetamorphosisScore(metamorphosisInput);
184
+ if (metamorphosisScore < metamorphosisThreshold) {
185
+ finalDecision = 'needs_revision';
186
+ }
187
+ }
115
188
  return {
116
189
  proposalId: proposal.id,
117
- finalDecision: merged.finalDecision,
118
- averageConfidence: merged.averageConfidence,
190
+ finalDecision,
191
+ averageConfidence,
119
192
  votes,
120
193
  reasoningSummary: merged.reasoningSummary,
194
+ moralOverride,
195
+ ...(metamorphosisScore != null ? { metamorphosisScore } : {}),
121
196
  };
122
197
  });
123
198
  return { results };
@@ -252,12 +252,22 @@ export async function afterToolHook(tool, args, _result, _error, _context) {
252
252
  const result = _result;
253
253
  if (result && typeof result === "object" && "title" in result && "description" in result) {
254
254
  const { handleGovernRequest } = await import("../nucleus/index.js");
255
+ const localMode = process.env.XRAY_LOCAL_MODE === "1" || process.env.XRAY_LOCAL_MODE === "true";
255
256
  const governanceResult = await handleGovernRequest({
256
- proposalId: `gate-${Date.now()}`,
257
- title: String(result.title || ""),
258
- description: String(result.description || ""),
259
- type: String(result.type || "fix"),
260
- content: result,
257
+ proposals: [
258
+ {
259
+ id: `gate-${Date.now()}`,
260
+ type: String(result.type || "fix"),
261
+ title: String(result.title || ""),
262
+ description: String(result.description || ""),
263
+ evidence: [],
264
+ source: "manual",
265
+ metadata: result,
266
+ },
267
+ ],
268
+ options: {
269
+ requireExternalDynamo: localMode ? false : undefined,
270
+ },
261
271
  });
262
272
  governanceTriggered = governanceResult?.overallDecision === 'approve';
263
273
  if (governanceTriggered) {
@@ -25,16 +25,7 @@ const __dirname = path.dirname(new URL(import.meta.url).pathname);
25
25
  const packageRoot = path.resolve(__dirname, '..', '..', '..');
26
26
  const requireCjs = createRequire(import.meta.url);
27
27
  const { resolveConsumerTargetDir } = requireCjs(path.join(packageRoot, 'scripts/node/install-bridges.cjs'));
28
- /** Canonical 7-server MCP surface — matches install-bridges.cjs and package .mcp.json */
29
- const XRAY_MCP_SERVERS = [
30
- { name: 'xray-governance', mcpCmd: 'governance', env: { XRAY_FORCE_MCP_GOVERNANCE: 'true' } },
31
- { name: 'xray-skills', mcpCmd: 'skills', env: {} },
32
- { name: 'xray-orchestrator', mcpCmd: 'orchestrator', env: {} },
33
- { name: 'xray-enforcer', mcpCmd: 'enforcer', env: {} },
34
- { name: 'xray-researcher', mcpCmd: 'researcher', env: {} },
35
- { name: 'xray-code-review', mcpCmd: 'code-review', env: {} },
36
- { name: 'xray-architect-tools', mcpCmd: 'architect-tools', env: {} },
37
- ];
28
+ const { XRAY_MCP_SERVERS } = requireCjs(path.join(packageRoot, 'scripts/node/bridge-mcp-wiring.cjs'));
38
29
  function registerGrokMcpServers(targetDir) {
39
30
  try {
40
31
  execSync('which grok', { stdio: 'ignore' });
@@ -18,6 +18,8 @@ import {
18
18
  validateSpawnMatchesTodo,
19
19
  updatePlanTodoStatusInPlace,
20
20
  } from '../../hooks/delegation-gate-runtime.mjs';
21
+ import { isConferPendingForSession } from '../../hooks/confer-hook-runtime.mjs';
22
+ import { getActiveUserAsideBoot } from '../../hooks/user-aside-hook-runtime.mjs';
21
23
 
22
24
  export {
23
25
  checkPendingDelegationGate,
@@ -105,6 +107,30 @@ export function resolveCodexPath(root = workspaceRoot()) {
105
107
  return null;
106
108
  }
107
109
 
110
+ export function resolveSiblingWorkspaceRoots(root = workspaceRoot()) {
111
+ const featuresPath = resolveFeaturesPath(root);
112
+ if (!featuresPath) return [];
113
+ try {
114
+ const data = JSON.parse(fs.readFileSync(featuresPath, 'utf8'));
115
+ const siblings = data.multi_agent_orchestration?.sibling_repos ?? [];
116
+ if (!Array.isArray(siblings)) return [];
117
+ const resolved = [];
118
+ for (const entry of siblings) {
119
+ const rel = typeof entry === 'string' ? entry : entry?.path;
120
+ if (!rel || typeof rel !== 'string') continue;
121
+ const abs = path.isAbsolute(rel) ? rel : path.resolve(root, rel);
122
+ if (!fs.existsSync(abs)) continue;
123
+ resolved.push({
124
+ path: abs,
125
+ label: typeof entry === 'object' && entry.label ? entry.label : path.basename(abs),
126
+ });
127
+ }
128
+ return resolved;
129
+ } catch {
130
+ return [];
131
+ }
132
+ }
133
+
108
134
  export function loadFeatures(root = workspaceRoot()) {
109
135
  const featuresPath = resolveFeaturesPath(root);
110
136
  if (!featuresPath) return { lead_dev_mode: true, no_new_surface: true, per_suite_triage: true };
@@ -116,6 +142,7 @@ export function loadFeatures(root = workspaceRoot()) {
116
142
  no_new_surface: orch.no_new_surface !== false,
117
143
  per_suite_test_triage: orch.per_suite_test_triage !== false,
118
144
  auto_chain_delegations: orch.auto_chain_delegations !== false,
145
+ sibling_repos: resolveSiblingWorkspaceRoots(root),
119
146
  };
120
147
  } catch {
121
148
  return {
@@ -123,6 +150,7 @@ export function loadFeatures(root = workspaceRoot()) {
123
150
  no_new_surface: true,
124
151
  per_suite_triage: true,
125
152
  auto_chain_delegations: true,
153
+ sibling_repos: [],
126
154
  };
127
155
  }
128
156
  }
@@ -215,9 +243,22 @@ export function sessionBootPath(root = workspaceRoot()) {
215
243
  return path.join(root, '.xray', 'state', 'session-boot.json');
216
244
  }
217
245
 
246
+ export function loadConferPending(root = workspaceRoot(), sessionId = null) {
247
+ try {
248
+ return isConferPendingForSession(root, sessionId);
249
+ } catch {
250
+ return false;
251
+ }
252
+ }
253
+
218
254
  export function buildSessionBootPayload(root, source = '0xray/grok-session-start', extra = {}) {
219
- const features = loadFeatures();
255
+ const features = loadFeatures(root);
220
256
  const blockingTerms = loadBlockingCodexTerms();
257
+ const siblingRoots = features.sibling_repos ?? resolveSiblingWorkspaceRoots(root);
258
+ const sessionId =
259
+ extra.sessionId || process.env.GROK_SESSION_ID || process.env.GROK_SESSION || null;
260
+ const conferPending = loadConferPending(root, sessionId);
261
+ const userAsideBoot = getActiveUserAsideBoot(root, sessionId);
221
262
  return {
222
263
  hook: source,
223
264
  lead_dev_mode: features.lead_dev_mode,
@@ -228,7 +269,10 @@ export function buildSessionBootPayload(root, source = '0xray/grok-session-start
228
269
  mcpIntake: 'xray-orchestrator → analyze-complexity (required before spawn_subagent)',
229
270
  enforcement: 'PreToolUse hook — codex patterns + surface area + spawn gate',
230
271
  workspaceRoot: root,
231
- sessionId: process.env.GROK_SESSION_ID || null,
272
+ ...(siblingRoots.length > 0 ? { siblingWorkspaceRoots: siblingRoots } : {}),
273
+ ...(conferPending ? { conferPending: true, conferTrigger: 'analyze-complexity at synthesis checkpoint' } : {}),
274
+ ...(userAsideBoot ?? {}),
275
+ sessionId,
232
276
  timestamp: new Date().toISOString(),
233
277
  source,
234
278
  ...extra,
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Grok PostToolUse — auto-chain observability + targeted clear on Task spawn.
3
+ * Grok PostToolUse — auto-chain observability + consult receipt gate on Task spawn.
4
4
  * Passive hook: stdout ignored by Grok; side effects only.
5
5
  */
6
6
 
@@ -10,12 +10,25 @@ import {
10
10
  isSubagentTool,
11
11
  readStdinJson,
12
12
  resolveSessionId,
13
- satisfyDelegationsFromToolInput,
14
- validateSpawnMatchesTodo,
15
- updatePlanTodoStatusInPlace,
16
13
  workspaceRoot,
17
14
  } from './grok-hook-utils.js';
18
15
  import { appendHookActivity } from './grok-hook-activity.js';
16
+ import {
17
+ evaluatePostToolSpawn,
18
+ isOrchestrateToolEvent as gateIsOrchestrateToolEvent,
19
+ } from '../../hooks/delegation-gate-runtime.mjs';
20
+ import { recordRoutingOutcome } from '../../hooks/pipeline-hook-runtime.mjs';
21
+
22
+ function extractToolOutput(event) {
23
+ return (
24
+ event.toolOutput ??
25
+ event.toolResult ??
26
+ event.result ??
27
+ event.output ??
28
+ event.tool_output ??
29
+ null
30
+ );
31
+ }
19
32
 
20
33
  async function main() {
21
34
  const root = workspaceRoot();
@@ -25,28 +38,49 @@ async function main() {
25
38
  const sessionId = resolveSessionId(event);
26
39
  const { toolName, toolInput } = extractToolContext(event);
27
40
 
28
- if (isOrchestrateToolEvent(toolName, toolInput)) {
41
+ if (isOrchestrateToolEvent(toolName, toolInput) || gateIsOrchestrateToolEvent(toolName, toolInput)) {
29
42
  appendHookActivity(eventRoot, 'grok-post-tool-use', 'auto-chain-pending', 'info', {
30
43
  tool: toolName,
31
44
  sessionId,
32
45
  note: 'orchestrate-task completed — pending-delegations.json written by task-handler',
33
46
  });
47
+ try {
48
+ recordRoutingOutcome(eventRoot, {
49
+ tool: toolName,
50
+ agent: toolInput?.subagent_type ?? toolInput?.agent ?? 'orchestrate-task',
51
+ planTodoId: toolInput?.planTodoId ?? null,
52
+ sessionId,
53
+ });
54
+ } catch {
55
+ /* non-blocking */
56
+ }
34
57
  }
35
58
 
36
59
  if (isSubagentTool(toolName)) {
37
- const spawnCheck = validateSpawnMatchesTodo(toolInput, eventRoot);
38
- if (spawnCheck.valid && spawnCheck.expectedTodoId) {
39
- updatePlanTodoStatusInPlace(spawnCheck.expectedTodoId, 'completed', eventRoot);
40
- }
60
+ const spawnResult = evaluatePostToolSpawn(toolName, toolInput, eventRoot, {
61
+ toolOutput: extractToolOutput(event),
62
+ sessionId,
63
+ });
41
64
 
42
- const result = satisfyDelegationsFromToolInput(toolInput, eventRoot);
43
- if (result.satisfied.length > 0) {
65
+ if (spawnResult.satisfied.length > 0 || spawnResult.expectedTodoId) {
44
66
  appendHookActivity(eventRoot, 'grok-post-tool-use', 'auto-chain-cleared', 'success', {
45
67
  tool: toolName,
46
- satisfied: result.satisfied.map((d) => d.id),
47
- clearedAll: result.clearedAll,
48
- planTodoId: spawnCheck.expectedTodoId ?? null,
68
+ satisfied: spawnResult.satisfied.map((d) => d.id),
69
+ clearedAll: spawnResult.clearedAll,
70
+ planTodoId: spawnResult.expectedTodoId ?? null,
71
+ receiptRecorded: spawnResult.receiptRecorded ?? false,
72
+ todoCompleted: spawnResult.todoCompleted ?? false,
73
+ });
74
+ }
75
+ try {
76
+ recordRoutingOutcome(eventRoot, {
77
+ tool: toolName,
78
+ agent: toolInput?.subagent_type ?? toolInput?.agent ?? 'subagent',
79
+ planTodoId: toolInput?.planTodoId ?? spawnResult.expectedTodoId ?? null,
80
+ sessionId,
49
81
  });
82
+ } catch {
83
+ /* non-blocking */
50
84
  }
51
85
  }
52
86