# Machine Authoring Playbook This guide describes how a contributor adds a new machine to Mechanica without touching catalogue, navigation, or core viewer code. ## 1. Choose stable identifiers Use a lowercase kebab-case machine ID: ```ts id: 'radial-engine' ``` Part IDs must also be kebab-case and unique within the machine: ```ts parts: [ { id: 'crankcase', ... }, { id: 'master-rod', ... }, { id: 'cylinder-bank', ... }, ] ``` These IDs are part of shareable URLs, localStorage preferences, guided tours, animation bindings, and scene hit-testing. Do not rename an ID after release; add aliases in URL parsing if a rename is unavoidable. ## 2. Add data in the registry data layer Core machines live in: ```text src/modules/machines/data/coreMachines.ts ``` Stretch machines should be added in a sibling file when their milestone begins: ```text src/modules/machines/data/stretchMachines.ts ``` Then export them through `src/modules/machines/data/index.ts` and merge them in `src/modules/machines/registry.ts`. A complete machine entry includes: - `id`, `slug`, `title`, `subtitle`, `description` - `category`, `difficulty`, `complexity`, `releaseOrder` - `tags`, `keywords` - `facts` - `parts` - `guidedTour` - `related` - `animation` - `asset` - `learningObjectives` - `seo` ## 3. Define parts for every selectable component Every visible/selectable engineering component should have a part definition: ```ts part( 'sun-gear', 'Sun gear', 'Central external gear meshing with all planets.', 'rotating', [0, 0, 0.35], ) ``` Part definitions drive: - left sidebar component lists - hover labels - click selection detail drawer - opacity and visibility controls - exploded-view offsets - guided tour focus - animation target bindings - future GLB node mapping Guidelines: 1. Keep part IDs stable. 2. Use engineering names, not mesh names. 3. Put manufacturing or material details in `material` and `specs`. 4. Use `explode` as a semantic separation direction, not a final transform. 5. Mark non-physical overlays as `sensor` or `annotation` roles. ## 4. Add animation metadata first Even before the animation module is implemented, define the descriptor: ```ts animation: { modulePath: 'src/animations/machines/radialEngine.ts', defaultRpm: 900, minRpm: 200, maxRpm: 2400, cycleSeconds: 0.1333, cycleType: 'hybrid', loopMode: 'seamless', stepCount: 9, drivenParts: ['crankshaft', 'master-rod', 'piston-set'], notes: ['Master rod controls articulated rod motion.'], } ``` The animation module must later satisfy `MachineAnimationModule` from `src/animations/types.ts`. The core player will call only the standard controller methods: - `update(clock)` - `seek(cycleProgress)` - `step(direction)` - `reset()` - `dispose()` ## 5. Add scene implementation without coupling the viewer A machine scene adapter must satisfy `MachineSceneAdapter` from `src/three/sceneContracts.ts`. The adapter receives: - the machine definition - quality tier - display state - camera state - reduced-motion preference The adapter must not own global UI state. It should expose part bindings so the viewer can map pointer hits back to registry part IDs. ## 6. Prepare assets through the asset descriptor During early implementation, use: ```ts asset: { strategy: 'procedural', status: 'procedural-placeholder', ... } ``` When a production GLB is ready, update to: ```ts asset: { strategy: 'gltf', status: 'shipped', url: '/assets/machines/radial-engine/radial-engine.glb', draco: true, ktx2: true, ... } ``` Required asset handoff contents: ```text public/assets/machines// ├── .glb ├── manifest.json ├── preview.webp └── source/ ├── README.md └── attribution.md ``` The GLB node naming convention should include part IDs where practical: ```text part__crankshaft part__piston-set__instance_001 annotation__tdc-marker ``` ## 7. Add guided tour steps Guided tours are content, not imperative code. Each step should focus existing part IDs: ```ts { id: 'compression', title: 'Compression stroke', body: 'The piston rises with both valves closed, raising charge pressure and temperature.', focusPartIds: ['piston', 'intake-valve', 'exhaust-valve'], cameraPreset: 'front', durationSeconds: 6, } ``` Tour rules: 1. Focus one to four parts per step. 2. Keep captions under two short sentences. 3. Use camera presets instead of hardcoded camera vectors unless a custom camera move is required. 4. Pair tour steps with animation cues only when the animation module supports them. ## 8. Update related-machine links Related machines should form useful learning paths: - same principle, different implementation - upstream/downstream system relationship - simpler prerequisite mechanism - advanced follow-up machine Example: ```ts related: ['slider-crank', 'four-stroke-petrol-engine', 'piston-pump'] ``` ## 9. Validate registry integrity Run the validation utility during development or add it to a future test script: ```ts import { assertValidMachineRegistry } from './src/modules/machines/validation'; assertValidMachineRegistry(); ``` The validator checks: - duplicate machine IDs - invalid categories and difficulty values - invalid part opacity - duplicate part IDs - animation references to missing parts - guided tour references to missing parts - self-referential related links - shipped assets without URLs ## 10. Authoring review checklist Before opening a pull request: - [ ] Machine ID is stable, lowercase, and kebab-case. - [ ] Category and difficulty are from the schema constants. - [ ] At least four engineering facts are present. - [ ] Every important component has a part entry. - [ ] Part descriptions are educational, not placeholder text. - [ ] Guided tour focus IDs all exist. - [ ] Animation `drivenParts` all exist. - [ ] Related machine IDs are valid. - [ ] Asset descriptor accurately states procedural vs GLTF status. - [ ] New files do not import undeclared dependencies. - [ ] Catalogue UI works without changes to core components.