ThePirateRay/docs/research/rayforge-recipe-model-spike.md

112 lines
4.7 KiB
Markdown

# Rayforge material-recipe model spike
- Date: 2026-10-20
- Rayforge commit: `c28918c87f8273dd1d244f5a057addc192807fa5`
- Scope: offline recipe schema, matching, persistence and extension seams
- Transport: none
## Result
Rayforge already provides the main recipe mechanism needed by the MOPA
workbench. A recipe is a YAML-backed preset that can target a machine, laser
head, material, thickness range and operation type. Applied settings are
filtered through the selected step type's allowlist, and individual values can
be marked “leave unchanged”. This is a substantially better reuse point than a
second PirateRay recipe engine.
The pinned laser-step contract includes power, speed, frequency in Hz and pulse
width in ns. A synthetic recipe carrying those fields matched only when its
machine, material, thickness and step context were complete. It rejected a
wrong material and missing stock context. An unknown setting survived recipe
storage but was excluded when applying values to the synthetic step.
## Existing fit
The current model covers:
- stable recipe identity, name and description;
- machine and laser-head compatibility;
- material and thickness compatibility;
- operation/step-type compatibility;
- setting-level apply/leave-unchanged behavior;
- post-processing settings;
- specificity-based recipe selection;
- one user-editable YAML file per recipe;
- forward-compatible unknown metadata through `Recipe.extra`.
Rayforge materials and recipes are separate concepts. Materials describe stock
identity and appearance; recipes carry process settings and can refer to a
material UID. That separation fits the PirateRay requirement that recipes are
optional accelerators rather than mandatory material wizards.
## PirateRay gaps
The source does not expose native, validated fields for:
- evidence provenance;
- approval or validation status;
- source/profile revision;
- lens compatibility;
- correction-profile compatibility;
- named personal, makerspace-shared or project collections.
Machine and head matching use identifiers, but those identifiers alone do not
prove that a recipe is safe for a JPT source, mounted lens or correction file.
The existing `extra` round-trip can preserve namespaced PirateRay metadata,
but preservation is not validation and the stock Rayforge UI does not explain
or enforce those fields.
## Narrow implementation direction
Reuse `Recipe`, `RecipeManager`, the existing selector/editor and step
allowlists. The smallest extension should define a versioned, namespaced
PirateRay metadata block with explicit provenance and compatibility fields,
then validate it before a recipe can receive a stronger label than
“unverified”. Keep unknown or incomplete compatibility fail-closed for
encoding and physical execution.
Do not fork the recipe schema merely to add collections. First test whether
separate manager directories or a small library wrapper can provide personal
and makerspace-shared views while retaining Rayforge's file-per-recipe format.
Project-local portability remains a separate, unproven requirement.
The exact evidence required for “makerspace-approved” remains a product and
safety decision for Kaz. This spike deliberately does not invent that policy.
## Implemented offline metadata gate
The external no-device addon now contains a version-1 `pirateray` metadata
validator. It requires non-empty provenance and exact matches for machine
profile, source type, lens profile and correction-profile hash. Missing or
mismatched values are rejected independently.
The only currently accepted status is `unverified`. The tempting
`makerspace-approved` label is rejected until its evidence and review policy
has been decided. Even successfully validated metadata returns
`encoding_allowed: false` and `hardware_use_allowed: false`; this contract
organizes evidence but grants no controller capability.
The synthetic public fixture and regression tests exercise the contract
without using physical machine identifiers or settings.
## Reproduce
Run against the pinned checkout with the isolated Rayforge Python environment:
```powershell
python scripts/spike_rayforge_recipe_model.py <rayforge-checkout> `
--output tests/fixtures/rayforge-recipe-model.json
```
## Source anchors
- `rayforge/core/recipe.py`: schema, matching, allowlisting and `extra`
- `rayforge/core/recipe_manager.py`: YAML persistence and selection
- `rayforge/core/material.py`: material identity and appearance
- `rayforge/core/material_library.py`: material-library storage
- `rayforge/builtin_addons/rayforge-addon-laser/laser_essentials/steps/laser_step.py`:
laser recipe fields and Hz/ns contracts
- `rayforge/ui_gtk/doceditor/recipes/`: existing editor and selector
No USB enumeration, controller command, motion, physical framing, arming or
laser output occurred.