Close evidence phase and prepare external M1 build

This commit is contained in:
Codex 2026-10-24 13:50:56 +02:00
parent 11426e7969
commit 862d5a3cca
17 changed files with 167 additions and 126 deletions

1
.build/.gitkeep Normal file
View file

@ -0,0 +1 @@

3
.gitignore vendored
View file

@ -3,7 +3,8 @@ __pycache__/
.pytest_cache/
.venv/
.uv-cache/
/.build/
/.build/*
!/.build/.gitkeep
# Sensitive/raw acquisition data belongs in ThePirateRay-PrivateStash.
/raw/

View file

@ -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

View file

@ -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",

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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"

View file

@ -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",
],
}

View file

@ -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",

View file

@ -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",

View file

@ -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()