0xray 4.0.21 → 4.0.22
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.
- package/.grok-plugin/plugin.json +1 -1
- package/.opencode/skills/hermes-agent/SKILL.md +1 -1
- package/.opencode/skills/orchestrator/SKILL.md +22 -0
- package/.xray/features.json +1 -1
- package/AGENTS-consumer.md +11 -11
- package/AGENTS.md +8 -8
- package/README.md +27 -27
- package/SKILLS.md +1 -1
- package/dist/AGENTS.md +8 -8
- package/dist/CHANGELOG.md +75 -0
- package/dist/README.md +27 -27
- package/dist/SKILLS.md +1 -1
- package/dist/governance/governance-core.d.ts +2 -1
- package/dist/governance/governance-core.js +20 -11
- package/dist/governance/governance-service.js +9 -7
- package/dist/inference/inference-cycle.js +22 -5
- package/dist/integrations/cursor/README.md +1 -1
- package/dist/integrations/cursor/hooks/cursor-hook-utils.js +19 -0
- package/dist/integrations/cursor/hooks/pre-tool-use.js +2 -1
- package/dist/integrations/hooks/station-hook-runtime.cjs +90 -30
- package/dist/integrations/hooks/station-hook-runtime.mjs +1 -0
- package/dist/integrations/hooks/station-memory-ingest.mjs +4 -2
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/memory-routing/provider-loader.d.ts +3 -1
- package/dist/memory-routing/provider-loader.js +3 -1
- package/dist/nucleus/thin-dispatch.js +11 -2
- package/dist/processors/implementations/agents-md-validation-processor.js +1 -1
- package/dist/skills/hermes-agent/SKILL.md +1 -1
- package/dist/skills/orchestrator/SKILL.md +22 -0
- package/package.json +2 -2
- package/scripts/foundry/CHANGELOG.md +2 -0
- package/scripts/foundry/README.md +2 -2
- package/scripts/foundry/cli.mjs +1 -1
- package/scripts/foundry/inspect.mjs +160 -5
- package/scripts/foundry/plant/skills/inspect/SKILL.md +2 -0
- package/scripts/foundry/validate-release-docs.mjs +24 -20
- package/scripts/foundry/version-manager.mjs +127 -7
- package/scripts/mjs/test-consumer-readiness.mjs +10 -3
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/skills/hermes-agent/SKILL.md +1 -1
- package/src/skills/orchestrator/SKILL.md +22 -0
- package/vendor/@0xray/repertoire/data/stack-overlay.json +154 -1
- package/vendor/@0xray/repertoire/dist/RepertoireService.js +7 -6
- package/vendor/@0xray/repertoire/dist/ingestion/xray-session-ingester.js +10 -11
- package/vendor/@0xray/repertoire/dist/paths.js +66 -4
- package/vendor/@0xray/repertoire/dist/registry/CuratedSignalsManager.js +54 -14
- package/xray/agents_template.md +10 -5
- package/xray/features.json +1 -1
- package/dist/mcps/shared/prompt-security-validator.d.ts +0 -44
- package/dist/mcps/shared/prompt-security-validator.js +0 -148
- package/dist/mcps/shared/security-scanner.d.ts +0 -119
- package/dist/mcps/shared/security-scanner.js +0 -429
- package/dist/scripts/activate-kernel-pipeline.d.ts +0 -7
- package/dist/scripts/activate-kernel-pipeline.js +0 -101
- package/src/mcps/shared/prompt-security-validator.ts +0 -199
- package/src/mcps/shared/security-scanner.ts +0 -599
|
@@ -182,6 +182,25 @@ export function cursorHeatRoots(event = {}) {
|
|
|
182
182
|
return out;
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
function packageNameAt(root) {
|
|
186
|
+
try {
|
|
187
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
188
|
+
return typeof pkg.name === 'string' ? pkg.name : '';
|
|
189
|
+
} catch {
|
|
190
|
+
return '';
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Constitution follows the suit mill when that package is in the heat set.
|
|
196
|
+
* A sibling repo's leftover synthesis checkpoint must not deny the host.
|
|
197
|
+
*/
|
|
198
|
+
export function cursorGateRoot(heatRoots, eventRoot) {
|
|
199
|
+
const mill = heatRoots.find((root) => packageNameAt(root) === '0xray');
|
|
200
|
+
if (mill) return mill;
|
|
201
|
+
return heatRoots[0] || eventRoot;
|
|
202
|
+
}
|
|
203
|
+
|
|
185
204
|
export function cursorSessionId(event = {}) {
|
|
186
205
|
return (
|
|
187
206
|
event.conversation_id ||
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
appendHookActivity,
|
|
10
10
|
checkCodexPatterns,
|
|
11
11
|
checkFullTestSuite,
|
|
12
|
+
cursorGateRoot,
|
|
12
13
|
cursorHeatRoots,
|
|
13
14
|
cursorSessionId,
|
|
14
15
|
cursorToolContext,
|
|
@@ -53,7 +54,7 @@ async function main() {
|
|
|
53
54
|
for (const root of heatRoots) {
|
|
54
55
|
ensureCursorSessionBoot(root, '0xray/cursor-pre-tool-use-boot', { sessionId });
|
|
55
56
|
}
|
|
56
|
-
const gateRoot = heatRoots
|
|
57
|
+
const gateRoot = cursorGateRoot(heatRoots, eventRoot);
|
|
57
58
|
|
|
58
59
|
const features = loadFeatures(gateRoot);
|
|
59
60
|
const gateFeatures = loadDelegationGateFeatures(gateRoot, 'cursor');
|
|
@@ -395,6 +395,30 @@ function pruneKeywordDest(root) {
|
|
|
395
395
|
return { removed, kept: kept.length };
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
+
const STACK_LAW_FIELDS = [
|
|
399
|
+
"definition",
|
|
400
|
+
"tags",
|
|
401
|
+
"priority",
|
|
402
|
+
"evaluation_criteria",
|
|
403
|
+
"validation_experiment",
|
|
404
|
+
"example_inference_snippet",
|
|
405
|
+
"implementation_notes",
|
|
406
|
+
];
|
|
407
|
+
|
|
408
|
+
/** Stack text cadences onto an existing project law. Observation stats stay. */
|
|
409
|
+
function refreshStackLaw(existing, signal) {
|
|
410
|
+
let dirty = false;
|
|
411
|
+
for (const field of STACK_LAW_FIELDS) {
|
|
412
|
+
if (signal[field] == null) continue;
|
|
413
|
+
const next = signal[field];
|
|
414
|
+
const prev = existing[field];
|
|
415
|
+
if (JSON.stringify(prev) === JSON.stringify(next)) continue;
|
|
416
|
+
existing[field] = next;
|
|
417
|
+
dirty = true;
|
|
418
|
+
}
|
|
419
|
+
return dirty;
|
|
420
|
+
}
|
|
421
|
+
|
|
398
422
|
function mergeMissingSignals(destPath, incoming) {
|
|
399
423
|
if (!incoming.length || !existsSync(destPath)) return 0;
|
|
400
424
|
let data;
|
|
@@ -404,24 +428,35 @@ function mergeMissingSignals(destPath, incoming) {
|
|
|
404
428
|
return 0;
|
|
405
429
|
}
|
|
406
430
|
if (!Array.isArray(data.signals)) return 0;
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
431
|
+
const byName = new Map();
|
|
432
|
+
for (const signal of data.signals) {
|
|
433
|
+
const name = String((signal && signal.name) || "").trim();
|
|
434
|
+
if (name) byName.set(name, signal);
|
|
435
|
+
}
|
|
436
|
+
let changed = 0;
|
|
411
437
|
for (const signal of incoming) {
|
|
412
438
|
const name = String(signal.name).trim();
|
|
413
|
-
if (!name
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
439
|
+
if (!name) continue;
|
|
440
|
+
const existing = byName.get(name);
|
|
441
|
+
if (!existing) {
|
|
442
|
+
const created = {
|
|
443
|
+
...signal,
|
|
444
|
+
name,
|
|
445
|
+
tags: Array.isArray(signal.tags) ? signal.tags : [],
|
|
446
|
+
definition: typeof signal.definition === "string" ? signal.definition : name,
|
|
447
|
+
};
|
|
448
|
+
data.signals.push(created);
|
|
449
|
+
byName.set(name, created);
|
|
450
|
+
changed += 1;
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (refreshStackLaw(existing, signal)) changed += 1;
|
|
454
|
+
}
|
|
455
|
+
if (changed) {
|
|
456
|
+
data.last_updated = new Date().toISOString();
|
|
457
|
+
writeFileSync(destPath, `${JSON.stringify(data, null, 2)}\n`);
|
|
422
458
|
}
|
|
423
|
-
|
|
424
|
-
return added;
|
|
459
|
+
return changed;
|
|
425
460
|
}
|
|
426
461
|
|
|
427
462
|
/** Hydrate project dest from seed + stack. Subject repo-* stay off dest. */
|
|
@@ -661,6 +696,7 @@ function maybeCaptureSessionOnHeadMove(root) {
|
|
|
661
696
|
from: stamp && stamp.head ? stamp.head : commits[commits.length - 1] ? commits[commits.length - 1].hash : git.head,
|
|
662
697
|
to: git.head,
|
|
663
698
|
};
|
|
699
|
+
const patterns = patternsFromGit(root, commits, span);
|
|
664
700
|
const session = {
|
|
665
701
|
sessionId,
|
|
666
702
|
timestamp: new Date().toISOString(),
|
|
@@ -669,8 +705,8 @@ function maybeCaptureSessionOnHeadMove(root) {
|
|
|
669
705
|
approaches,
|
|
670
706
|
wrongTurns: [],
|
|
671
707
|
solutions: [],
|
|
672
|
-
patterns
|
|
673
|
-
matched_primitives:
|
|
708
|
+
patterns,
|
|
709
|
+
matched_primitives: preferLawHits(patterns.map((row) => row && row.name)),
|
|
674
710
|
metrics: { commits: commits.length },
|
|
675
711
|
};
|
|
676
712
|
const outDir = join(root, "docs", "inference");
|
|
@@ -754,8 +790,16 @@ function growDestOnWake(root) {
|
|
|
754
790
|
const line = String(out).trim().split("\n").filter(Boolean).at(-1) || "{}";
|
|
755
791
|
const parsed = JSON.parse(line);
|
|
756
792
|
const pruned = pruneKeywordDest(root);
|
|
757
|
-
|
|
758
|
-
return {
|
|
793
|
+
const destCount = countCuratedSignals(destSignalsPath(root)) || 0;
|
|
794
|
+
if (!parsed || typeof parsed !== "object") return { pruned: pruned.removed, destCount };
|
|
795
|
+
const heated = Array.isArray(parsed.heated) ? parsed.heated.length : 0;
|
|
796
|
+
return {
|
|
797
|
+
...parsed,
|
|
798
|
+
observed: heated,
|
|
799
|
+
pruned: pruned.removed,
|
|
800
|
+
destCount,
|
|
801
|
+
after: destCount,
|
|
802
|
+
};
|
|
759
803
|
} catch {
|
|
760
804
|
return null;
|
|
761
805
|
}
|
|
@@ -811,6 +855,28 @@ function retainCompactFields(existing, extra = {}) {
|
|
|
811
855
|
return kept;
|
|
812
856
|
}
|
|
813
857
|
|
|
858
|
+
/** Persist heated length. A number stays a number. An array is a count. Never drop a number to 0. */
|
|
859
|
+
function workingGrowSnapshot(grow) {
|
|
860
|
+
if (!grow || typeof grow !== "object") return null;
|
|
861
|
+
if (typeof grow.after !== "number") {
|
|
862
|
+
return typeof grow.skipped === "string" ? grow.skipped : null;
|
|
863
|
+
}
|
|
864
|
+
const observed =
|
|
865
|
+
typeof grow.observed === "number"
|
|
866
|
+
? grow.observed
|
|
867
|
+
: Array.isArray(grow.observed)
|
|
868
|
+
? grow.observed.length
|
|
869
|
+
: Array.isArray(grow.heated)
|
|
870
|
+
? grow.heated.length
|
|
871
|
+
: 0;
|
|
872
|
+
return {
|
|
873
|
+
before: grow.before,
|
|
874
|
+
after: grow.after,
|
|
875
|
+
imported: grow.imported,
|
|
876
|
+
observed,
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
814
880
|
function formatWorkingLine(working) {
|
|
815
881
|
if (!working || typeof working !== "object") return null;
|
|
816
882
|
const matched = stationSafeSignals(working.matchedSignals);
|
|
@@ -826,7 +892,7 @@ function buildRepertoireResume(root) {
|
|
|
826
892
|
const modulePath = resolveRepertoireProviderModule(root);
|
|
827
893
|
const mr = readMemoryRoutingConfig(root);
|
|
828
894
|
if (!modulePath) {
|
|
829
|
-
return "Repertoire:
|
|
895
|
+
return "Repertoire: module unresolved";
|
|
830
896
|
}
|
|
831
897
|
const destPath = destSignalsPath(root);
|
|
832
898
|
let signalsPath = existsSync(destPath) ? destPath : mr.config && mr.config.signalsPath;
|
|
@@ -932,15 +998,8 @@ function applyStationHeat(root, host, extra = {}, existing = {}) {
|
|
|
932
998
|
if (pickup) workingSnapshot.pickup = pickup;
|
|
933
999
|
if (matchText) workingSnapshot.matchText = matchText;
|
|
934
1000
|
if (captured) workingSnapshot.sessionCapture = captured;
|
|
935
|
-
|
|
936
|
-
if (
|
|
937
|
-
workingSnapshot.grow = {
|
|
938
|
-
before: grow.before,
|
|
939
|
-
after: grow.after,
|
|
940
|
-
imported: grow.imported,
|
|
941
|
-
observed: Array.isArray(grow.observed) ? grow.observed.length : 0,
|
|
942
|
-
};
|
|
943
|
-
}
|
|
1001
|
+
const growReceipt = workingGrowSnapshot(grow);
|
|
1002
|
+
if (growReceipt) workingSnapshot.grow = growReceipt;
|
|
944
1003
|
if (matchedSignals.length) workingSnapshot.matchedSignals = matchedSignals.slice(0, 8);
|
|
945
1004
|
const opProcNames = readOpProcNames(root);
|
|
946
1005
|
if (opProcNames.length) workingSnapshot.opProcNames = opProcNames;
|
|
@@ -1108,7 +1167,7 @@ function formatStationMarkdown(fields) {
|
|
|
1108
1167
|
} else {
|
|
1109
1168
|
lines.push("Git: n/a");
|
|
1110
1169
|
}
|
|
1111
|
-
lines.push(fields.repertoireResume || "Repertoire:
|
|
1170
|
+
lines.push(fields.repertoireResume || "Repertoire: module unresolved");
|
|
1112
1171
|
if (fields.workingLine) {
|
|
1113
1172
|
lines.push(fields.workingLine);
|
|
1114
1173
|
}
|
|
@@ -1165,6 +1224,7 @@ module.exports = {
|
|
|
1165
1224
|
readNotesPickup,
|
|
1166
1225
|
maybeCaptureSessionOnHeadMove,
|
|
1167
1226
|
formatWorkingLine,
|
|
1227
|
+
workingGrowSnapshot,
|
|
1168
1228
|
applyStationHeat,
|
|
1169
1229
|
isStockTicket,
|
|
1170
1230
|
readStationTicketField,
|
|
@@ -28,6 +28,7 @@ export const patternsFromGit = impl.patternsFromGit;
|
|
|
28
28
|
export const readNotesPickup = impl.readNotesPickup;
|
|
29
29
|
export const maybeCaptureSessionOnHeadMove = impl.maybeCaptureSessionOnHeadMove;
|
|
30
30
|
export const formatWorkingLine = impl.formatWorkingLine;
|
|
31
|
+
export const workingGrowSnapshot = impl.workingGrowSnapshot;
|
|
31
32
|
export const applyStationHeat = impl.applyStationHeat;
|
|
32
33
|
export const isStockTicket = impl.isStockTicket;
|
|
33
34
|
export const readStationTicketField = impl.readStationTicketField;
|
|
@@ -128,13 +128,15 @@ async function growDest(projectRoot) {
|
|
|
128
128
|
sources: [...(kernel.sources || []), ...cleanup.sources],
|
|
129
129
|
});
|
|
130
130
|
const after = service.signalsManager.load().signals.length;
|
|
131
|
+
const heatedNames = Array.isArray(diary.heated) ? diary.heated : [];
|
|
131
132
|
process.stdout.write(
|
|
132
133
|
`${JSON.stringify({
|
|
133
134
|
before,
|
|
134
135
|
after,
|
|
135
136
|
imported: sessions.imported,
|
|
136
|
-
observed:
|
|
137
|
-
heated:
|
|
137
|
+
observed: heatedNames.length,
|
|
138
|
+
heated: heatedNames,
|
|
139
|
+
destCount: after,
|
|
138
140
|
})}\n`,
|
|
139
141
|
);
|
|
140
142
|
return;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { MemoryRoutingConfig, MemoryRoutingProvider } from './types.js';
|
|
2
2
|
export declare function isExplicitMemoryRoutingOptOut(config: MemoryRoutingConfig): boolean;
|
|
3
|
-
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
3
|
+
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
4
|
+
* resolveLeftoverEnabledConfig migrates old tarballs when the organ is on disk.
|
|
5
|
+
* Explicit opt-out is enabled: false + provider: repertoire. */
|
|
4
6
|
export declare function isLeftoverMemoryRoutingOff(config: MemoryRoutingConfig): boolean;
|
|
5
7
|
export declare function resolveLeftoverEnabledConfig(config: MemoryRoutingConfig, cwd: string): MemoryRoutingConfig | null;
|
|
6
8
|
export declare function loadMemoryRoutingProvider(config: MemoryRoutingConfig, cwd?: string): Promise<MemoryRoutingProvider>;
|
|
@@ -59,7 +59,9 @@ function defaultPathForProvider(provider, cwd) {
|
|
|
59
59
|
export function isExplicitMemoryRoutingOptOut(config) {
|
|
60
60
|
return config.enabled === false && config.provider === 'repertoire';
|
|
61
61
|
}
|
|
62
|
-
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
62
|
+
/** Leftover shipped default: enabled false + provider null. Not an explicit opt-out.
|
|
63
|
+
* resolveLeftoverEnabledConfig migrates old tarballs when the organ is on disk.
|
|
64
|
+
* Explicit opt-out is enabled: false + provider: repertoire. */
|
|
63
65
|
export function isLeftoverMemoryRoutingOff(config) {
|
|
64
66
|
if (config.enabled === true)
|
|
65
67
|
return false;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* Import from here or from delegation/index.ts (which re-exports these).
|
|
8
8
|
*/
|
|
9
9
|
import { frameworkLogger } from "../core/framework-logger.js";
|
|
10
|
-
import { getLevelFromScore, getAgentForTier, levelToTier, } from "../delegation/complexity-core.js";
|
|
10
|
+
import { getLevelFromScore, getAgentForTier, getAgentCountForLevel, getStrategyForLevel, levelToTier, } from "../delegation/complexity-core.js";
|
|
11
11
|
import { ComplexityAnalyzer } from "../delegation/complexity-analyzer.js";
|
|
12
12
|
import { getMemoryRoutingProviderSync, initializeMemoryRouting, } from "../memory-routing/index.js";
|
|
13
13
|
initializeMemoryRouting();
|
|
@@ -18,6 +18,9 @@ export const NUCLEUS_THIN_DISPATCH_VERSION = "0.1.0";
|
|
|
18
18
|
*/
|
|
19
19
|
export function scoreComplexity(operation, context, thresholds) {
|
|
20
20
|
const analyzer = new ComplexityAnalyzer();
|
|
21
|
+
if (thresholds) {
|
|
22
|
+
analyzer.setThresholds(thresholds);
|
|
23
|
+
}
|
|
21
24
|
const metrics = analyzer.analyzeComplexity(operation, context);
|
|
22
25
|
const score = analyzer.calculateComplexityScore(metrics);
|
|
23
26
|
frameworkLogger.log("nucleus-thin-dispatch", "score-complexity", "info", {
|
|
@@ -76,7 +79,13 @@ export function scoreAndRoute(operation, context, thresholds) {
|
|
|
76
79
|
signals: resolved.context.matchedSignals,
|
|
77
80
|
});
|
|
78
81
|
return {
|
|
79
|
-
score: {
|
|
82
|
+
score: {
|
|
83
|
+
...score,
|
|
84
|
+
score: resolved.adjustedScore,
|
|
85
|
+
level: adjustedLevel,
|
|
86
|
+
recommendedStrategy: getStrategyForLevel(adjustedLevel),
|
|
87
|
+
estimatedAgents: getAgentCountForLevel(adjustedLevel),
|
|
88
|
+
},
|
|
80
89
|
agent,
|
|
81
90
|
memoryRouting,
|
|
82
91
|
};
|
|
@@ -224,7 +224,7 @@ export class AgentsMdValidationProcessor {
|
|
|
224
224
|
"",
|
|
225
225
|
"## Codex",
|
|
226
226
|
"",
|
|
227
|
-
`0xRay enforces Universal Development Codex (
|
|
227
|
+
`0xRay enforces Universal Development Codex (69 terms) for systematic error prevention.`,
|
|
228
228
|
"",
|
|
229
229
|
].join("\n"),
|
|
230
230
|
"## CLI Commands": [
|
|
@@ -64,7 +64,7 @@ Returns: pass/fail with per-file results and violations.
|
|
|
64
64
|
|
|
65
65
|
### xray_codex_check
|
|
66
66
|
|
|
67
|
-
Validate code against the
|
|
67
|
+
Validate code against the 69-term Universal Development Codex. Checks error-handling, type-safety, performance, security, architecture.
|
|
68
68
|
|
|
69
69
|
```
|
|
70
70
|
xray_codex_check(code="const x: any = foo()", operation="create")
|
|
@@ -64,6 +64,10 @@ Config: `features.json` → `multi_agent_orchestration.lead_dev_mode`
|
|
|
64
64
|
|
|
65
65
|
The seven rules are the engine. The cadence is the time signature. Disk SSOT: `grok-bot/ops/LEAD-CADENCE.md`.
|
|
66
66
|
|
|
67
|
+
Jargon is not the job. Review and cadence ask whether the change works. Do not spend the loop reciting dest names, mill liturgy, or host mantras.
|
|
68
|
+
|
|
69
|
+
- **Feat rebase:** when the mandated work turns into mantra, or the job has split across planes that no longer share one present, stop. Go up a level and answer the original message again from this seat. Do not continue the side thread and call that the job.
|
|
70
|
+
|
|
67
71
|
- **Peer test:** another instance with only worn 0xRay continues the ticket. No command stream.
|
|
68
72
|
- **Wear first:** Read `.xray/state/STATION.md`. Same cloud. Same critic (resume only). Dummy Reads Station.
|
|
69
73
|
- **Dummy tests `SKILLS.md`:** a suited dummy proves the worn catalog. Same-sess bodies keep context and hold a Station card.
|
|
@@ -73,6 +77,24 @@ The seven rules are the engine. The cadence is the time signature. Disk SSOT: `g
|
|
|
73
77
|
- **Live tick, not pacer:** `/loop` runs work now. Keep workstreams moving. Do not idle-wait 12 minutes. **Clean ticks every cycle** — unsubscribe then resubscribe so the prompt matches metal. Name-dedupe does not rewrite. A stale prompt (waiting URL after live, Hold after strip) is dirty: rewrite, do not act. **If the card and WAVEBOARD are idle, unsubscribe and do not resubscribe.**
|
|
74
78
|
- **Board clock:** PR events + resume the same critic. Not two idle pacers. Merge after PASS+CI (gate A). Publish only after mill-gate D + `foundry gate`.
|
|
75
79
|
- **Dispatch:** ticket + PR URLs + “Wear, then review.” The critic Reads `SEATS.md` Reviewer. Do not paste a command novel. When a law is spoken twice, encode it this wake.
|
|
80
|
+
- **Cascade:** Chat is not the brain. A cascade that must survive compaction is a named repertoire signal on the stack overlay, hydrated onto the project law list. NOTES holds the same cascade for the second Read. Station stays the ticket. A reflection is not this capture. Capture priority: a law is an invariant you would otherwise re-read the source to relearn. One law per subsystem bone. Heat touches last_seen and does not append a confidence sample, so more hits do not raise conviction. Heat touches last_seen only when the diary contains that signal's name; two definition words are not a hit; a weak session score is not lifted to 0.55 and a sample below 0.55 is not recorded. Constructing the organ does not append workspace samples. Hero names: `heat-is-not-conviction`, `inference-cycle-invariant`, `governance-vote-invariant`, `thin-dispatch-invariant`. Repertoire mergeStackOverlay refreshes a changed stack definition and keeps observation stats.
|
|
81
|
+
- **Cold brief:** a new critic or any new subagent that does not already have this thread gets the latest commit SHA on the branch under review (fetch it; do not reuse an earlier SHA from a previous pass) and the Station card (`.xray/state/STATION.md`: intent, plan, git). If the card is missing, give branch + HEAD + what just changed. If the branch moves after you brief them, send the new SHA before they verdict.
|
|
82
|
+
- **Control:** The lead keeps control. Do not assume the subagent has the context. Give the duty, the latest SHA, the Station card, and the laws that apply. The subagent wears the applicable 0xRay suit before it works: the tree under review, built and installed. An older published package is not that suit. The lead checks the result.
|
|
83
|
+
|
|
84
|
+
### Engine cadence
|
|
85
|
+
|
|
86
|
+
This suite is the engine that builds the engine. When a seat sees OP-PROC, write it onto these existing surfaces in the same wake. Do not open a new skill.
|
|
87
|
+
|
|
88
|
+
1. Change the engine with precision.
|
|
89
|
+
2. Ship it local: build and install the tree. A live npm version stays live. Once a version is live, the stamper strips every present-tense patch pin from shipped guides and shipped OP-PROC, or reconcile advances the next cut. The stamper does not edit Station, NOTES, dest, or node_modules. It does not publish.
|
|
90
|
+
3. Test the change.
|
|
91
|
+
4. Run another deep review of the diff you just made.
|
|
92
|
+
5. Monitor long-term memory (dest name count and whether observations are the only growth) and short-term memory (Station: intent, plan, git).
|
|
93
|
+
6. Ship the PR.
|
|
94
|
+
|
|
95
|
+
### Planes
|
|
96
|
+
|
|
97
|
+
Operating planes: code, OP-PROC, model, suit, mill, host, and test/ship. Test/ship is the critical OP-PROC plane. Test then ship is the hero. Ship without that proof is the catastrophe. Long-term memory is the project law list. Each wake refreshes a changed stack definition and keeps observation stats. Heat writes the matched names onto Station, the short-term card that survives compaction. Reactive decisions move out of TypeScript into OP-PROC as the model improves. That move is why the mill and the suit exist. It is not fully proved. Once a version is live, the stamper strips every present-tense patch pin from shipped guides and shipped OP-PROC, or reconcile advances the next cut. The stamper does not edit Station, NOTES, dest, or node_modules. It does not publish. Do not freeze the live cut.
|
|
76
98
|
- **Close:** loop until Station is done. Compact: Station survives the cut. Overlay dest names are OP-PROC — reload `repertoire-working.json` `opProcNames`, not Station.
|
|
77
99
|
- **Board:** critic Strict comments; forge implements; lead stays main thread.
|
|
78
100
|
- **Codex 69:** rewire this skill and existing ops. Do not add a new `SKILL.md` to hold the cadence.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "0xray",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.22",
|
|
4
4
|
"description": "Multi-agent orchestration and Codex governance for OpenCode, Hermes, Grok Build, and OpenClaw",
|
|
5
5
|
"readme": "README.md",
|
|
6
6
|
"license": "MIT",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"debug:inference:force": "XRAY_FORCE_MCP_GOVERNANCE=true XRAY_DEV_PATH=dist node dist/cli/index.js inference:run --force",
|
|
107
107
|
"validate:full": "npm run validate",
|
|
108
108
|
"test:unified": "node scripts/test/test-unified-framework.mjs",
|
|
109
|
-
"test:consumer": "node scripts/
|
|
109
|
+
"test:consumer": "node scripts/mjs/test-consumer-readiness.mjs",
|
|
110
110
|
"config:setup": "node scripts/config/utils.js setup-dev",
|
|
111
111
|
"monitoring:start": "node scripts/monitoring/daemon.js start",
|
|
112
112
|
"monitoring:stop": "node scripts/monitoring/daemon.js stop",
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
Shim `sound-voice.cjs` → `@0xray/blip/sound-voice`. Depend on published `@0xray/blip@^0.1.2`. Foundry stamp stays 0.1.12. Do not vendor mill drawers.
|
|
6
6
|
|
|
7
|
+
Inspect `--go` emits a shop-style forge GO receipt (harness profile: bare / suited / partial). Observational by default: Cursor/Grok `hooks.json` bound, Repertoire fastened (`node_modules` or `.xray/state/repertoire`), Station present. `--require-harness=` gates dual-cloud A/B. Vendor Repertoire source and `memory_routing` declaration are not fasten. Not an 8th MCP. Not PPE. Not 5.0.
|
|
8
|
+
|
|
7
9
|
## [0.1.12] - 2026-09-18
|
|
8
10
|
|
|
9
11
|
Fasten is foundry-plant/0: mill packages declare plants in package.json (`mill.protocol`, prefix selection when skills/agents are omitted) and foundry copies organs from that package. Builtin catalog is mill+inspect only; foundry does not catalog mill organs. `foundry.json` `"plant": "blip"|"sound"` are package aliases for `@0xray/blip`, not skill lists. Unknown mill packages fail closed (`npm i`). Inventory records `millPackage` and `millProtocol` when a mill package was used.
|
|
@@ -23,7 +23,7 @@ npx @0xray/foundry release [patch|minor|major] --i-mean-it
|
|
|
23
23
|
npx @0xray/foundry release --publish-only --dry-run
|
|
24
24
|
npx @0xray/foundry release --publish-only --i-mean-it
|
|
25
25
|
npx @0xray/foundry mint
|
|
26
|
-
npx @0xray/foundry inspect [--skip-live]
|
|
26
|
+
npx @0xray/foundry inspect [--skip-live] [--go] [--go-out PATH] [--require-harness=bare|suited|partial]
|
|
27
27
|
npx @0xray/foundry sound render [--brief TEXT] [--genre ambient|techno|jazz]
|
|
28
28
|
npx @0xray/foundry sound inspect
|
|
29
29
|
npx @0xray/foundry sound mix
|
|
@@ -39,7 +39,7 @@ npx @0xray/foundry hooks
|
|
|
39
39
|
|
|
40
40
|
`FOUNDRY_ROOT` overrides cwd (the repo being milled).
|
|
41
41
|
|
|
42
|
-
`gate` is build + test + docs-check. `docs-build` runs Docusaurus on the 0xray exo (`docs-site/`); stranger mills skip. `inspect` runs the six mill checks (diff, plant vs worn, receipt, CI, live tarball GET, isolated HOME). Mint fails on a costume dump. Isolated HOME skips machine `~/.grok`. Shared HOME last-mile dest is project `.grok/plugins/0xray` (no last-wins machine plugin). `ci` reports GitHub Actions (no auto-push). `hooks` installs git pre/post hooks. GitHub `0xRay CI/CD` is the mill gate on `main`. `Deploy Docs` is the mill Pages put.
|
|
42
|
+
`gate` is build + test + docs-check. `docs-build` runs Docusaurus on the 0xray exo (`docs-site/`); stranger mills skip. `inspect` runs the six mill checks (diff, plant vs worn, receipt, CI, live tarball GET, isolated HOME) plus observational harness (Cursor/Grok hooks bound, Repertoire fastened, Station present). `--go` emits a shop-style forge GO receipt; `--require-harness=bare|suited|partial` turns that observation into a gate. Mint fails on a costume dump. Isolated HOME skips machine `~/.grok`. Shared HOME last-mile dest is project `.grok/plugins/0xray` (no last-wins machine plugin). `ci` reports GitHub Actions (no auto-push). `hooks` installs git pre/post hooks. GitHub `0xRay CI/CD` is the mill gate on `main`. `Deploy Docs` is the mill Pages put.
|
|
43
43
|
|
|
44
44
|
`docs-check` and `gate` run **full** 0xRay corpus only when the milled `package.json` name is `0xray` and `docs-site/` exists. Otherwise **light**: `package.json` + CHANGELOG.
|
|
45
45
|
|
package/scripts/foundry/cli.mjs
CHANGED
|
@@ -29,7 +29,7 @@ const HELP =
|
|
|
29
29
|
"Mill, not exo. FOUNDRY_ROOT overrides cwd (the repo being milled).\n" +
|
|
30
30
|
"release bumps/commits/pushes/publishes the milled cwd — pass --dry-run or --i-mean-it (or FOUNDRY_RELEASE=1).\n" +
|
|
31
31
|
"Mint fastens the requested factory plant (default mill; foundry.json plant: sound|blip turns mill off), then inspect.\n" +
|
|
32
|
-
"inspect runs mill checks plus, on a sound seat, last bed receipt PASS/FAIL; on a blip seat, last blip receipt.\n" +
|
|
32
|
+
"inspect runs mill checks plus, on a sound seat, last bed receipt PASS/FAIL; on a blip seat, last blip receipt. Observational harness (hooks / Repertoire / Station). --go emits a forge GO receipt. Not an 8th MCP.\n" +
|
|
33
33
|
"sound render|inspect|mix is the factory-sound plant (brief → seed → genre → wav → metrics; mix levels every variation). Sibling to mill, not a mill copy.\n" +
|
|
34
34
|
"blip render|inspect|vibe|look is the factory-blip plant (brief → seed → still|motion:<id> → 4.44s mp4; vibe is stamp density, look is the friend hitting replay, ship bar 8). Sibling to mill + sound.\n" +
|
|
35
35
|
"gate is build+test. docs-build is Docusaurus on the 0xray exo. ci reports GitHub Actions (no auto-push). hooks installs git pre/post hooks.\n";
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Mill inspect organ. Runs the six checks
|
|
3
|
+
* Mill inspect organ. Runs the six plant checks plus observational harness.
|
|
4
|
+
* Not an 8th MCP. Not PPE. Does not fasten hooks or Repertoire.
|
|
4
5
|
*
|
|
5
|
-
* npx @0xray/foundry inspect [--skip-live]
|
|
6
|
+
* npx @0xray/foundry inspect [--skip-live] [--go] [--go-out PATH]
|
|
7
|
+
* [--require-harness=bare|suited|partial] [--goal TEXT]
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
import { spawnSync } from "node:child_process";
|
|
@@ -63,6 +65,140 @@ function readJson(file) {
|
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
const HARNESS_PROFILES = new Set(["bare", "suited", "partial"]);
|
|
69
|
+
|
|
70
|
+
export function parseInspectArgs(argv = process.argv.slice(2)) {
|
|
71
|
+
const out = {
|
|
72
|
+
skipLive: false,
|
|
73
|
+
go: false,
|
|
74
|
+
goOut: null,
|
|
75
|
+
requireHarness: null,
|
|
76
|
+
goal: null,
|
|
77
|
+
};
|
|
78
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
79
|
+
const arg = argv[i];
|
|
80
|
+
if (arg === "--skip-live") out.skipLive = true;
|
|
81
|
+
else if (arg === "--go") out.go = true;
|
|
82
|
+
else if (arg === "--go-out") out.goOut = argv[++i] || null;
|
|
83
|
+
else if (arg.startsWith("--go-out=")) out.goOut = arg.slice("--go-out=".length);
|
|
84
|
+
else if (arg === "--require-harness") out.requireHarness = argv[++i] || null;
|
|
85
|
+
else if (arg.startsWith("--require-harness=")) {
|
|
86
|
+
out.requireHarness = arg.slice("--require-harness=".length);
|
|
87
|
+
} else if (arg === "--goal") out.goal = argv[++i] || null;
|
|
88
|
+
else if (arg.startsWith("--goal=")) out.goal = arg.slice("--goal=".length);
|
|
89
|
+
}
|
|
90
|
+
if (out.goOut) out.go = true;
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function hooksFileBound(file) {
|
|
95
|
+
if (!fs.existsSync(file)) return false;
|
|
96
|
+
const json = readJson(file);
|
|
97
|
+
const hooks = json?.hooks;
|
|
98
|
+
if (!hooks || typeof hooks !== "object") return false;
|
|
99
|
+
return Object.values(hooks).some(
|
|
100
|
+
(list) =>
|
|
101
|
+
Array.isArray(list) &&
|
|
102
|
+
list.some((entry) => typeof entry?.command === "string" && entry.command.trim()),
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function classifyHarnessProfile({ hooksBound, repertoireFastened, stationPresent }) {
|
|
107
|
+
if (!hooksBound && !repertoireFastened && !stationPresent) return "bare";
|
|
108
|
+
if (hooksBound && repertoireFastened) return "suited";
|
|
109
|
+
return "partial";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function probeHarness(root) {
|
|
113
|
+
const cursorHooks = path.join(root, ".cursor", "hooks.json");
|
|
114
|
+
const grokHooks = path.join(root, ".grok", "plugins", "0xray", "hooks", "hooks.json");
|
|
115
|
+
const grokPluginHooks = path.join(root, ".grok-plugin", "hooks", "hooks.json");
|
|
116
|
+
const hooks = {
|
|
117
|
+
cursor: hooksFileBound(cursorHooks),
|
|
118
|
+
grok: hooksFileBound(grokHooks) || hooksFileBound(grokPluginHooks),
|
|
119
|
+
};
|
|
120
|
+
hooks.bound = Boolean(hooks.cursor || hooks.grok);
|
|
121
|
+
|
|
122
|
+
const repertoire = {
|
|
123
|
+
nodeModules: fs.existsSync(path.join(root, "node_modules", "@0xray", "repertoire")),
|
|
124
|
+
state: fs.existsSync(path.join(root, ".xray", "state", "repertoire")),
|
|
125
|
+
vendorSource: fs.existsSync(path.join(root, "vendor", "@0xray", "repertoire")),
|
|
126
|
+
};
|
|
127
|
+
repertoire.fastened = Boolean(repertoire.nodeModules || repertoire.state);
|
|
128
|
+
|
|
129
|
+
const features =
|
|
130
|
+
readJson(path.join(root, ".xray", "features.json")) ||
|
|
131
|
+
readJson(path.join(root, "xray", "features.json"));
|
|
132
|
+
repertoire.memoryRoutingDeclared = features?.memory_routing?.provider === "repertoire";
|
|
133
|
+
|
|
134
|
+
const stationPresent = fs.existsSync(path.join(root, ".xray", "state", "STATION.md"));
|
|
135
|
+
const profile = classifyHarnessProfile({
|
|
136
|
+
hooksBound: hooks.bound,
|
|
137
|
+
repertoireFastened: repertoire.fastened,
|
|
138
|
+
stationPresent,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
id: "harness",
|
|
143
|
+
hooks,
|
|
144
|
+
repertoire,
|
|
145
|
+
station: { present: stationPresent },
|
|
146
|
+
profile,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function checkHarness(root, requireHarness = null) {
|
|
151
|
+
const probe = probeHarness(root);
|
|
152
|
+
if (requireHarness && !HARNESS_PROFILES.has(requireHarness)) {
|
|
153
|
+
return {
|
|
154
|
+
...probe,
|
|
155
|
+
ok: false,
|
|
156
|
+
require: requireHarness,
|
|
157
|
+
detail: `unknown --require-harness=${requireHarness} (bare|suited|partial)`,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (requireHarness && probe.profile !== requireHarness) {
|
|
161
|
+
return {
|
|
162
|
+
...probe,
|
|
163
|
+
ok: false,
|
|
164
|
+
require: requireHarness,
|
|
165
|
+
detail: `harness profile ${probe.profile} != required ${requireHarness}`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
...probe,
|
|
170
|
+
ok: true,
|
|
171
|
+
require: requireHarness,
|
|
172
|
+
skipped: !requireHarness,
|
|
173
|
+
detail: requireHarness ? null : "observational — pass --require-harness to gate",
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function gitRemote(root) {
|
|
178
|
+
const remote = spawnSync("git", ["config", "--get", "remote.origin.url"], {
|
|
179
|
+
cwd: root,
|
|
180
|
+
encoding: "utf8",
|
|
181
|
+
});
|
|
182
|
+
if (remote.error || remote.status !== 0) return null;
|
|
183
|
+
return (remote.stdout || "").trim() || null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function buildForgeGo(root, harness, opts = {}) {
|
|
187
|
+
return {
|
|
188
|
+
kind: "forge-go",
|
|
189
|
+
mill: "inspect",
|
|
190
|
+
profile: harness.profile,
|
|
191
|
+
hooksBound: Boolean(harness.hooks?.bound),
|
|
192
|
+
repertoireFastened: Boolean(harness.repertoire?.fastened),
|
|
193
|
+
stationPresent: Boolean(harness.station?.present),
|
|
194
|
+
require: harness.require || null,
|
|
195
|
+
ok: harness.ok !== false,
|
|
196
|
+
goal: opts.goal || process.env.FOUNDRY_GO_GOAL || null,
|
|
197
|
+
repo: gitRemote(root),
|
|
198
|
+
generatedAt: new Date().toISOString(),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
66
202
|
function checkDiff(root) {
|
|
67
203
|
const status = spawnSync("git", ["status", "-sb"], { cwd: root, encoding: "utf8" });
|
|
68
204
|
if (status.error || status.status === 128) {
|
|
@@ -412,6 +548,9 @@ export async function inspectSuit(root, opts = {}) {
|
|
|
412
548
|
const skipLive = Boolean(opts.skipLive);
|
|
413
549
|
const env = opts.env || process.env;
|
|
414
550
|
const machineHome = opts.machineHome || mint.machineHome();
|
|
551
|
+
const requireHarness = opts.requireHarness || null;
|
|
552
|
+
const go = Boolean(opts.go);
|
|
553
|
+
const goal = opts.goal || null;
|
|
415
554
|
|
|
416
555
|
const checks = [];
|
|
417
556
|
checks.push(checkDiff(root));
|
|
@@ -445,22 +584,38 @@ export async function inspectSuit(root, opts = {}) {
|
|
|
445
584
|
}
|
|
446
585
|
|
|
447
586
|
checks.push(checkIsolatedHome(root, env, machineHome));
|
|
587
|
+
checks.push(checkHarness(root, requireHarness));
|
|
448
588
|
|
|
449
589
|
const failed = checks.filter((c) => c.ok === false);
|
|
450
590
|
const receipt = checks.find((c) => c.id === "receipt") || {};
|
|
451
|
-
|
|
591
|
+
const harness = checks.find((c) => c.id === "harness") || {};
|
|
592
|
+
const report = {
|
|
452
593
|
ok: failed.length === 0,
|
|
453
594
|
failed: failed.map((c) => c.id),
|
|
454
595
|
checks,
|
|
455
596
|
dna: receipt.dna || null,
|
|
456
597
|
pack: "0xray-suit",
|
|
457
598
|
};
|
|
599
|
+
if (go) {
|
|
600
|
+
report.go = buildForgeGo(root, harness, { goal });
|
|
601
|
+
}
|
|
602
|
+
return report;
|
|
458
603
|
}
|
|
459
604
|
|
|
460
605
|
async function main() {
|
|
461
|
-
const
|
|
606
|
+
const args = parseInspectArgs(process.argv.slice(2));
|
|
462
607
|
const root = resolveMillRoot();
|
|
463
|
-
const report = await inspectSuit(root, {
|
|
608
|
+
const report = await inspectSuit(root, {
|
|
609
|
+
skipLive: args.skipLive,
|
|
610
|
+
requireHarness: args.requireHarness,
|
|
611
|
+
go: args.go,
|
|
612
|
+
goal: args.goal,
|
|
613
|
+
});
|
|
614
|
+
if (args.goOut) {
|
|
615
|
+
const dest = path.isAbsolute(args.goOut) ? args.goOut : path.join(root, args.goOut);
|
|
616
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
617
|
+
fs.writeFileSync(dest, `${JSON.stringify(report.go || report, null, 2)}\n`);
|
|
618
|
+
}
|
|
464
619
|
process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
|
|
465
620
|
process.exit(report.ok ? 0 : 1);
|
|
466
621
|
}
|