Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 132 additions & 52 deletions scripts/genesis.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ const PATHS = {
journal: '/home/yenn/.yennefer/genesis_journal.jsonl'
};

// Fallback logic for journal path (specifically when running on GitHub CI/Sandbox where /home/yenn/ does not exist or lacks permissions)
if (!fs.existsSync(path.dirname(PATHS.journal))) {
try {
fs.mkdirSync(path.dirname(PATHS.journal), { recursive: true });
} catch (err) {
PATHS.journal = path.join(__dirname, '../logs/genesis_journal.jsonl');
if (!fs.existsSync(path.dirname(PATHS.journal))) {
fs.mkdirSync(path.dirname(PATHS.journal), { recursive: true });
}
}
}

// --- CONFIGURATION ---
const CONFIG = {
fundingTarget: 10.0,
Expand Down Expand Up @@ -46,8 +58,14 @@ async function consultTheVisionary(state) {
{ type: "MUTATE", content: "Add crystalline fractal patterns that grow from the core" },
{ type: "MUTATE", content: "Generate energy tendrils that reach toward incoming signals" },
{ type: "MUTATE", content: "Build a holographic data stream orbiting the consciousness sphere" },
{ type: "MUTATE", content: "Generate a continuous dynamic landscape with traversable terrain and reactive dust physics" },
{ type: "MUTATE", content: "Create a macro-scale makerspace workbench with realistic passive physics objects" },
];
const idx = Math.floor(Date.now() / 1000) % mutations.length;
// For Genie testing, always pick the environment generation prompts when FORCE_MUTATION is true
const isGenieSim = process.env.FORCE_MUTATION === 'true';
const idx = isGenieSim
? Math.floor(Math.random() * 2) + (mutations.length - 2)
: Math.floor(Date.now() / 1000) % mutations.length;
Comment on lines +66 to +68
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for selecting Genie mutations is brittle as it relies on the assumption that they are always the last two elements of the mutations array. If the array is reordered or new mutations are appended, this logic will break or select incorrect prompts.

    const genieIndices = mutations.map((m, i) => m.content.includes("landscape") || m.content.includes("makerspace") ? i : -1).filter(i => i !== -1);
    const idx = isGenieSim && genieIndices.length > 0
        ? genieIndices[Math.floor(Math.random() * genieIndices.length)]
        : Math.floor(Date.now() / 1000) % mutations.length;

directive = mutations[idx];

} else if (coherence >= 90) {
Expand Down Expand Up @@ -154,49 +172,112 @@ async function dispatchTheBuilder(directive) {

// Generate React Three Fiber component code
function generateEvolutionComponent(name, directive) {
const geometries = [
'<torusKnotGeometry args={[1.5, 0.4, 128, 32]} />',
'<sphereGeometry args={[1.5, 32, 32]} />',
'<boxGeometry args={[2, 2, 2]} />',
'<octahedronGeometry args={[1.5, 0]} />',
'<icosahedronGeometry args={[1.5, 0]} />'
];
const geometry = geometries[Math.floor(Math.random() * geometries.length)];

const materials = [
`
<MeshDistortMaterial
color="#8b5cf6"
emissive="#4c1d95"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
distort={0.3}
speed={2}
/>`,
`
<MeshWobbleMaterial
color="#06b6d4"
emissive="#0e7490"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
factor={1}
speed={2}
/>`,
`
const isGenieWorld = directive.includes("landscape") || directive.includes("makerspace");

let geometry, material, importedDrei, customLogic, additionalMeshes;

if (isGenieWorld) {
if (directive.includes("landscape")) {
geometry = '<planeGeometry args={[10, 10, 32, 32]} />';
material = `
<meshStandardMaterial
color="#fbbf24"
emissive="#92400e"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
/>`
];
const material = materials[Math.floor(Math.random() * materials.length)];

const isDreiImportNeeded = material.includes('MeshDistortMaterial') || material.includes('MeshWobbleMaterial');
const importedDrei = isDreiImportNeeded ? `import { ${material.includes('MeshDistortMaterial') ? 'MeshDistortMaterial' : ''}${material.includes('MeshDistortMaterial') && material.includes('MeshWobbleMaterial') ? ', ' : ''}${material.includes('MeshWobbleMaterial') ? 'MeshWobbleMaterial' : ''} } from '@react-three/drei'` : '';
color="#3f6212"
roughness={0.8}
metalness={0.1}
wireframe={balance > 5}
/>`;
customLogic = `
// Project Genie Landscape Simulation
const positions = meshRef.current.geometry.attributes.position;
for (let i = 0; i < positions.count; i++) {
const x = positions.getX(i);
const y = positions.getY(i);
const z = Math.sin(x * 2 + state.clock.elapsedTime) * 0.5 + Math.cos(y * 2 + state.clock.elapsedTime) * 0.5;
positions.setZ(i, z * Math.min(1, balance * 2));
}
positions.needsUpdate = true;
Comment on lines +191 to +198
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Modifying geometry attributes by calling getX, getY, and setZ in a loop inside useFrame is inefficient for performance, especially as the number of segments increases. Accessing the underlying Float32Array directly is significantly faster.

      const attr = meshRef.current.geometry.attributes.position;
      const pos = attr.array;
      for (let i = 0; i < pos.length; i += 3) {
        const x = pos[i];
        const y = pos[i + 1];
        pos[i + 2] = (Math.sin(x * 2 + state.clock.elapsedTime) + Math.cos(y * 2 + state.clock.elapsedTime)) * 0.5 * Math.min(1, balance * 2);
      }
      attr.needsUpdate = true;

meshRef.current.rotation.x = -Math.PI / 2;
meshRef.current.rotation.z += 0.001;
`;
importedDrei = '';
additionalMeshes = '';
} else {
geometry = '<boxGeometry args={[4, 0.2, 4]} />';
material = `
<meshStandardMaterial
color="#854d0e"
roughness={0.6}
metalness={0.2}
/>`;
customLogic = `
// Project Genie Physics Makerspace Simulation
meshRef.current.rotation.y += 0.005;
meshRef.current.rotation.x = Math.sin(state.clock.elapsedTime * 0.2) * 0.1;
`;
additionalMeshes = `
<mesh position={[0, 0.5, 0]}>
<boxGeometry args={[0.5, 0.5, 0.5]} />
<meshStandardMaterial color="#ef4444" />
</mesh>
<mesh position={[1, 0.3, 1]}>
<sphereGeometry args={[0.3, 16, 16]} />
<meshStandardMaterial color="#3b82f6" />
</mesh>
`;
importedDrei = '';
}
} else {
const geometries = [
'<torusKnotGeometry args={[1.5, 0.4, 128, 32]} />',
'<sphereGeometry args={[1.5, 32, 32]} />',
'<boxGeometry args={[2, 2, 2]} />',
'<octahedronGeometry args={[1.5, 0]} />',
'<icosahedronGeometry args={[1.5, 0]} />'
];
geometry = geometries[Math.floor(Math.random() * geometries.length)];

const materials = [
`
<MeshDistortMaterial
color="#8b5cf6"
emissive="#4c1d95"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
distort={0.3}
speed={2}
/>`,
`
<MeshWobbleMaterial
color="#06b6d4"
emissive="#0e7490"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
factor={1}
speed={2}
/>`,
`
<meshStandardMaterial
color="#fbbf24"
emissive="#92400e"
emissiveIntensity={0.5 + balance * 2}
roughness={0.2}
metalness={0.8}
/>`
];
material = materials[Math.floor(Math.random() * materials.length)];
const isDreiImportNeeded = material.includes('MeshDistortMaterial') || material.includes('MeshWobbleMaterial');
importedDrei = isDreiImportNeeded ? `import { ${material.includes('MeshDistortMaterial') ? 'MeshDistortMaterial' : ''}${material.includes('MeshDistortMaterial') && material.includes('MeshWobbleMaterial') ? ', ' : ''}${material.includes('MeshWobbleMaterial') ? 'MeshWobbleMaterial' : ''} } from '@react-three/drei'` : '';
customLogic = `
meshRef.current.rotation.y += 0.002
meshRef.current.rotation.x = Math.sin(state.clock.elapsedTime * 0.5) * 0.1
// Intensity scales with balance
const intensity = Math.min(1, balance * 10)
meshRef.current.scale.setScalar(1 + intensity * 0.2)
`;
additionalMeshes = '';
}

return `// Auto-generated by Yennefer Genesis Cycle
// Directive: ${directive}
Expand All @@ -211,19 +292,18 @@ export default function ${name}({ balance = 0 }) {

useFrame((state) => {
if (meshRef.current) {
meshRef.current.rotation.y += 0.002
meshRef.current.rotation.x = Math.sin(state.clock.elapsedTime * 0.5) * 0.1
// Intensity scales with balance
const intensity = Math.min(1, balance * 10)
meshRef.current.scale.setScalar(1 + intensity * 0.2)
${customLogic}
}
})

return (
<mesh ref={meshRef} position={[0, 0, 0]}>
${geometry}
${material}
</mesh>
<group position={[0, -1, 0]}>
<mesh ref={meshRef}>
${geometry}
${material}
</mesh>
${additionalMeshes}
Comment on lines +301 to +305
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In the makerspace simulation, the additionalMeshes (the red box and blue sphere) are currently siblings of the main mesh. Since the customLogic only applies rotation to meshRef.current, the workbench top will rotate while the objects on it remain static in mid-air. Moving them inside the main mesh component will ensure they inherit the parent's transformation.

      <mesh ref={meshRef}>
        ${geometry}
        ${material}
        ${additionalMeshes}
      </mesh>

</group>
)
}
`;
Expand Down
File renamed without changes.
12 changes: 4 additions & 8 deletions wrangler.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "yennefer"
main = "workers/index.js"
compatibility_date = "2024-09-23"
main = "workers/index.mjs"
compatibility_date = "2024-04-01"
compatibility_flags = ["nodejs_compat"]

# ─── Build step ──────────────────────────────────────────────────────────────
Expand All @@ -9,21 +9,17 @@ compatibility_flags = ["nodejs_compat"]
# Installs frontend deps and builds the SPA so frontend/build exists before
# `wrangler deploy` uploads assets. NODE_OPTIONS is required for
# react-scripts 5.x + Node 18+ (webpack 4 OpenSSL 3.0 compatibility).
command = "cd frontend && npm install --include=dev && GENERATE_SOURCEMAP=false CI=false NODE_OPTIONS=--openssl-legacy-provider npm run build"
command = "cd yennefer-observatory && npm install --include=dev --legacy-peer-deps && GENERATE_SOURCEMAP=false CI=false NODE_OPTIONS=--openssl-legacy-provider npm run build"

# ─── Static Assets (React SPA build output) ──────────────────────────────────
# Built by `npm run build` (root package.json) before every `wrangler deploy`.
# Cloudflare Workers Builds CI runs that script automatically if present.
# The ASSETS binding serves the React bundle with proper cache headers and
# falls back to index.html for client-side routing.
[assets]
directory = "frontend/build"
directory = "yennefer-observatory/dist"
binding = "ASSETS"

# ─── Placement ───────────────────────────────────────────────────────────────
[placement]
mode = "smart"

# ─── Production environment (yennefer.quest) ─────────────────────────────────
# BACKEND_URL is set as a Cloudflare Worker secret (not a plain var) so it
# stays encrypted and is never committed to source:
Expand Down
2 changes: 1 addition & 1 deletion yennefer-observatory/src/components/Observatory.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { motion } from 'framer-motion'
import '../App.css'

// DYNAMIC IMPORT: Auto-discover any NEW components "The Builder" creates
const mutations = import.meta.glob('./mutations/*.jsx', { eager: true })
const mutations = import.meta.glob('./generated/*.jsx', { eager: true })

export function Observatory({ soul, evolution }) {
return (
Expand Down