{ "schemaVersion": "1.0.0", "project": { "name": "blb-throne-of-the-bone-king", "displayName": "Bone Lord Bob - Throne of the Bone King", "purpose": "Landscape-first game shell with shared React Native UI across browser, Electron, and future native mobile targets.", "root": ".", "model": "single-repo", "repo": { "gitea": "http://100.79.253.19:3000/jacob-mathison/blb-throne-of-the-bone-king", "slug": "jacob-mathison/blb-throne-of-the-bone-king", "defaultBranch": "main" }, "universe": "Bone Lord Bob (BLB) - Mathison Projects Inc.", "status": "early-development", "summary": "Menu shell with persisted route navigation, settings, policies, changelog, credits, gameplay phase scaffolding, local saves, build metadata display, background music, browser/Electron rendering, and a browser-only socket-gated Toolkit for volatile gameplay/content systems." }, "principles": [ "Start with llm.txt for human-readable orientation.", "Use manifest.llm.json for machine-readable routing, commands, targets, and known gaps.", "Read AGENTS.md for coding-agent operating rules before broad changes.", "Shared UI under src/ is React Native, not raw HTML.", "Do not use DOM APIs in shared src/ code; platform shims belong in .web.ts files or src/web/.", "Metro bundles mobile; Vite bundles web and the Electron renderer.", "Navigation is Zustand-based; do not add React Navigation unless explicitly requested.", "Persistence is platform-split via persistStorage.ts for native and persistStorage.web.ts for web/Electron.", "Toolkit is browser-only, requires Browser resolution, and requires an active Socket.IO connection for entry.", "Commit as Jacob Mathison (jacob@mathisonprojects.com); never commit as an AI identity." ], "guideFiles": [ { "path": "llm.txt", "type": "root-orientation", "purpose": "First-read guide for project shape, stack, architecture, commands, conventions, and LLM notes.", "alwaysRead": true }, { "path": "manifest.llm.json", "type": "machine-manifest", "purpose": "Machine-readable routing manifest: targets, commands, task routes, known gaps, and verification baseline.", "alwaysRead": true }, { "path": "AGENTS.md", "type": "agent-instructions", "purpose": "Operational instructions for coding agents: ground rules, structure conventions, and common tasks.", "alwaysRead": true }, { "path": "README.md", "type": "human-overview", "purpose": "Human-facing overview for setup, commands, architecture, toolkit behavior, and validation." } ], "globalReadOrder": [ "llm.txt", "manifest.llm.json", "AGENTS.md", "README.md" ], "globalReadOrderSemantics": "Broad orientation order. Task routers should prefer taskRoutes read arrays to avoid over-reading.", "documentationFiles": [ { "path": "llm.txt", "type": "root-orientation", "purpose": "Human-readable first-read guide." }, { "path": "manifest.llm.json", "type": "machine-manifest", "purpose": "Machine-readable project manifest." }, { "path": "AGENTS.md", "type": "agent-instructions", "purpose": "Agent operating instructions." }, { "path": "README.md", "type": "human-overview", "purpose": "Human-facing setup and development guide." }, { "path": "socket/README.md", "type": "toolkit-socket-notes", "purpose": "Socket folder notes." }, { "path": "src/data/AGENTS.md", "type": "data-instructions", "purpose": "Rules for editable JSON content and Toolkit placeholder resources." } ], "targets": [ { "id": "web", "label": "Web", "runtime": "react-native-web in browser", "bundler": "Vite", "entry": "index.html -> src/web/main.tsx -> App.tsx", "devCommand": "npm run web", "devUrl": "http://127.0.0.1:5173", "primary": true }, { "id": "electron", "label": "Desktop", "runtime": "Electron Chromium renderer", "bundler": "Vite renderer + TypeScript main/preload compile", "entry": "electron/main.ts loads the Vite dev server in development or dist/renderer/ in production", "devCommand": "npm run electron:dev", "buildCommand": "npm run electron:build", "packageCommand": "npm run electron:package", "primary": true }, { "id": "toolkit-socket", "label": "Toolkit Socket", "runtime": "Node + Socket.IO", "entry": "socket/server.mjs", "devCommand": "npm run dev:toolkit", "healthUrl": "http://127.0.0.1:5174/health", "primary": true }, { "id": "android", "label": "Android", "runtime": "React Native", "bundler": "Metro", "entry": "index.js -> App.tsx", "devCommand": "npm run android", "status": "native android/ folder is not currently present in this checkout" }, { "id": "ios", "label": "iOS", "runtime": "React Native", "bundler": "Metro", "entry": "index.js -> App.tsx", "devCommand": "npm run ios", "status": "native ios/ folder is not currently present in this checkout" } ], "stack": [ "React Native 0.85.3", "React 19.2.3", "react-native-web ^0.21.2", "TypeScript ^5.8.3", "Zustand ^5.0.14", "@react-native-async-storage/async-storage ^3.1.1", "Vite ^8.0.16", "Electron ^42.4.0", "Socket.IO and socket.io-client ^4.8.3", "Sass ^1.101.0", "Jest", "ESLint + Prettier", "Husky pre-commit build version bump and validation", "Node >= 22.12.0" ], "scripts": { "dev": "npm run web", "web": "vite --host 127.0.0.1", "web:build": "vite build", "web:preview": "vite preview --host 127.0.0.1", "dev:toolkit": "node socket/server.mjs", "dev:all": "concurrently -k -n app,toolkit -c blue,magenta \"npm run dev\" \"npm run dev:toolkit\"", "dev:clear": "node socket/clear.mjs", "electron:dev": "npm run electron:compile && cross-env NODE_ENV=development VITE_DEV_SERVER_URL=http://127.0.0.1:5173 concurrently -k \"vite --host 127.0.0.1\" \"wait-on http://127.0.0.1:5173 && electron .\"", "electron:build": "npm run web:build && npm run electron:compile", "electron:compile": "tsc -p tsconfig.electron.json", "electron:package": "npm run electron:build && electron-builder", "lint": "eslint .", "test": "jest", "validate:commit": "npm run lint && npm run test -- --runInBand && npm run electron:build", "version:bump-build": "node tools/bump-build-version.mjs" }, "architecture": { "rootComponent": "App.tsx", "componentTree": [ "SafeAreaProvider", "GameViewport", "AppNavigator" ], "navigation": { "type": "zustand-store with animated fade transitions", "routes": "src/navigation/routes.ts", "navigator": "src/navigation/AppNavigator.tsx", "store": "src/state/navigationStore.ts", "persistKey": "blb-navigation" }, "state": { "library": "zustand", "stores": [ { "file": "src/state/navigationStore.ts", "persistKey": "blb-navigation", "purpose": "Current route and pending route persistence." }, { "file": "src/state/gameSettingsStore.ts", "persistKey": "blb-game-settings", "purpose": "Resolution, autosave, mute, and audio volume settings." }, { "file": "src/state/gameplayStore.ts", "persistKey": "blb-gameplay", "purpose": "Active gameplay run, day counter, phase state, and phase navigation." }, { "file": "src/state/saveGameStore.ts", "persistKey": "blb-save-games", "purpose": "Local save slots for gameplay runs." }, { "file": "src/state/toolkitSocketStore.ts", "purpose": "Toolkit socket connection status." }, { "file": "src/state/toolkitContentStore.ts", "purpose": "Toolkit credits content, revision, load status, and socket refresh/save actions." } ], "persistHelper": "src/state/createPersistOptions.ts", "platformStorage": { "mobile": "src/state/persistStorage.ts", "web": "src/state/persistStorage.web.ts" } }, "settings": { "file": "src/settings/gameSettings.ts", "resolutionIds": [ "browser", "iphoneMini", "iphoneDefault", "androidDefault", "phoneLarge" ], "volumeKeys": [ "master", "music", "ambience", "speech", "sfx" ] }, "screens": [ "src/screens/HomeScreen.tsx", "src/screens/GameplayScreen.tsx", "src/screens/SavesScreen.tsx", "src/screens/ChangelogScreen.tsx", "src/screens/SettingsScreen.tsx", "src/screens/PoliciesScreen.tsx", "src/screens/ToolkitScreen.tsx", "src/screens/PageScreen.tsx" ], "components": [ "src/components/GameViewport.tsx", "src/components/ScreenLayout.tsx", "src/components/MenuButton.tsx", "src/components/BackHomeButton.tsx" ], "assets": { "images": "src/assets/images/", "fonts": "src/assets/fonts/", "music": "src/assets/music/", "scss": "src/assets/scss/", "favicon": "src/assets/images/favicon.svg" }, "data": { "changelog": { "json": "src/data/changelog.json", "typedExport": "src/data/changelog.ts", "sortOrder": "newest-created-first" }, "credits": { "json": "src/data/credits.json", "typedExport": "src/data/credits.ts" }, "legal": { "json": "src/data/legal.json" }, "toolkitPlaceholders": { "directory": "src/data/toolkit/", "shape": "empty arrays until schemas stabilize", "files": [ "audio.json", "credits.json", "endings.json", "factions.json", "flags.json", "lore.json", "map.json", "opening.json", "petitioners.json", "references.json", "resources.json", "rulings.json", "seed.json", "story.json" ] } }, "audio": { "hook": "src/hooks/useBackgroundMusic.ts", "webHook": "src/hooks/useBackgroundMusic.web.ts", "defaultTrack": "src/assets/music/throne-of-the-bone-king.mp3", "volumeControls": [ "master", "music", "mute" ] }, "toolkit": { "screen": "src/screens/ToolkitScreen.tsx", "accessRules": [ "resolution setting must be browser", "socket status must be connected" ], "routes": [ "toolkitOpening", "toolkitPetitioners", "toolkitRulings", "toolkitSeed", "toolkitResources", "toolkitMap", "toolkitFactions", "toolkitFlags", "toolkitStory", "toolkitLore", "toolkitAudio", "toolkitCredits", "toolkitEndings", "toolkitReferences" ], "contentSync": { "server": "socket/server.mjs", "serverHelpers": "socket/contentStore.cjs", "client": "src/socket/toolkitSocketClient.ts", "store": "src/state/toolkitContentStore.ts", "events": [ "toolkit:content:read", "toolkit:content:save", "toolkit:content:changed", "toolkit:content:error" ] } }, "webEntry": "src/web/main.tsx", "electronMain": "electron/main.ts", "electronPreload": "electron/preload.ts", "buildOutputs": [ "dist/renderer/", "dist/electron/" ] }, "taskRoutes": { "ui-screen": { "description": "Add or modify screens, navigation routes, or shared UI components.", "read": [ "llm.txt", "manifest.llm.json", "AGENTS.md", "src/navigation/routes.ts", "src/navigation/AppNavigator.tsx", "" ], "update": [ "src/navigation/routes.ts when adding a route", "src/navigation/AppNavigator.tsx when wiring a route", "manifest.llm.json when screens, routes, or architecture sections change" ] }, "state": { "description": "Zustand stores, persistence, navigation, or game settings.", "read": [ "llm.txt", "manifest.llm.json", "src/state/", "src/settings/gameSettings.ts", "src/state/persistStorage.ts", "src/state/persistStorage.web.ts" ], "policies": [ "Extend persistStorage.ts and persistStorage.web.ts for platform storage changes.", "Do not embed DOM or AsyncStorage calls directly in stores.", "Add merge handling when persisted state shape changes." ], "update": [ "manifest.llm.json when stores, persist keys, or settings surface change" ] }, "web-electron": { "description": "Vite config, web entry, Electron shell, browser assets, SCSS, or platform-specific .web.ts overrides.", "read": [ "llm.txt", "manifest.llm.json", "vite.config.ts", "index.html", "src/web/", "src/assets/", "electron/", "tsconfig.electron.json" ], "policies": [ "DOM APIs are allowed in src/web/ and .web.ts shims only.", "Electron main/preload compile to dist/electron/.", "Vite emits renderer assets to dist/renderer/." ], "verify": [ "npm run lint", "npm run web:build", "npm run electron:build" ], "update": [ "manifest.llm.json when targets, build outputs, or dev commands change" ] }, "toolkit": { "description": "Toolkit screens, socket-gated browser tooling, JSON content sync, or Toolkit content panels.", "read": [ "llm.txt", "manifest.llm.json", "src/screens/ToolkitScreen.tsx", "src/navigation/routes.ts", "src/state/toolkitSocketStore.ts", "src/state/toolkitContentStore.ts", "src/socket/toolkitSocketClient.ts", "socket/" ], "policies": [ "Keep Toolkit browser-only unless explicitly requested.", "Keep Toolkit accessible only through Browser resolution and connected socket status.", "Use src/data JSON as canonical editable content until schemas stabilize.", "Do not add full CRUD forms unless explicitly requested." ], "verify": [ "npm run test -- --runInBand", "npm run dev:toolkit and check /health" ] }, "data-content": { "description": "Editable JSON content, typed data exports, and content validation.", "read": [ "llm.txt", "manifest.llm.json", "src/data/AGENTS.md", "src/data/", "socket/contentStore.cjs" ], "policies": [ "Keep JSON valid and typed through src/data exports.", "Add runtime validation for data imported by stores or socket helpers.", "Use atomic writes for server-side JSON save behavior." ], "verify": [ "npm run test -- --runInBand" ] }, "realtime": { "description": "Socket.IO server/client wiring for Toolkit connection status and content sync.", "read": [ "llm.txt", "manifest.llm.json", "socket/server.mjs", "socket/contentStore.cjs", "src/hooks/useToolkitSocketConnection.ts", "src/socket/toolkitSocketClient.ts", "src/state/toolkitSocketStore.ts", "src/state/toolkitContentStore.ts", "package.json" ], "policies": [ "Keep socket host/port configurable through environment variables.", "Broadcast content changes after valid reads or saves.", "Emit toolkit:content:error for read, parse, validation, or write failures." ], "verify": [ "npm run test -- --runInBand", "npm run dev:toolkit and check http://127.0.0.1:5174/health" ] }, "mobile": { "description": "React Native mobile target changes, Metro behavior, or native dependency setup.", "read": [ "llm.txt", "manifest.llm.json", "AGENTS.md", "metro.config.js", "index.js", "App.tsx" ], "policies": [ "Do not modify android/ or ios/ native files unless explicitly requested and those folders exist.", "Run pod install inside ios/ after native dependency changes when ios/ exists.", "Keep shared src/ free of browser-only APIs." ] }, "testing": { "description": "Jest tests for logic, stores, routes, socket helpers, or screen scaffolding.", "read": [ "llm.txt", "manifest.llm.json", "", "" ], "verify": [ "npm run test -- --runInBand" ] }, "docs": { "description": "README, AGENTS, llm.txt, manifest, docs, or wiki maintenance.", "read": [ "README.md", "AGENTS.md", "llm.txt", "manifest.llm.json", "package.json", "src/navigation/routes.ts" ], "verify": [ "node -e \"JSON.parse(require('fs').readFileSync('manifest.llm.json','utf8')); console.log('manifest ok')\"", "npm run lint" ] } }, "rootCommands": [ { "command": "npm run dev", "purpose": "Alias for browser development.", "target": "web" }, { "command": "npm run web", "purpose": "Vite dev server for browser iteration.", "target": "web" }, { "command": "npm run web:build", "purpose": "Production web build to dist/renderer/.", "target": "web" }, { "command": "npm run web:preview", "purpose": "Preview production web build.", "target": "web" }, { "command": "npm run dev:toolkit", "purpose": "Start local Toolkit Socket.IO server.", "target": "toolkit-socket" }, { "command": "npm run dev:all", "purpose": "Start browser app and Toolkit socket together.", "target": "web+toolkit-socket" }, { "command": "npm run dev:clear", "purpose": "Stop related local dev processes if a run hangs.", "target": "local-dev" }, { "command": "npm run electron:dev", "purpose": "Run Vite dev server and Electron concurrently.", "target": "electron" }, { "command": "npm run electron:build", "purpose": "Build renderer and compile Electron main/preload files.", "target": "electron" }, { "command": "npm run electron:package", "purpose": "Build and package desktop installer via electron-builder.", "target": "electron" }, { "command": "npm run start", "purpose": "Start Metro bundler.", "target": "mobile" }, { "command": "npm run android", "purpose": "Run on Android emulator or device when android/ exists.", "target": "android" }, { "command": "npm run ios", "purpose": "Run on iOS simulator or device when ios/ exists.", "target": "ios" }, { "command": "npm run lint", "purpose": "ESLint." }, { "command": "npm run test", "purpose": "Jest test suite." }, { "command": "npm run validate:commit", "purpose": "Pre-commit safety check: lint, Jest, and Electron build." }, { "command": "npm run version:bump-build", "purpose": "Increment the package patch version, update public .env build metadata, and stage package/env metadata before a commit." } ], "knownGaps": [ "docs/ is still a placeholder README stub.", "No JSON Schema validator exists yet for manifest.llm.json.", "Toolkit content panels are read-only scaffolds except for socket/store save plumbing.", "Toolkit placeholder JSON files are intentionally empty arrays until content schemas are requested.", "Native android/ and ios/ folders are not currently present in this checkout.", "No database exists yet; editable content is intentionally JSON-backed until schemas stabilize." ], "verificationBaseline": { "logicOrStoreChanges": [ "npm run test -- --runInBand" ], "uiChanges": [ "npm run lint", "npm run test -- --runInBand", "npm run web:build" ], "webElectronChanges": [ "npm run lint", "npm run test -- --runInBand", "npm run electron:build" ], "socketChanges": [ "npm run test -- --runInBand", "npm run dev:toolkit", "curl http://127.0.0.1:5174/health" ], "mobileChanges": [ "npm run lint", "npm run test -- --runInBand" ], "docsChanges": [ "node -e \"JSON.parse(require('fs').readFileSync('manifest.llm.json','utf8')); console.log('manifest ok')\"", "npm run lint" ] } }