From 862d5a3cca59ba87ca3fb0b267427047b351ddc8 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 24 Oct 2026 13:50:56 +0200 Subject: [PATCH] Close evidence phase and prepare external M1 build --- .build/.gitkeep | 1 + .gitignore | 3 +- README.md | 24 +++--- assets/branding/generated/manifest.json | 16 ++-- docs/CODEX_HANDOFF.md | 35 +++++---- docs/adr/0002-reuse-rayforge-and-balormk.md | 28 +++---- .../lightburn-rayforge-ux-capability-map.md | 10 ++- .../lightburn-verified-projects-2026-10-24.md | 3 +- docs/research/m1-no-device-acceptance.md | 21 ++++- docs/research/machine-profile.yaml | 3 +- docs/roadmap.md | 77 +++++++++---------- docs/status/phase-0.md | 33 ++++---- scripts/build_rayforge_windows_checkpoint.ps1 | 27 ++++++- scripts/run_m1_no_device_acceptance.py | 2 +- scripts/validate_m3_hardware_protocol.py | 2 +- .../m3-hardware-protocol-template.json | 2 +- tests/test_m1_acceptance_pipeline.py | 6 +- 17 files changed, 167 insertions(+), 126 deletions(-) create mode 100644 .build/.gitkeep diff --git a/.build/.gitkeep b/.build/.gitkeep new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/.build/.gitkeep @@ -0,0 +1 @@ + diff --git a/.gitignore b/.gitignore index 738eda3..ae24c8f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,8 @@ __pycache__/ .pytest_cache/ .venv/ .uv-cache/ -/.build/ +/.build/* +!/.build/.gitkeep # Sensitive/raw acquisition data belongs in ThePirateRay-PrivateStash. /raw/ diff --git a/README.md b/README.md index 35c6976..6a10937 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The Pirate Ray is Makerspace Næstved's clean-room effort to build an open-source, safe desktop workflow for its laser equipment. The first target is the **JPT 100 W E2/M7 MOPA fiber laser** with a JCZ/EZCAD2-compatible -controller and the currently assumed 110 x 110 mm lens. +controller and the operator-confirmed 110 x 110 mm lens. The destination is the useful core of a LightBurn-like workflow: import or create geometry, assign operations and MOPA parameters, preview and validate @@ -36,8 +36,7 @@ Phase 0 has established a reproducible evidence and research baseline: - the public tests, provenance checks, leak guard, and clean-clone validation pass. -Phase 0 remains open while we collect machine photos and operator notes directly -from the actual control computer. The settings and device screenshots, bundle, +Phase 0 is complete as an evidence baseline. The settings and device screenshots, bundle, exported preferences, closed-application local settings and three explicitly operator-verified projects are preserved and analyzed. The operator confirmed a 110 mm lens; other local test projects remain unverified. See the @@ -75,23 +74,22 @@ We do not need to begin with a blank-sheet LightBurn clone: material recipes, simulation, pre-flight checks, packaging, and an addon architecture. Its job model already represents frequency and pulse width. -The leading route is therefore either to use and improve MeerK40t directly or -to build a Rayforge JCZ/Balor addon using the narrowest practical layer from -BalorMK. This remains a proposed decision until the packaged interaction and -real-project gates in -[ADR 0002](docs/adr/0002-reuse-rayforge-and-balormk.md) have passed. +The accepted route is a Rayforge JCZ addon with BalorMK retained as the pinned +behavioral oracle and reference boundary. Packaged interaction remains an M1 +release gate, not an architecture-decision gate; see +[ADR 0002](docs/adr/0002-reuse-rayforge-and-balormk.md). ## Next steps The initial offline architecture spikes have passed. The next work remains hardware-free: -1. collect exterior labels and startup/interlock notes; -2. obtain or produce a reviewed Windows Rayforge build artifact without +1. obtain or produce a reviewed Windows Rayforge build artifact without installing a toolchain through this repository; -3. run and record the packaged no-device desktop interaction checklist; -4. accept, revise, or reject the proposed reuse architecture from evidence; -5. complete and independently review the M3 hardware-protocol template before +2. run and record the packaged no-device desktop interaction checklist; +3. promote only evidence-backed vector MOPA imports through the existing + fail-closed review boundary; +4. complete and independently review the M3 hardware-protocol template before seeking separate approval for any live controller communication. An independent desktop application is the fallback only if both existing diff --git a/assets/branding/generated/manifest.json b/assets/branding/generated/manifest.json index ec87abe..c6158bc 100644 --- a/assets/branding/generated/manifest.json +++ b/assets/branding/generated/manifest.json @@ -46,13 +46,13 @@ }, { "path": "android/mipmap-anydpi-v26/ic_launcher.xml", - "sha256": "e071a8516287e0bacccc1a14a62502f3e1eda8360b2aad3c88c390ccbfa77122", - "bytes": 275 + "sha256": "32715ff646f6b9fef7c321138e4a14556c4a1f98a87b06070b56f41643dad718", + "bytes": 270 }, { "path": "android/mipmap-anydpi-v26/ic_launcher_round.xml", - "sha256": "e071a8516287e0bacccc1a14a62502f3e1eda8360b2aad3c88c390ccbfa77122", - "bytes": 275 + "sha256": "32715ff646f6b9fef7c321138e4a14556c4a1f98a87b06070b56f41643dad718", + "bytes": 270 }, { "path": "android/mipmap-hdpi/ic_launcher.png", @@ -136,8 +136,8 @@ }, { "path": "android/values/ic_launcher_background.xml", - "sha256": "1ff64d109f4173e84bf4d6ed9957b6f37f3456b0db3b19bb17d3a3c1de2687e4", - "bytes": 125 + "sha256": "b54bc246c11f5e12b46926eef269f08b67cf612cc8e9a9580f86d95820b1a2b1", + "bytes": 121 }, { "path": "ios/AppIcon.appiconset/Contents.json", @@ -980,8 +980,8 @@ }, { "path": "web/site.webmanifest", - "sha256": "ca7277912f7c8ccb373ca252c88050eecc217c355b05d5d6687517947812a32a", - "bytes": 699 + "sha256": "e56c1cff35765833a1f4d8d543fdf117d9c6b1572103122340ff037e1fedbac6", + "bytes": 666 }, { "path": "windows/app.ico", diff --git a/docs/CODEX_HANDOFF.md b/docs/CODEX_HANDOFF.md index 47e7359..bf5022e 100644 --- a/docs/CODEX_HANDOFF.md +++ b/docs/CODEX_HANDOFF.md @@ -1,7 +1,7 @@ # Codex continuation brief - Updated: 2026-10-24 -- Current focus: M1 build, packaging and no-device acceptance +- Current focus: produce and accept the packaged Windows M1 checkpoint - Hardware access: prohibited - Automatic toolchain installation: prohibited @@ -30,7 +30,7 @@ The repository now provides: - pinned upstream contract/license manifests and a drift checker; - a fail-closed API-23 adapter isolating Rayforge's internal driver registry; - a proposed MIT project-license decision in ADR 0003; Kaz has not accepted - it, so the repository remains unlicensed. + it, so the repository remains unlicensed; - a fail-closed M3 hardware-research protocol, incomplete public plan template and validator; validation can reach only `ready_for_review` and never authorizes hardware access. @@ -66,19 +66,20 @@ physically disconnected. LightBurn Pro 2.1.03 showed the JPT profile as `Disconnected`, the 110 mm material library, and explicit `Frequency (kHz)` / `Q-Pulse (ns)` UI labels. The live preferences, newest native backup, all five general Settings tabs and all three JPT Device Settings tabs are hashed in -PrivateStash. Exterior hardware photographs and startup/interlock notes remain -open; see `docs/research/lightburn-control-machine-observation-2026-10-24.md`. +PrivateStash. Exterior hardware photographs and startup/interlock notes are +deferred to M3 hardware readiness and do not block the offline product +checkpoint; see +`docs/research/lightburn-control-machine-observation-2026-10-24.md`. -M1 is not complete. Do not describe the repository as a finished desktop -application until both gates below have real evidence: +M1 is not yet a packaged desktop checkpoint. Only these two result-oriented +gates remain: 1. A Windows artifact for the pinned Rayforge revision must be produced on a reviewed build runner, verified without execution, and pass the manual no-device desktop checklist. -2. The known-working private JPT `.lbrn2` serialization evidence gate is met. - Offline mapping and disabled-output workflow staging now have synthetic - tests. Promotion still requires an independently established unit contract, - explicit resolution of absent pulse width and machine-context validation. +2. Promote only complete vector MOPA imports after an explicit unit contract, + absent-pulse-width policy and exact machine-context validation. The private + serialization-evidence gate itself is complete. Raw projects, screenshots containing identifiers, machine data and build binaries stay out of the public repository. @@ -105,11 +106,13 @@ order: `docs/research/m1-no-device-acceptance.md`; 4. `scripts/validate_m1_manual_acceptance.py`. -If no artifact is available, do not install a build environment here. The next -offline implementation task is evidence-backed MOPA import mapping with -synthetic tests; Kaz's explicit decision on ADR 0003 is also outstanding. +If no artifact is available, send the external-builder handoff in +`docs/research/m1-no-device-acceptance.md`; do not start another research +branch or install a build environment here. Kaz's explicit decision on ADR +0003 is also outstanding. The M3 protocol can also be reviewed offline, but its operation allowlist must stay empty until exact read-only request evidence is -available. Until the external gates and separate hardware approval arrive, -ADR 0002 remains Proposed and no controller backend, USB endpoint access, -physical framing, galvo motion or laser output is authorized. +available. ADR 0002 is accepted for the offline architecture. Until the +external gates and separate hardware approval arrive, no controller backend, +USB endpoint access, physical framing, galvo motion or laser output is +authorized. diff --git a/docs/adr/0002-reuse-rayforge-and-balormk.md b/docs/adr/0002-reuse-rayforge-and-balormk.md index 1fd8999..4e8e6fc 100644 --- a/docs/adr/0002-reuse-rayforge-and-balormk.md +++ b/docs/adr/0002-reuse-rayforge-and-balormk.md @@ -1,7 +1,8 @@ # ADR 0002: Prove reuse of Rayforge and BalorMK before building an app -- Status: Proposed +- Status: Accepted - Date: 2026-10-17 +- Accepted: 2026-10-24 ## Context @@ -20,7 +21,7 @@ Creating another editor, geometry stack and controller implementation would duplicate large, safety-relevant bodies of work and permanently increase the maintenance burden. -## Proposed decision +## Decision Do not begin independent desktop product implementation. First run three offline, version-pinned proofs: @@ -32,12 +33,11 @@ offline, version-pinned proofs: 3. translate one synthetic Rayforge operation stream to JCZ/Balor commands and compare it with a BalorMK mock trace. -If the proofs pass, prefer one of these outcomes in order: - -1. use and contribute to MeerK40t if its full application is adequate; -2. implement and upstream a Rayforge JCZ/Balor addon, adapting the narrowest - practical layer from BalorMK; -3. extract a small maintained controller library shared by either application. +The proofs pass at the offline architecture boundary. Build the product as a +Rayforge JCZ addon and retain pinned BalorMK behavior as the controller-level +oracle. Adapt only the narrowest necessary implementation after licensing and +attribution allow it. Packaged Windows interaction remains an M1 release gate, +not a reason to defer the host-architecture decision. An independent desktop application requires a later ADR showing concrete, tested gaps in both existing applications. @@ -121,14 +121,16 @@ Subsequent offline evidence: - [M1 no-device acceptance checkpoint](../research/m1-no-device-acceptance.md): the public regression suite and leak guard now form one transport-free gate over addon lifecycle, workflow, layers, recipes, encoding, negative cases, - cancellation/recovery and synthetic imports. The packaged Windows GUI and a - known-working private project remain external gates. + cancellation/recovery and synthetic imports. Packaged Windows interaction + remains a release gate. - [Upstream contract and license audit](../research/upstream-contract-and-license-audit.md): both pins and license notices are now machine-verifiable; the documented addon lifecycle is usable, but driver registration and cleanup still rely on an internal registry seam that should be replaced upstream. -These results satisfy the current model, buffering and no-device integration -proofs. They do not yet satisfy the packaged human-interaction or real-project -import obligations, so this ADR remains proposed. +Three operator-verified private projects subsequently established the +project-level `frequency` and `QPulseWidth` serialization needed to implement +the disabled-output import seam. Together, the evidence satisfies the host +architecture decision. It does not authorize hardware access or claim that the +packaged Windows interaction and import-promotion release gates have passed. diff --git a/docs/research/lightburn-rayforge-ux-capability-map.md b/docs/research/lightburn-rayforge-ux-capability-map.md index 4e69631..25a69b9 100644 --- a/docs/research/lightburn-rayforge-ux-capability-map.md +++ b/docs/research/lightburn-rayforge-ux-capability-map.md @@ -102,8 +102,9 @@ The PirateRay-specific work should remain narrow: ## Gaps that must not be papered over -1. A known-working private galvo `.lbrn2` is still required to establish - project-level `QPulseWidth` serialization and omission/default behavior. +1. Three known-working private galvo projects establish project-level + `QPulseWidth` serialization. Omission/default behavior still requires an + explicit policy before promotion to executable output. 2. The current Windows environment has not launched the pinned Rayforge GTK application; the headless UI contract is not a human interaction test. 3. `RequireFrameBeforeStart` is observed in a LightBurn device export and @@ -111,8 +112,9 @@ The PirateRay-specific work should remain narrow: framing behavior. 4. Machine-specific frequency, pulse-width, timing and correction limits are not established by LightBurn documentation or exported recipe values. -5. Material recipes lack a proven contract for provenance, approval and - machine/lens/correction compatibility. +5. PirateRay recipe metadata now supplies fail-closed provenance, approval and + machine/lens/correction compatibility checks; packaged UI interaction is + still part of the M1 desktop checklist. 6. The HTML prototype named by the handoff is absent from the active checkout; its historical controls must not be treated as implemented capability. diff --git a/docs/research/lightburn-verified-projects-2026-10-24.md b/docs/research/lightburn-verified-projects-2026-10-24.md index 1ee2b0d..6ccdb7d 100644 --- a/docs/research/lightburn-verified-projects-2026-10-24.md +++ b/docs/research/lightburn-verified-projects-2026-10-24.md @@ -55,4 +55,5 @@ test explicit unit conversions, field absence and invalid values. It must not copy private project geometry into public fixtures or enable hardware access. The reviewed Windows artifact and manual no-device desktop acceptance remain -open. ADR 0002 remains Proposed until those gates have also passed. +open as M1 release gates. ADR 0002 has accepted the Rayforge-addon architecture +independently of those packaging gates. diff --git a/docs/research/m1-no-device-acceptance.md b/docs/research/m1-no-device-acceptance.md index 4c8d955..d28c7c9 100644 --- a/docs/research/m1-no-device-acceptance.md +++ b/docs/research/m1-no-device-acceptance.md @@ -38,6 +38,17 @@ runtime is unavailable, it stops and reports the missing prerequisite. Any toolchain preparation remains a separate, explicitly invoked upstream action or should happen on a dedicated build runner. +### External builder handoff + +The external builder needs only a clean checkout of this repository, Windows, +and a reviewed Rayforge-compatible MSYS2/UCRT64 runtime. No laser or controller +may be connected. Run `scripts/build_rayforge_windows_checkpoint.ps1` from the +repository root with a clean checkout and an absent or empty artifact path. A +successful run leaves one transferable directory, +`.build/artifacts/`, containing exactly the portable ZIP, installer, Rayforge +license sidecar and `rayforge-windows-checkpoint.json`. Transfer that directory +as a unit; the receiving side verifies it before executing either binary. + Before staging, the script rejects transport imports in the addon. The build entry point also rejects tracked Rayforge modifications and any unexpected untracked source. It removes and recreates only the validated addon staging @@ -150,10 +161,12 @@ hardware, non-isolated app data, changed evidence, malformed review or any stop condition. Final `passed` requires a non-empty reviewer name and an ISO-8601 review timestamp with timezone. No status authorizes hardware access. -## Remaining external gates +## Remaining release gates -- A known-working private `.lbrn2` is still required before implementing - project-level `QPulseWidth` mapping. - Human interaction results from the packaged build must be recorded before - ADR 0002 can be accepted. + M1 can be called a packaged desktop checkpoint. +- The three known-working private projects establish serialized MOPA fields. + Promotion from disabled staging still requires the explicit unit contract, + absent-pulse-width policy and exact machine-context validation already + enforced by preflight. - Physical controller behavior remains outside M1. diff --git a/docs/research/machine-profile.yaml b/docs/research/machine-profile.yaml index 90197da..7d3327d 100644 --- a/docs/research/machine-profile.yaml +++ b/docs/research/machine-profile.yaml @@ -14,7 +14,7 @@ optics: active_field_mm: width: 110 height: 110 - status: assumed-pending-physical-verification + status: operator-confirmed-on-control-machine other_configurations_observed_mm: - width: 300 height: 300 @@ -63,3 +63,4 @@ notes: - USB revision 0005 is a descriptor value, not a proven board or firmware version. - LightBurn bundle manifests establish version 2.1.03 but do not expose a separate build identifier. - Exported recipe values are observations, not certified hardware limits or safe operating parameters. + - The operator confirmed the installed 110 mm lens during the disconnected control-machine inspection on 2026-10-24. diff --git a/docs/roadmap.md b/docs/roadmap.md index 4f27ed1..b5d6b08 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1,6 +1,6 @@ # Product and research roadmap -Last updated: 2026-10-21 +Last updated: 2026-10-24 ## Destination @@ -65,8 +65,7 @@ It does **not** yet establish: - which values are controller settings versus laser-source settings; - status, interlock, error, abort, and recovery behavior; - whether an apparently harmless query can move a galvo or arm emission; -- how a known-working `.lbrn2` project serializes JPT frequency and - `QPulseWidth` settings. +- the effective pulse-width default for settings that omit `QPulseWidth`. The later LightBurn export collection now establishes version `2.1.03`, a 110 x 110 mm `JCZFiber`/`LibUSB` JPT profile, and the `QPulseWidth` device @@ -78,7 +77,7 @@ would be premature and would make the safety boundary impossible to audit. ## Milestones -### M0 — Evidence baseline (current) +### M0 — Evidence baseline (complete) Preserve and hash the installer snapshot, publish sanitized findings, and keep public/private provenance verifiable. Complete M0 with one local collection @@ -90,7 +89,7 @@ Exit criteria: - exact LightBurn version, bundle, preferences, screenshots, and working project examples are present in PrivateStash; -- mounted lens, startup, interlock, and USB device identity are confirmed; +- mounted lens and USB device identity are confirmed; - sanitized metadata and new analysis are committed publicly; - both repositories pass their clean-clone validation. @@ -100,7 +99,7 @@ Ecosystem research found that Rayforge already implements most of the desired application and MeerK40t BalorMK already implements the relevant controller. Validate those assets before defining competing application architecture. See the [open-source landscape](research/open-source-landscape.md) and -[proposed ADR 0002](adr/0002-reuse-rayforge-and-balormk.md). +[accepted ADR 0002](adr/0002-reuse-rayforge-and-balormk.md). The first work is entirely offline: @@ -150,24 +149,21 @@ width, and unsupported Ops. Hardware-specific upper limits remain unknown and are deliberately not guessed. [The transport-free JCZ record spike](research/rayforge-jcz-packet-spike.md) -packs the same operation stream into deterministic little-endian 12-byte -records whose opcodes, values, coordinates and distances match the pinned -BalorMK mock evidence. It deliberately omits list buffering and all transport; -controller-safe state ordering remains an explicit evidence gap. +now extends the deterministic 12-byte records with fixed-size list buffering +and explicit state-before-motion ordering. Upload, execution and physical +transport remain outside M1. [The synthetic LightBurn MOPA import spike](research/rayforge-lightburn-mopa-import-spike.md) -confirms that Rayforge preserves geometry, power, speed and passes and retains -an observed LightBurn frequency value in raw source metadata. Frequency is not -mapped into workflow settings. A later real device export identifies -`QPulseWidth` and its nanosecond UI contract, but project serialization remains -unknown, so the job still deliberately fails the JCZ encoding gate. A private -known-working `.lbrn2` fixture is required before implementing the missing -MOPA mapping. +confirms that Rayforge preserves geometry, power, speed and passes. Three later +operator-verified projects establish project-level `frequency` and +`QPulseWidth` fields. The addon now stages complete vector settings into +disabled Rayforge steps and rejects them at preflight until units, defaults and +machine context are explicitly approved. -The architecture decision remains proposed until the packaged Windows -interaction and known-working private-project gates produce evidence. An -independent app requires a documented gap analysis showing why neither -existing platform can be extended responsibly. +ADR 0002 accepts the Rayforge-addon architecture with BalorMK as the pinned +behavioral oracle. Packaged Windows interaction is a release gate, not an open +architecture question. An independent app now requires a later ADR with +concrete evidence that this accepted route cannot meet product needs. Exit criteria: a representative JPT job survives import/planning, MOPA parameters and units, validation and deterministic mock encoding; the chosen @@ -266,26 +262,24 @@ Bodor BCL503MU. Each controller family gets its own evidence, protocol, capability, and hardware-validation track. A successful JPT backend does not imply compatible safety or control behavior on either CO2 machine. -## Immediate next work package +## Current completion track -The next work package should be small enough to review in a few commits: +The exploratory work package is complete: verified projects, device evidence, +profile import, list buffering, cancellation/recovery, workflow modeling and +the M3 protocol draft are all present. Work now converges on two deliverables: -1. collect two or three known-working `.lbrn2` projects plus the remaining - device screenshots, preferences folder, photos and operator notes; -2. add a sanitized synthetic JCZ `.lbdev` fixture and characterize the exact - gaps in Rayforge's device importer; -3. implement and test the smallest evidence-backed JCZ profile-import seam, - keeping project `QPulseWidth` mapping gated until a real project confirms - its serialization; -4. extend the offline JCZ encoder from semantic records to controller-list - buffering and explicit safe state ordering, still without transport; -5. exercise cancellation/recovery and the graphical workflow against mock - backends, then accept, amend or reject ADR 0002; -6. complete and independently review the written M3 hardware-research protocol - before seeking separate approval for any USB endpoint access or controller - command. +1. produce the pinned Windows Rayforge checkpoint on a reviewed external build + runner, verify the transferred artifact set and complete the no-device + desktop interaction checklist; +2. promote complete vector MOPA imports only after recording the explicit unit + contract, absent-pulse-width policy and exact machine-context validation. -The draft, template, validator and regression tests for item 6 are present. +Exterior photographs, additional screenshots and speculative protocol research +are not M1 blockers. Hardware work remains separately gated by M3. + +### Completed implementation record + +The M3 draft, template, validator and regression tests are present. Hardware work still cannot proceed until the exact read-only operation evidence, driver/rollback plan, physical controls, distinct assigned roles and separate human approval are all in place. @@ -299,8 +293,8 @@ profile model. The fixture and regression test remain transport-free. Item 3 now has a minimal external-addon seam: it recognizes only the synthetic `JCZFiber` fixture with explicit provenance and frame-before-start, binds it to the no-device JCZ driver, preserves process values as raw observations with -unit uncertainty, and keeps encoding disabled until the remaining profile and -project evidence exists. +unit uncertainty, and keeps encoding disabled until the unit/default and exact +machine-context review passes. Item 4 now covers transport-free fixed-size list buffering and an explicit state-before-motion order derived from pinned BalorMK. Port commands, upload, @@ -318,7 +312,8 @@ generated settings UI exposes frequency and pulse width with Hz/ns contracts, the values survive step serialization, and the preview artifact is populated without machine code or transport. This is a headless UI-contract and pipeline proof, not a completed human GTK test. Packaged Windows interaction with the -external addon remains open before item 5 or ADR 0002 can close. +external addon remains the final desktop release gate; ADR 0002 has accepted +the architecture independently of it. The first Windows launch preflight is recorded separately. It fails closed because the available spike venv has no PyGObject `gi` module and this machine diff --git a/docs/status/phase-0.md b/docs/status/phase-0.md index f121f2d..4dcf215 100644 --- a/docs/status/phase-0.md +++ b/docs/status/phase-0.md @@ -1,4 +1,4 @@ -# Phase 0 status +# Phase 0 status — complete Last updated: 2026-10-24 @@ -43,9 +43,10 @@ Last updated: 2026-10-24 and preserved privately. They confirm the 110 x 110 mm field, Q-pulse-width enablement, visible 1-4000 kHz range and require-frame-before-start setting. -## Pending external acquisition +## Deferred hardware-readiness evidence -These items remain pending from the actual control machine: +These items are still useful before hardware work, but no longer block the +completed Phase 0 evidence baseline or the offline M1 product checkpoint: - safe exterior hardware/label photographs and startup/interlock notes; - stock-driver identity and version from the actual LightBurn control computer. @@ -58,13 +59,8 @@ Collect them directly into a dated directory under PrivateStash by following the [LightBurn checklist](../collection/lightburn.md). Do not use Google Drive as an intermediate project workspace and do not generate Support Data. -Phase 0 remains open until the safety- and interoperability-relevant remainder -is hashed, committed, pushed, and represented by sanitized public evidence. - -The work that does not depend on this acquisition can proceed in parallel. -The [product and research roadmap](../roadmap.md) defines the offline application -core, configuration analysis, protocol research, gated hardware experiments, -and workshop release as separate milestones. +Collect them when preparing an approved M3 hardware session. Do not expand the +offline product scope merely to keep Phase 0 open. ## Offline M1 status @@ -112,17 +108,20 @@ evidence for: commit, reviewer, bounded session and explicit safety statements while still requiring live preflight and returning no automatic hardware authorization. -The remaining M1 gates are deliberately explicit: +The remaining M1 release gates are deliberately narrow: -- a private known-working `.lbrn2` project is still required before mapping - project-level `QPulseWidth` into workflow settings; - packaged Windows/GTK interaction is blocked until a reviewed Rayforge runtime with PyGObject and MSYS2/UCRT64 is available; the repository build entry point fetches pinned source but never installs that runtime; -- graphical workflow, import, lens correction and controller protocol - behavior remain unproven at the physical boundary; -- ADR 0002 remains **Proposed** until those gaps are resolved or rejected by - evidence. +- three operator-verified projects establish project-level `frequency` and + `QPulseWidth` serialization, and the offline bridge stages complete vector + settings with output disabled; promotion still requires an explicit unit + contract, absent-pulse-width policy and exact machine-context validation; +- graphical workflow, lens correction and controller behavior remain unproven + at the physical boundary and belong to later gated milestones. + +ADR 0002 is accepted for the offline product architecture. These release gates +do not reopen that decision. No item in this section authorizes USB access, controller commands or laser output. diff --git a/scripts/build_rayforge_windows_checkpoint.ps1 b/scripts/build_rayforge_windows_checkpoint.ps1 index 8a2300a..ba8d1b8 100644 --- a/scripts/build_rayforge_windows_checkpoint.ps1 +++ b/scripts/build_rayforge_windows_checkpoint.ps1 @@ -19,6 +19,22 @@ if (-not $ArtifactRoot) { $ArtifactRoot = Join-Path $repositoryRoot ".build\artifacts" } +$repositoryStatus = @( + & git -C $repositoryRoot status --porcelain=v1 --untracked-files=all +) +if ($LASTEXITCODE -ne 0) { + throw "Could not inspect The Pirate Ray checkout state" +} +if ($repositoryStatus.Count -gt 0) { + throw "The Pirate Ray checkout must be clean before an external checkpoint build" +} +if ( + (Test-Path -LiteralPath $ArtifactRoot) -and + @(Get-ChildItem -LiteralPath $ArtifactRoot -Force).Count -gt 0 +) { + throw "Artifact root must be absent or empty: $ArtifactRoot" +} + & (Join-Path $PSScriptRoot "prepare_rayforge_build.ps1") ` -CacheRoot $CacheRoot ` -AllowStagedPirateRayAddon @@ -87,6 +103,10 @@ if (-not (Test-Path -LiteralPath $installer)) { } New-Item -ItemType Directory -Force -Path $ArtifactRoot | Out-Null +$artifactInstaller = Join-Path $ArtifactRoot (Split-Path -Leaf $installer) +if ($PSCmdlet.ShouldProcess($artifactInstaller, "Copy installer into checkpoint")) { + Copy-Item -LiteralPath $installer -Destination $artifactInstaller -Force +} $licenseDirectory = Join-Path $bundleDirectory "THIRD_PARTY_LICENSES" $rayforgeLicense = Join-Path $checkout "LICENSE" $normalizedLicense = [IO.File]::ReadAllText($rayforgeLicense).Replace("`r`n", "`n").Replace("`r", "`n") @@ -134,9 +154,9 @@ $result = [ordered]@{ bytes = (Get-Item -LiteralPath $archive).Length } installer = [ordered]@{ - path = Split-Path -Leaf $installer - sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $installer).Hash.ToLowerInvariant() - bytes = (Get-Item -LiteralPath $installer).Length + path = Split-Path -Leaf $artifactInstaller + sha256 = (Get-FileHash -Algorithm SHA256 -LiteralPath $artifactInstaller).Hash.ToLowerInvariant() + bytes = (Get-Item -LiteralPath $artifactInstaller).Length } license_files = @( [ordered]@{ @@ -152,3 +172,4 @@ $result = [ordered]@{ $manifestPath = Join-Path $ArtifactRoot "rayforge-windows-checkpoint.json" $result | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $manifestPath -Encoding utf8 Write-Output "Checkpoint manifest: $manifestPath" +Write-Output "Transfer the complete artifact directory: $ArtifactRoot" diff --git a/scripts/run_m1_no_device_acceptance.py b/scripts/run_m1_no_device_acceptance.py index 9d3d1ed..f808a49 100644 --- a/scripts/run_m1_no_device_acceptance.py +++ b/scripts/run_m1_no_device_acceptance.py @@ -131,7 +131,7 @@ def run(source: Path) -> dict: "evidence": evidence, "remaining_manual_gates": [ "packaged Windows GUI interaction", - "known-working private LightBurn project import", + "reviewed promotion of imported MOPA settings", ], } diff --git a/scripts/validate_m3_hardware_protocol.py b/scripts/validate_m3_hardware_protocol.py index e218437..9a0c50e 100644 --- a/scripts/validate_m3_hardware_protocol.py +++ b/scripts/validate_m3_hardware_protocol.py @@ -17,7 +17,7 @@ EXPECTED_TARGET = { "usb_pid": "9899", "device_description": "USBLMCV4", "lens_field_mm": 110, - "lens_status": "assumed-pending-physical-verification", + "lens_status": "operator-confirmed-on-control-machine", } PROHIBITED_CAPABILITIES = { "persistent_write", diff --git a/tests/fixtures/m3-hardware-protocol-template.json b/tests/fixtures/m3-hardware-protocol-template.json index d044931..6b0b06a 100644 --- a/tests/fixtures/m3-hardware-protocol-template.json +++ b/tests/fixtures/m3-hardware-protocol-template.json @@ -9,7 +9,7 @@ "usb_pid": "9899", "device_description": "USBLMCV4", "lens_field_mm": 110, - "lens_status": "assumed-pending-physical-verification" + "lens_status": "operator-confirmed-on-control-machine" }, "scope": { "milestone": "M3", diff --git a/tests/test_m1_acceptance_pipeline.py b/tests/test_m1_acceptance_pipeline.py index f088803..808d1df 100644 --- a/tests/test_m1_acceptance_pipeline.py +++ b/tests/test_m1_acceptance_pipeline.py @@ -32,7 +32,7 @@ class M1AcceptancePipelineTests(unittest.TestCase): source = ACCEPTANCE.read_text(encoding="utf-8") self.assertIn(manifest["commit"], source) self.assertIn("packaged Windows GUI interaction", source) - self.assertIn("known-working private LightBurn project import", source) + self.assertIn("reviewed promotion of imported MOPA settings", source) self.assertIn('"-m", "unittest", "discover"', source) self.assertIn("scripts/check_public_repo.py", source) self.assertIn("timeout=120", source) @@ -50,6 +50,10 @@ class M1AcceptancePipelineTests(unittest.TestCase): self.assertIn("GetFullPath", source) self.assertIn("Remove-Item -LiteralPath $resolvedAddonDestination", source) self.assertIn("AllowStagedPirateRayAddon", source) + self.assertIn("Copy installer into checkpoint", source) + self.assertIn("-LiteralPath $artifactInstaller", source) + self.assertIn("checkout must be clean", source) + self.assertIn("Artifact root must be absent or empty", source) def test_acceptance_has_no_hardware_commands(self): source = ACCEPTANCE.read_text(encoding="utf-8").lower()