0xray 4.0.34 → 4.0.36
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/.xray/codex.json +12 -4
- package/.xray/features.json +1 -1
- package/AGENTS-consumer.md +6 -6
- package/AGENTS.md +5 -5
- package/README.md +2 -2
- package/dist/AGENTS.md +5 -5
- package/dist/CHANGELOG.md +26 -0
- package/dist/README.md +2 -2
- package/dist/cli/index.js +2 -5
- package/dist/governance/governance-service.js +1 -1
- package/dist/integrations/base/README.md +1 -9
- package/dist/integrations/base/index.d.ts +0 -1
- package/dist/integrations/base/index.js +0 -2
- package/dist/integrations/governance/index.js +31 -8
- package/dist/integrations/grok/hooks/grok-hook-utils.js +1 -1
- package/dist/integrations/grok/hooks/session-start.js +12 -0
- package/dist/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/dist/integrations/hooks/goggles-mcp.mjs +10 -3
- package/dist/integrations/hooks/goggles-pipeline.mjs +110 -23
- package/dist/integrations/hooks/host-truth.mjs +74 -0
- package/dist/integrations/hooks/station-hook-runtime.cjs +3 -0
- package/dist/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/dist/mcps/knowledge-skills/storyteller.server.js +0 -1
- package/dist/nucleus/delegation-gate.d.ts +2 -2
- package/dist/nucleus/delegation-gate.js +13 -2
- package/dist/nucleus/work-freshness.mjs +189 -0
- package/dist/processors/implementations/agents-md-validation-processor.js +1 -1
- package/docs-site/docs/plates/boot.md +17 -2
- package/docs-site/docs/plates/governance.md +3 -2
- package/docs-site/docs/plates/index.md +3 -1
- package/docs-site/docs/plates/memory-recall.md +26 -35
- package/docs-site/docs/plates/orchestration.md +39 -35
- package/docs-site/docs/plates/processor.md +34 -36
- package/docs-site/docs/plates/reporting.md +3 -8
- package/docs-site/docs/plates/routing.md +22 -11
- package/package.json +3 -2
- package/scripts/foundry/release.mjs +48 -5
- package/scripts/mjs/run-grok-hook.mjs +50 -0
- package/scripts/mjs/test-consumer-readiness.mjs +1 -0
- package/scripts/mjs/test-mcp-functionality.mjs +14 -9
- package/scripts/node/basic-security-audit.cjs +0 -1
- package/scripts/node/bridge-mcp-wiring.cjs +66 -3
- package/scripts/node/install-bridges.cjs +3 -3
- package/scripts/node/postinstall.cjs +1 -1
- package/scripts/node/prepare-consumer.cjs +11 -7
- package/scripts/node/setup-dev.cjs +18 -26
- package/scripts/node/validate-mcp-connectivity.js +2 -1
- package/src/integrations/grok/plugin/0xray/hooks/hooks.json +6 -6
- package/src/integrations/openclaw/plugin/xray-pre-tool/package.json +1 -1
- package/src/mcps/knowledge-skills/storyteller.server.ts +0 -1
- package/xray/agents_template.md +5 -5
- package/xray/codex.json +12 -4
- package/xray/features.json +1 -1
- package/dist/agents/known-names.d.ts +0 -26
- package/dist/agents/known-names.js +0 -72
- package/dist/analytics/consent-manager.d.ts +0 -92
- package/dist/analytics/consent-manager.js +0 -257
- package/dist/benchmark/performance-benchmark.d.ts +0 -103
- package/dist/benchmark/performance-benchmark.js +0 -373
- package/dist/cli/commands/analytics-disable.d.ts +0 -9
- package/dist/cli/commands/analytics-disable.js +0 -72
- package/dist/cli/commands/analytics-enable-action.d.ts +0 -8
- package/dist/cli/commands/analytics-enable-action.js +0 -82
- package/dist/cli/commands/analytics-preview.d.ts +0 -9
- package/dist/cli/commands/analytics-preview.js +0 -106
- package/dist/cli/commands/analytics-status.d.ts +0 -9
- package/dist/cli/commands/analytics-status.js +0 -68
- package/dist/cli/commands/enforce.d.ts +0 -12
- package/dist/cli/commands/enforce.js +0 -78
- package/dist/cli/commands/govern.d.ts +0 -29
- package/dist/cli/commands/govern.js +0 -149
- package/dist/cli/commands/security-audit.d.ts +0 -23
- package/dist/cli/commands/security-audit.js +0 -219
- package/dist/cli/server.d.ts +0 -1
- package/dist/cli/server.js +0 -147
- package/dist/config/routing-mappings.d.ts +0 -26
- package/dist/config/routing-mappings.js +0 -151
- package/dist/core/bridge.mjs +0 -922
- package/dist/core/trace-context.d.ts +0 -12
- package/dist/core/trace-context.js +0 -33
- package/dist/enforcement/index.d.ts +0 -22
- package/dist/enforcement/index.js +0 -40
- package/dist/enforcement/loaders/index.d.ts +0 -36
- package/dist/enforcement/loaders/index.js +0 -39
- package/dist/enforcement/test-auto-healing.d.ts +0 -83
- package/dist/enforcement/test-auto-healing.js +0 -325
- package/dist/inference/index.d.ts +0 -10
- package/dist/inference/index.js +0 -5
- package/dist/infrastructure/iac-validator.d.ts +0 -150
- package/dist/infrastructure/iac-validator.js +0 -465
- package/dist/infrastructure/schemas/cloud-schemas.d.ts +0 -482
- package/dist/infrastructure/schemas/cloud-schemas.js +0 -488
- package/dist/integrations/base/ExampleIntegration.d.ts +0 -46
- package/dist/integrations/base/ExampleIntegration.js +0 -180
- package/dist/integrations/enforcement-gate.d.ts +0 -44
- package/dist/integrations/enforcement-gate.js +0 -348
- package/dist/mcps/connection/connection-manager.d.ts +0 -39
- package/dist/mcps/connection/connection-manager.js +0 -79
- package/dist/mcps/index.d.ts +0 -17
- package/dist/mcps/index.js +0 -65
- package/dist/mcps/orchestrator/index.d.ts +0 -6
- package/dist/mcps/orchestrator/index.js +0 -5
- package/dist/metrics/index.d.ts +0 -6
- package/dist/metrics/index.js +0 -6
- package/dist/orchestrator/self-direction-activation.d.ts +0 -108
- package/dist/orchestrator/self-direction-activation.js +0 -244
- package/dist/orchestrator/universal-registry-bridge.d.ts +0 -84
- package/dist/orchestrator/universal-registry-bridge.js +0 -246
- package/dist/performance/performance-budget-enforcer.d.ts +0 -157
- package/dist/performance/performance-budget-enforcer.js +0 -433
- package/dist/performance/performance-regression-tester.d.ts +0 -107
- package/dist/performance/performance-regression-tester.js +0 -341
- package/dist/postprocessor/analysis/CodeChangeAnalyzer.d.ts +0 -11
- package/dist/postprocessor/analysis/CodeChangeAnalyzer.js +0 -50
- package/dist/postprocessor/config/ProcessorConfigLoader.d.ts +0 -44
- package/dist/postprocessor/config/ProcessorConfigLoader.js +0 -16
- package/dist/postprocessor/integration.d.ts +0 -156
- package/dist/postprocessor/integration.js +0 -194
- package/dist/postprocessor/metamorphosis/index.d.ts +0 -2
- package/dist/postprocessor/metamorphosis/index.js +0 -1
- package/dist/postprocessor/redeploy/RetryHandler.d.ts +0 -49
- package/dist/postprocessor/redeploy/RetryHandler.js +0 -118
- package/dist/processors/implementations/session-capture-processor.d.ts +0 -14
- package/dist/processors/implementations/session-capture-processor.js +0 -37
- package/dist/public/XrayService.d.ts +0 -17
- package/dist/public/XrayService.js +0 -40
- package/dist/public/index.d.ts +0 -4
- package/dist/public/index.js +0 -2
- package/dist/security/comprehensive-security-audit.d.ts +0 -157
- package/dist/security/comprehensive-security-audit.js +0 -1004
- package/dist/security/index.d.ts +0 -13
- package/dist/security/index.js +0 -13
- package/dist/security/security-agent-coordinator.d.ts +0 -71
- package/dist/security/security-agent-coordinator.js +0 -203
- package/dist/security/security-hardening-system.d.ts +0 -238
- package/dist/security/security-hardening-system.js +0 -726
- package/dist/security/security-orchestration-layer.d.ts +0 -118
- package/dist/security/security-orchestration-layer.js +0 -495
- package/dist/testing/memory-regression-suite.d.ts +0 -105
- package/dist/testing/memory-regression-suite.js +0 -258
- package/dist/tools/assess-complexity-tool.d.ts +0 -28
- package/dist/tools/assess-complexity-tool.js +0 -44
- package/dist/tools/query-routing-tool.d.ts +0 -17
- package/dist/tools/query-routing-tool.js +0 -59
- package/dist/utils/batch-operations.d.ts +0 -25
- package/dist/utils/batch-operations.js +0 -292
- package/dist/utils/task-graph.d.ts +0 -29
- package/dist/utils/task-graph.js +0 -106
- package/dist/utils/test-template-generator.d.ts +0 -25
- package/dist/utils/test-template-generator.js +0 -178
- package/dist/utils/token-manager.d.ts +0 -48
- package/dist/utils/token-manager.js +0 -163
- package/dist/validation/agent-config-validator.d.ts +0 -110
- package/dist/validation/agent-config-validator.js +0 -280
- package/dist/validation/orchestration-flow-validator.d.ts +0 -87
- package/dist/validation/orchestration-flow-validator.js +0 -606
- package/src/mcps/connection/connection-manager.ts +0 -91
- package/src/mcps/index.ts +0 -80
- package/src/mcps/orchestrator/index.ts +0 -29
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* A named plane returns its view. Field pipes fill what they can see.
|
|
4
4
|
* The view keeps every field for the next look.
|
|
5
5
|
* A held plane stays quiet. A leave is denied.
|
|
6
|
+
* On this host a search is grep, glob, or a shell that runs one.
|
|
7
|
+
* The lens file stays open. One later read may go on, then the next read stops.
|
|
6
8
|
*/
|
|
7
9
|
import { existsSync, mkdirSync, readdirSync, readFileSync, realpathSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
8
10
|
import { createRequire } from 'node:module';
|
|
@@ -72,9 +74,12 @@ export function organStop(held, action) {
|
|
|
72
74
|
const text = String(action?.text || '');
|
|
73
75
|
const paths = (action?.paths || []).map(String).filter(Boolean);
|
|
74
76
|
const tool = String(action?.tool || '');
|
|
75
|
-
|
|
77
|
+
const spoken = [text, ...paths].join('\n');
|
|
78
|
+
if (/docs-site\/docs\/plates\/[a-z0-9-]+\.md/i.test(spoken)) {
|
|
76
79
|
return organDeny(`The reading is ${plane}. That drawing is not the plane.`);
|
|
77
80
|
}
|
|
81
|
+
const otherFile = otherPlaneFile(plane, spoken);
|
|
82
|
+
if (otherFile) return organDeny(`The reading is ${plane}. The action is ${otherFile}.`);
|
|
78
83
|
const stripped = withoutPaths(text);
|
|
79
84
|
const others = acceptedPlanes().filter((name) => name !== plane && planeWord(name, stripped));
|
|
80
85
|
const namesThis = planeWord(plane, stripped);
|
|
@@ -255,10 +260,10 @@ function cardView(plane, scope, flavor, root) {
|
|
|
255
260
|
teardown: plane.teardown || '',
|
|
256
261
|
worn: plane.worn || '',
|
|
257
262
|
};
|
|
263
|
+
view.exam = examinePlane(plane, root).text;
|
|
258
264
|
if (flavor === 'triage') {
|
|
259
265
|
const filled = new Set(filledOf(plane));
|
|
260
266
|
view.empty = FIELD_ORDER.filter((name) => !filled.has(name));
|
|
261
|
-
view.exam = examinePlane(plane, root).text;
|
|
262
267
|
}
|
|
263
268
|
return view;
|
|
264
269
|
}
|
|
@@ -283,6 +288,8 @@ function formatCardText(view) {
|
|
|
283
288
|
if (view.empty.length) lines.push(`Empty: ${view.empty.join(', ')}`);
|
|
284
289
|
lines.push(view.exam);
|
|
285
290
|
text = `${text}\n${lines.join('\n')}`;
|
|
291
|
+
} else if (typeof view.exam === 'string' && view.exam.startsWith('Drift:')) {
|
|
292
|
+
text = `${text}\n${view.exam}`;
|
|
286
293
|
}
|
|
287
294
|
return text;
|
|
288
295
|
}
|
|
@@ -304,6 +311,8 @@ export function formatCardPane(view) {
|
|
|
304
311
|
if (view.flavor === 'triage') {
|
|
305
312
|
if (Array.isArray(view.empty) && view.empty.length) rows.push(['Empty', view.empty.join(', ')]);
|
|
306
313
|
if (view.exam) rows.push(['', view.exam]);
|
|
314
|
+
} else if (typeof view.exam === 'string' && view.exam.startsWith('Drift:')) {
|
|
315
|
+
rows.push(['', view.exam]);
|
|
307
316
|
}
|
|
308
317
|
const body = rows.map(([label, value]) => (label ? `${label.padEnd(8)} ${value}` : value).trimEnd());
|
|
309
318
|
const title = `${view.flavor} · ${view.plane}`;
|
|
@@ -684,12 +693,86 @@ export function suitHint(text) {
|
|
|
684
693
|
}
|
|
685
694
|
|
|
686
695
|
const DEEP_SEARCH = /search_codebase|find_implementation|get_documentation/i;
|
|
696
|
+
const HOLDS_ON_NAME = new Set(['dichotomy', 'syncopate', 'synthesis']);
|
|
697
|
+
const SHELL_SEARCH = /(?:^|&&|\|\||[;&|`(\n])\s*(?:(?:\/[\w.+/-]+\/)|(?:npx\s+))?(?:rg|grep|ag|ack|fd|find)\b|\bgit\s+grep\b/;
|
|
698
|
+
|
|
699
|
+
function shellSearches(text) {
|
|
700
|
+
return SHELL_SEARCH.test(String(text || ''));
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function mappedPlaneFiles(name) {
|
|
704
|
+
try {
|
|
705
|
+
const data = JSON.parse(readFileSync(planesFile(), 'utf8'));
|
|
706
|
+
const entry = data[name];
|
|
707
|
+
if (!entry || typeof entry !== 'object') return [];
|
|
708
|
+
const candidates = [entry.file, entry.worn, entry.skills];
|
|
709
|
+
if (Array.isArray(entry.files)) candidates.push(...entry.files);
|
|
710
|
+
return candidates.filter((file) => typeof file === 'string' && file.includes('.') && file.includes('/'));
|
|
711
|
+
} catch {
|
|
712
|
+
return [];
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function otherPlaneFile(held, spoken) {
|
|
717
|
+
const text = String(spoken || '');
|
|
718
|
+
for (const name of acceptedPlanes()) {
|
|
719
|
+
if (name === held) continue;
|
|
720
|
+
if (mappedPlaneFiles(name).some((file) => text.includes(file))) return name;
|
|
721
|
+
}
|
|
722
|
+
return '';
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function mapFiles() {
|
|
726
|
+
try {
|
|
727
|
+
const data = JSON.parse(readFileSync(planesFile(), 'utf8'));
|
|
728
|
+
const files = [];
|
|
729
|
+
for (const name of [...CARD_PLANES, ...WORN_PLANES]) {
|
|
730
|
+
const entry = data[name];
|
|
731
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
732
|
+
const candidates = [entry.file, entry.worn, entry.skills];
|
|
733
|
+
if (Array.isArray(entry.files)) candidates.push(...entry.files);
|
|
734
|
+
for (const file of candidates) {
|
|
735
|
+
if (typeof file === 'string' && file.includes('.') && file.includes('/')) files.push(file);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return files;
|
|
739
|
+
} catch {
|
|
740
|
+
return [];
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function opensLensFile(text) {
|
|
745
|
+
const spoken = String(text || '');
|
|
746
|
+
return mapFiles().some((file) => spoken.includes(file));
|
|
747
|
+
}
|
|
687
748
|
|
|
688
749
|
function researchCall(toolName, text) {
|
|
689
750
|
const tool = String(toolName || '');
|
|
690
751
|
const spoken = String(text || '');
|
|
691
752
|
if (/researcher|explorer|deep[- ]?research/i.test(tool) || DEEP_SEARCH.test(tool)) return true;
|
|
692
|
-
|
|
753
|
+
if (/bash|shell/i.test(tool) && (/researcher|explorer|deep[- ]?research/i.test(spoken) || DEEP_SEARCH.test(spoken) || shellSearches(spoken))) return true;
|
|
754
|
+
if (/^(grep|glob)$/i.test(tool)) return true;
|
|
755
|
+
return /^read_file$/i.test(tool) && !opensLensFile(spoken);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function passPath(root) {
|
|
759
|
+
return join(root, '.xray', 'state', 'goggles-lens-pass.json');
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function loadReadPass(root) {
|
|
763
|
+
try {
|
|
764
|
+
const data = JSON.parse(readFileSync(passPath(root), 'utf8'));
|
|
765
|
+
if (!data || data.read !== true) return null;
|
|
766
|
+
return data.spent === true ? 'spent' : 'armed';
|
|
767
|
+
} catch {
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function saveReadPass(root, spent) {
|
|
773
|
+
const file = passPath(root);
|
|
774
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
775
|
+
writeFileSync(file, `${JSON.stringify({ read: true, spent: Boolean(spent) })}\n`);
|
|
693
776
|
}
|
|
694
777
|
|
|
695
778
|
/** One plane: the files the suit continues with. A look with no plane stops. Two planes stay quiet. */
|
|
@@ -724,11 +807,22 @@ function stampPlane(root, id) {
|
|
|
724
807
|
}
|
|
725
808
|
}
|
|
726
809
|
|
|
727
|
-
/** A search stays stopped. The
|
|
810
|
+
/** A search stays stopped. The lens file stays open. One later read may go on, then the next read stops. */
|
|
728
811
|
export function lensBeforeResearch(root, toolName, text) {
|
|
729
812
|
const spoken = String(text || '');
|
|
730
813
|
if (!researchCall(toolName, spoken)) return null;
|
|
731
814
|
const names = CARD_PLANES.filter((name) => intentWords(spoken).includes(name));
|
|
815
|
+
if (/^read_file$/i.test(String(toolName || ''))) {
|
|
816
|
+
if (loadReadPass(root) === 'armed') {
|
|
817
|
+
saveReadPass(root, true);
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
if (loadReadPass(root) !== 'spent') {
|
|
821
|
+
if (names.length === 1) stampPlane(root, names[0]);
|
|
822
|
+
saveReadPass(root, false);
|
|
823
|
+
}
|
|
824
|
+
return { gate: 'lens', decision: 'deny', reason: lensPage(root, names) };
|
|
825
|
+
}
|
|
732
826
|
if (names.length === 1) stampPlane(root, names[0]);
|
|
733
827
|
return { gate: 'lens', decision: 'deny', reason: lensPage(root, names) };
|
|
734
828
|
}
|
|
@@ -772,22 +866,17 @@ function seenFields(plane, root) {
|
|
|
772
866
|
};
|
|
773
867
|
}
|
|
774
868
|
|
|
775
|
-
function
|
|
776
|
-
if (key === 'files')
|
|
777
|
-
|
|
778
|
-
return Array.isArray(stored.files) ? stored.files.map(String) : [];
|
|
779
|
-
}
|
|
780
|
-
if (seen[key]) return seen[key];
|
|
781
|
-
return stored[key] || '';
|
|
869
|
+
function freshField(seen, key) {
|
|
870
|
+
if (key === 'files') return Array.isArray(seen.files) ? seen.files.map(String) : [];
|
|
871
|
+
return seen[key] || '';
|
|
782
872
|
}
|
|
783
873
|
|
|
784
874
|
export function growPlane(id, platesDir, root) {
|
|
785
875
|
const seen = seenFields(assemblePlane(id, platesDir), sourceRoot(platesDir));
|
|
786
876
|
const views = loadViews(root);
|
|
787
|
-
const stored = views[id] && typeof views[id] === 'object' ? views[id] : {};
|
|
788
877
|
const grown = { id };
|
|
789
|
-
for (const key of VIEW_KEYS) grown[key] =
|
|
790
|
-
if (id === 'ground') grown.from =
|
|
878
|
+
for (const key of VIEW_KEYS) grown[key] = freshField(seen, key);
|
|
879
|
+
if (id === 'ground') grown.from = '';
|
|
791
880
|
views[id] = {};
|
|
792
881
|
for (const key of VIEW_KEYS) views[id][key] = grown[key];
|
|
793
882
|
saveViews(root, views);
|
|
@@ -876,17 +965,13 @@ function isGogglesWork(text) {
|
|
|
876
965
|
|
|
877
966
|
|
|
878
967
|
export function cardStop(root, toolName, text, paths = []) {
|
|
879
|
-
const spoken = String(text || '');
|
|
880
|
-
if (spoken.length <= 200 && isGogglesWork(spoken) && !(paths || []).some((item) => /goggles/i.test(String(item)))) {
|
|
881
|
-
clearReading(root);
|
|
882
|
-
clearCardLine(root);
|
|
883
|
-
return null;
|
|
884
|
-
}
|
|
885
968
|
const held = loadReading(root);
|
|
886
969
|
if (!held) return null;
|
|
887
970
|
const drift = actualityLine();
|
|
888
971
|
if (drift) return organDeny(drift);
|
|
889
|
-
|
|
972
|
+
const stopped = organStop(held, { tool: toolName, text, paths });
|
|
973
|
+
if (stopped) return stopped;
|
|
974
|
+
return { gate: 'goggles', decision: 'allow' };
|
|
890
975
|
}
|
|
891
976
|
|
|
892
977
|
function stationFile(root) {
|
|
@@ -928,14 +1013,16 @@ export function handCard(root, intent) {
|
|
|
928
1013
|
clearCardLine(root);
|
|
929
1014
|
return null;
|
|
930
1015
|
}
|
|
931
|
-
|
|
1016
|
+
const asked = lookVerb(words);
|
|
1017
|
+
const planes = acceptedPlanes().filter((name) => words.includes(name));
|
|
1018
|
+
if (!asked && planes.length === 0) return null;
|
|
1019
|
+
if (!asked && planes.length === 1 && !HOLDS_ON_NAME.has(planes[0])) return null;
|
|
932
1020
|
clearCardLine(root);
|
|
933
1021
|
const looked = readGoggles(words);
|
|
934
1022
|
if (looked.text.startsWith('Actuality. Drift')) {
|
|
935
1023
|
saveReading(root, { drift: looked.text });
|
|
936
1024
|
return null;
|
|
937
1025
|
}
|
|
938
|
-
const planes = acceptedPlanes().filter((name) => words.includes(name));
|
|
939
1026
|
if (planes.length !== 1) {
|
|
940
1027
|
clearReading(root);
|
|
941
1028
|
return null;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each chat can actually do.
|
|
3
|
+
* Friend line first. Hook names are labels, not the sentence.
|
|
4
|
+
*
|
|
5
|
+
* stopsTool — the host can refuse a tool before it runs.
|
|
6
|
+
* noticesDeath — a crush/compact hook runs.
|
|
7
|
+
* note — what comes back into the chat: none, one-line, or the whole card.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const HOST_TRUTH = [
|
|
11
|
+
{
|
|
12
|
+
id: 'cursor',
|
|
13
|
+
name: 'Cursor',
|
|
14
|
+
stopsTool: true,
|
|
15
|
+
noticesDeath: true,
|
|
16
|
+
note: 'one-line',
|
|
17
|
+
hooks: 'preToolUse, preCompact',
|
|
18
|
+
line: 'Cursor can stop a tool. When the chat dies, it can leave one line in the next message. The rest of the card is a file. Read it.',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
id: 'grok',
|
|
22
|
+
name: 'Grok',
|
|
23
|
+
stopsTool: true,
|
|
24
|
+
noticesDeath: true,
|
|
25
|
+
note: 'none',
|
|
26
|
+
hooks: 'PreToolUse, PreCompact',
|
|
27
|
+
line: 'Grok can stop a tool. When the chat dies, the hook still writes the file, and the host throws the note away. Read the file.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'grok-bot',
|
|
31
|
+
name: 'Grok Bot',
|
|
32
|
+
stopsTool: false,
|
|
33
|
+
noticesDeath: false,
|
|
34
|
+
note: 'none',
|
|
35
|
+
hooks: 'none',
|
|
36
|
+
line: 'Grok Bot cannot stop a tool and cannot leave a note. The house is how a team shares rules there.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'openclaw',
|
|
40
|
+
name: 'OpenClaw',
|
|
41
|
+
stopsTool: true,
|
|
42
|
+
noticesDeath: false,
|
|
43
|
+
note: 'none',
|
|
44
|
+
hooks: 'PreToolUse',
|
|
45
|
+
line: 'OpenClaw can stop a tool. It has no hook for when the chat dies, and it does not leave a note.',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'hermes',
|
|
49
|
+
name: 'Hermes',
|
|
50
|
+
stopsTool: true,
|
|
51
|
+
noticesDeath: false,
|
|
52
|
+
note: 'none',
|
|
53
|
+
hooks: 'pre_tool_call',
|
|
54
|
+
line: 'Hermes can stop a tool. It does not register a hook for when the chat dies, and it does not leave a note.',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'opencode',
|
|
58
|
+
name: 'OpenCode',
|
|
59
|
+
stopsTool: true,
|
|
60
|
+
noticesDeath: false,
|
|
61
|
+
note: 'whole-card',
|
|
62
|
+
hooks: 'tool.execute.before',
|
|
63
|
+
line: 'OpenCode can stop a tool. It does not register a hook for when the chat dies. When the card file exists, the whole card is already in the prompt.',
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
export function hostTruthById(id) {
|
|
68
|
+
return HOST_TRUTH.find((row) => row.id === id) ?? null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The card a person reads. One paragraph per chat. */
|
|
72
|
+
export function hostTruthCard() {
|
|
73
|
+
return HOST_TRUTH.map((row) => row.line).join('\n\n');
|
|
74
|
+
}
|
|
@@ -1252,6 +1252,9 @@ function formatStationMarkdown(fields) {
|
|
|
1252
1252
|
} else {
|
|
1253
1253
|
lines.push("Git: n/a");
|
|
1254
1254
|
}
|
|
1255
|
+
if (fields.freshnessLine) {
|
|
1256
|
+
lines.push(fields.freshnessLine);
|
|
1257
|
+
}
|
|
1255
1258
|
lines.push(fields.repertoireResume || "Repertoire: module unresolved");
|
|
1256
1259
|
if (fields.workingLine) {
|
|
1257
1260
|
lines.push(fields.workingLine);
|
|
@@ -13,7 +13,6 @@ const STORY_TYPES = ['reflection', 'saga', 'journey', 'narrative'];
|
|
|
13
13
|
function resolveSkillMd() {
|
|
14
14
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
15
|
const candidates = [
|
|
16
|
-
join(here, '../../../skills/storyteller/SKILL.md'),
|
|
17
16
|
join(here, '../../skills/storyteller/SKILL.md'),
|
|
18
17
|
join(process.cwd(), 'node_modules/0xray/dist/skills/storyteller/SKILL.md'),
|
|
19
18
|
join(process.cwd(), 'node_modules/xray/dist/skills/storyteller/SKILL.md'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-host delegation gate SSOT — pending-delegations + spawn todo enforcement.
|
|
3
3
|
* Grok / Hermes / OpenCode adapters call evaluatePreToolGate via delegation-gate-runtime.
|
|
4
|
-
* Constitution (11/29/69 + destructive shell) lives here. Grok may still extra-block Codex 2/7.
|
|
4
|
+
* Constitution (11/29/69/70 + destructive shell) lives here. Grok may still extra-block Codex 2/7.
|
|
5
5
|
*/
|
|
6
6
|
import { type PendingDelegation } from './pending-delegations.js';
|
|
7
7
|
export { getActivePendingDelegations } from './pending-delegations.js';
|
|
@@ -91,7 +91,7 @@ export declare function evaluateSpawnPlanGate(toolName: string, toolInput: ToolG
|
|
|
91
91
|
export declare function evaluateSynthesisGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
92
92
|
/** Block writes / unrelated work while pending delegations exist. */
|
|
93
93
|
export declare function evaluatePendingDelegationGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
94
|
-
/** Codex 11 / 29 / 69 + destructive shell — always on, all hosts that call this SSOT. */
|
|
94
|
+
/** Codex 11 / 29 / 69 / 70 + destructive shell — always on, all hosts that call this SSOT. */
|
|
95
95
|
export declare function evaluateConstitutionGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
96
96
|
export declare function evaluatePreToolGate(toolName: string, toolInput: ToolGateInput, ctx: PreToolGateContext): PreToolGateResult;
|
|
97
97
|
export declare function evaluatePostToolSpawn(toolName: string, toolInput: ToolGateInput, projectRoot: string, options?: PostToolSpawnOptions): PostToolSpawnResult;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-host delegation gate SSOT — pending-delegations + spawn todo enforcement.
|
|
3
3
|
* Grok / Hermes / OpenCode adapters call evaluatePreToolGate via delegation-gate-runtime.
|
|
4
|
-
* Constitution (11/29/69 + destructive shell) lives here. Grok may still extra-block Codex 2/7.
|
|
4
|
+
* Constitution (11/29/69/70 + destructive shell) lives here. Grok may still extra-block Codex 2/7.
|
|
5
5
|
*/
|
|
6
6
|
import * as fs from 'fs';
|
|
7
7
|
import * as path from 'path';
|
|
@@ -11,6 +11,7 @@ import { allPlanTodos, areSynthesisConsultTodosComplete, findRecentStalePlanArch
|
|
|
11
11
|
import { getSynthesisDueReason, isSynthesisCheckpointDue, recordExecutionSlice, } from './synthesis.js';
|
|
12
12
|
import { isSynthesisConsultTodoId, tryRecordSynthesisConsultReceipt, } from './synthesis-consult-receipt.js';
|
|
13
13
|
import { resolveSpawnPlan, hasValidSpawnPlanContext } from './spawn-plan-resolution.js';
|
|
14
|
+
import { probeFreshness } from './work-freshness.mjs';
|
|
14
15
|
import { isUserAsidesEnabled, isUserAsideTodoId, loadActiveAsidePointer, loadActiveUserAside, } from './user-aside.js';
|
|
15
16
|
import { validateAsideWorktreeCwd, } from './aside-worktree.js';
|
|
16
17
|
import { ceremonyForProfile, resolveSuitProfile, spawnPlanModeForProfile, writeSuitSessionBoot, maybeHeatHostStation, } from './suit-temperament.js';
|
|
@@ -416,11 +417,21 @@ function collectWritePaths(toolInput) {
|
|
|
416
417
|
}
|
|
417
418
|
return paths;
|
|
418
419
|
}
|
|
419
|
-
/** Codex 11 / 29 / 69 + destructive shell — always on, all hosts that call this SSOT. */
|
|
420
|
+
/** Codex 11 / 29 / 69 / 70 + destructive shell — always on, all hosts that call this SSOT. */
|
|
420
421
|
export function evaluateConstitutionGate(toolName, toolInput, ctx) {
|
|
421
422
|
const content = collectWriteContent(toolInput);
|
|
422
423
|
const paths = collectWritePaths(toolInput);
|
|
423
424
|
const writing = isWriteTool(toolName, ctx.host ?? 'generic');
|
|
425
|
+
if (writing) {
|
|
426
|
+
const freshness = probeFreshness(ctx.projectRoot);
|
|
427
|
+
if (freshness.stale) {
|
|
428
|
+
return {
|
|
429
|
+
allow: false,
|
|
430
|
+
reason: freshness.reason,
|
|
431
|
+
gate: 'codex-70',
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
}
|
|
424
435
|
if (writing && ctx.features.no_new_surface !== false) {
|
|
425
436
|
for (const p of paths) {
|
|
426
437
|
const normalized = p.replace(/\\/g, '/');
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codex 70. An edit waits until this copy is on the latest main
|
|
3
|
+
* and the package is not older than the published one.
|
|
4
|
+
* This repo is that package when package.json name is 0xray.
|
|
5
|
+
* The check is saved at session start. The edit gate reads the saved result.
|
|
6
|
+
* A temp directory inside a repo is not the workspace, so unit tests stay quiet.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { execFileSync } from 'node:child_process';
|
|
10
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
11
|
+
import { join, resolve } from 'node:path';
|
|
12
|
+
|
|
13
|
+
export function compareVersions(left, right) {
|
|
14
|
+
const a = String(left).split('.').map((part) => parseInt(part, 10) || 0);
|
|
15
|
+
const b = String(right).split('.').map((part) => parseInt(part, 10) || 0);
|
|
16
|
+
const length = Math.max(a.length, b.length);
|
|
17
|
+
for (let i = 0; i < length; i += 1) {
|
|
18
|
+
const da = a[i] || 0;
|
|
19
|
+
const db = b[i] || 0;
|
|
20
|
+
if (da !== db) return da < db ? -1 : 1;
|
|
21
|
+
}
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function decideFreshness(input) {
|
|
26
|
+
const behind = Number(input.behind) || 0;
|
|
27
|
+
if (behind > 0) {
|
|
28
|
+
const commits = behind === 1 ? '1 commit' : `${behind} commits`;
|
|
29
|
+
return {
|
|
30
|
+
stale: true,
|
|
31
|
+
reason: `Codex 70: this copy is ${commits} behind main. Update before you edit. Compare set-aside work to main before you throw it away.`,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const repoVersion = input.repoName === '0xray' ? input.repoVersion || null : null;
|
|
35
|
+
const worn = input.wornSuit || repoVersion;
|
|
36
|
+
const published = input.publishedSuit || null;
|
|
37
|
+
const target = published || repoVersion;
|
|
38
|
+
if (target && worn && compareVersions(worn, target) < 0) {
|
|
39
|
+
return {
|
|
40
|
+
stale: true,
|
|
41
|
+
reason: `Codex 70: package ${worn} is older than ${target}. Install the current one before you edit.`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { stale: false, reason: '' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function describeFreshness(input) {
|
|
48
|
+
const behind = Number(input.behind) || 0;
|
|
49
|
+
const parts = [behind > 0 ? `${behind} behind main` : 'up to date'];
|
|
50
|
+
if (input.wornSuit) parts.push(`package ${input.wornSuit}`);
|
|
51
|
+
if (input.publishedSuit) parts.push(`published ${input.publishedSuit}`);
|
|
52
|
+
const stashCount = Number(input.stashCount) || 0;
|
|
53
|
+
if (stashCount > 0) parts.push(`${stashCount} set aside, compare before drop`);
|
|
54
|
+
return `Fresh: ${parts.join('. ')}.`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function run(cmd, args, cwd, timeout) {
|
|
58
|
+
return execFileSync(cmd, args, {
|
|
59
|
+
cwd,
|
|
60
|
+
encoding: 'utf8',
|
|
61
|
+
timeout,
|
|
62
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
63
|
+
}).trim();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function gitTop(cwd) {
|
|
67
|
+
try {
|
|
68
|
+
return run('git', ['rev-parse', '--show-toplevel'], cwd, 3000);
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readJson(file) {
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function wornSuitVersion(projectRoot, opts = {}) {
|
|
83
|
+
if (projectRoot) {
|
|
84
|
+
const local = readJson(join(projectRoot, 'node_modules', '0xray', 'package.json'));
|
|
85
|
+
if (local && local.version) return String(local.version);
|
|
86
|
+
}
|
|
87
|
+
const candidates = [
|
|
88
|
+
'/opt/homebrew/lib/node_modules/0xray/package.json',
|
|
89
|
+
'/usr/local/lib/node_modules/0xray/package.json',
|
|
90
|
+
];
|
|
91
|
+
for (const file of candidates) {
|
|
92
|
+
const pkg = readJson(file);
|
|
93
|
+
if (pkg && pkg.version) return String(pkg.version);
|
|
94
|
+
}
|
|
95
|
+
if (!opts.liveGlobal) return null;
|
|
96
|
+
try {
|
|
97
|
+
const root = run('npm', ['root', '-g'], projectRoot || process.cwd(), 8000);
|
|
98
|
+
const pkg = readJson(join(root, '0xray', 'package.json'));
|
|
99
|
+
return pkg && pkg.version ? String(pkg.version) : null;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function readWorkSnapshot(projectRoot) {
|
|
106
|
+
const top = gitTop(projectRoot);
|
|
107
|
+
let behind = 0;
|
|
108
|
+
let stashCount = 0;
|
|
109
|
+
if (top && resolve(top) === resolve(projectRoot)) {
|
|
110
|
+
try {
|
|
111
|
+
behind = Number(run('git', ['rev-list', '--count', 'HEAD..origin/main'], projectRoot, 4000)) || 0;
|
|
112
|
+
} catch {
|
|
113
|
+
behind = 0;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const list = run('git', ['stash', 'list'], projectRoot, 4000);
|
|
117
|
+
stashCount = list ? list.split('\n').filter(Boolean).length : 0;
|
|
118
|
+
} catch {
|
|
119
|
+
stashCount = 0;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const repo = readJson(join(projectRoot, 'package.json'));
|
|
123
|
+
const saved = readJson(join(projectRoot, '.xray', 'state', 'freshness.json'));
|
|
124
|
+
return {
|
|
125
|
+
behind,
|
|
126
|
+
stashCount,
|
|
127
|
+
repoName: repo && repo.name ? String(repo.name) : null,
|
|
128
|
+
repoVersion: repo && repo.version ? String(repo.version) : null,
|
|
129
|
+
wornSuit: wornSuitVersion(projectRoot),
|
|
130
|
+
publishedSuit: saved && saved.publishedSuit ? String(saved.publishedSuit) : null,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function freshnessFile(projectRoot) {
|
|
135
|
+
return join(projectRoot, '.xray', 'state', 'freshness.json');
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function readSavedFreshness(projectRoot) {
|
|
139
|
+
const saved = readJson(freshnessFile(projectRoot));
|
|
140
|
+
return saved ? describeFreshness(saved) : null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function probeFreshness(projectRoot) {
|
|
144
|
+
const saved = readJson(freshnessFile(projectRoot));
|
|
145
|
+
if (saved && saved.behind != null) return decideFreshness(saved);
|
|
146
|
+
return decideFreshness(readWorkSnapshot(projectRoot));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function refreshFreshness(projectRoot) {
|
|
150
|
+
const top = gitTop(projectRoot);
|
|
151
|
+
if (top && resolve(top) === resolve(projectRoot)) {
|
|
152
|
+
try {
|
|
153
|
+
run('git', ['fetch', 'origin', '--prune'], projectRoot, 8000);
|
|
154
|
+
} catch {
|
|
155
|
+
/* offline fetch must not block the boot */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
let publishedSuit = null;
|
|
159
|
+
try {
|
|
160
|
+
publishedSuit = run('npm', ['view', '0xray', 'version'], projectRoot, 8000);
|
|
161
|
+
} catch {
|
|
162
|
+
publishedSuit = null;
|
|
163
|
+
}
|
|
164
|
+
const snapshot = readWorkSnapshot(projectRoot);
|
|
165
|
+
if (publishedSuit) snapshot.publishedSuit = publishedSuit;
|
|
166
|
+
if (snapshot.repoName === '0xray' && snapshot.repoVersion) {
|
|
167
|
+
snapshot.wornSuit = snapshot.repoVersion;
|
|
168
|
+
} else {
|
|
169
|
+
const globalWorn = wornSuitVersion(projectRoot, { liveGlobal: true });
|
|
170
|
+
if (globalWorn) snapshot.wornSuit = globalWorn;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const dir = join(projectRoot, '.xray', 'state');
|
|
174
|
+
mkdirSync(dir, { recursive: true });
|
|
175
|
+
writeFileSync(freshnessFile(projectRoot), `${JSON.stringify({
|
|
176
|
+
publishedSuit: snapshot.publishedSuit,
|
|
177
|
+
wornSuit: snapshot.wornSuit,
|
|
178
|
+
behind: snapshot.behind,
|
|
179
|
+
stashCount: snapshot.stashCount,
|
|
180
|
+
checkedAt: new Date().toISOString(),
|
|
181
|
+
}, null, 2)}\n`);
|
|
182
|
+
} catch {
|
|
183
|
+
/* the gate still reads git and the worn package */
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
...snapshot,
|
|
187
|
+
freshnessLine: describeFreshness(snapshot),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
@@ -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 (70 terms) for systematic error prevention.`,
|
|
228
228
|
"",
|
|
229
229
|
].join("\n"),
|
|
230
230
|
"## CLI Commands": [
|
|
@@ -20,6 +20,11 @@ sidebar_label: Boot
|
|
|
20
20
|
┌─────────────────────────────┼──────────────────────────────┐
|
|
21
21
|
│ PROCESSING LAYER v │
|
|
22
22
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
23
|
+
│ │ memory monitoring │ │
|
|
24
|
+
│ │ setupMemoryMonitoring │ │
|
|
25
|
+
│ └──────────────────────────┬─────────────────────────┘ │
|
|
26
|
+
│ v │
|
|
27
|
+
│ ┌────────────────────────────────────────────────────┐ │
|
|
23
28
|
│ │ load .xray config │ │
|
|
24
29
|
│ └──────────────────────────┬─────────────────────────┘ │
|
|
25
30
|
│ v │
|
|
@@ -33,12 +38,22 @@ sidebar_label: Boot
|
|
|
33
38
|
│ v │
|
|
34
39
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
35
40
|
│ │ activateProcessors (PROCESSOR_DEFS) │ │
|
|
36
|
-
│
|
|
41
|
+
│ └──────────────────────────┬─────────────────────────┘ │
|
|
42
|
+
│ v │
|
|
43
|
+
│ ┌────────────────────────────────────────────────────┐ │
|
|
44
|
+
│ │ processor health │ │
|
|
45
|
+
│ │ validateProcessorHealth │ │
|
|
46
|
+
│ └──────────────────────────┬─────────────────────────┘ │
|
|
47
|
+
│ v │
|
|
48
|
+
│ ┌────────────────────────────────────────────────────┐ │
|
|
49
|
+
│ │ agents and plugins │ │
|
|
50
|
+
│ │ loadRemainingAgents · initializePluginSystem │ │
|
|
37
51
|
│ └──────────────────────────┬─────────────────────────┘ │
|
|
38
52
|
│ v │
|
|
39
53
|
│ ┌────────────────────────────────────────────────────┐ │
|
|
40
54
|
│ │ enforcement · codex compliance │ │
|
|
41
|
-
│ │
|
|
55
|
+
│ │ finalizeSecurityIntegration │ │
|
|
56
|
+
│ │ governance init when that flag is not false │ │
|
|
42
57
|
│ └──────────────────────────┬─────────────────────────┘ │
|
|
43
58
|
└──────────────────────────────┼────────────────────────────┘
|
|
44
59
|
v
|
|
@@ -5,7 +5,7 @@ sidebar_label: Governance
|
|
|
5
5
|
|
|
6
6
|
# Governance
|
|
7
7
|
|
|
8
|
-
A proposal is deliberated inside the suit, filtered by Dynamo Solar, then merged. The PreToolUse codex gate stands beside the vote.
|
|
8
|
+
A proposal is deliberated inside the suit, filtered by Dynamo Solar, then merged. The vote is approve, reject, or needs_revision. The PreToolUse codex gate stands beside the vote.
|
|
9
9
|
|
|
10
10
|
```
|
|
11
11
|
┌────────────────────────────────────────────────────────────┐
|
|
@@ -44,7 +44,8 @@ A proposal is deliberated inside the suit, filtered by Dynamo Solar, then merged
|
|
|
44
44
|
│ OUTPUT LAYER v │
|
|
45
45
|
│ ┌──────────────────────┐ ┌──────────────────────┐ │
|
|
46
46
|
│ │ vote │ │ codex decision │ │
|
|
47
|
-
│ │ approve / reject
|
|
47
|
+
│ │ approve / reject / │ │ allow / deny │ │
|
|
48
|
+
│ │ needs_revision │ │ │ │
|
|
48
49
|
│ └──────────────────────┘ └──────────────────────┘ │
|
|
49
50
|
└────────────────────────────────────────────────────────────┘
|
|
50
51
|
```
|
|
@@ -23,6 +23,8 @@ Plates have a type. A domain model plate shows how named parts fit. A state flow
|
|
|
23
23
|
|
|
24
24
|
[House](./house.md)
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Pipeline
|
|
27
27
|
|
|
28
28
|
[Routing](./routing.md) · [Governance](./governance.md) · [Boot](./boot.md) · [Orchestration](./orchestration.md) · [Processor](./processor.md) · [Reporting](./reporting.md) · [Memory recall](./memory-recall.md)
|
|
29
|
+
|
|
30
|
+
These seven are the lens planes. Each drawing is an input layer, a processing layer, and an output layer. Ground has no plate file.
|