mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 08:41:13 -07:00
Normalize line endings to LF
.gitattributes sets text=auto with eol=lf, so every text file is stored and checked out with LF, and a patch keeps its bytes. The files that carried CRLF from a Windows editor are renormalized. No content changes.
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
# Every text file is stored and checked out with LF; a patch keeps its bytes.
|
||||
* text=auto eol=lf
|
||||
*.patch -text
|
||||
*.diff -text
|
||||
@@ -1,398 +1,398 @@
|
||||
# AGENTS.md
|
||||
|
||||
Instructions for AI coding agents that work in this repository. A human
|
||||
contributor is welcome to read it too: it is the same set of rules.
|
||||
|
||||
## ForgeFIRM repository rules
|
||||
|
||||
### Safety on the machine
|
||||
|
||||
- The laser never fires with the coolant pump deliberately off. The heater-only
|
||||
flow-calibrate and flow-verify tools are the only pump-off state, and the
|
||||
tube stays dark during them.
|
||||
- The 40 V motor rail stays up while the machine is on. Never cycle it for a
|
||||
controller handover. A rail glitch can leave the stepper drivers
|
||||
unserviceable.
|
||||
|
||||
### The iteration loop
|
||||
|
||||
- Edit, cross-build in the Yocto build environment, hot-deploy to the
|
||||
machine, prove. Then one commit, one push, one pin bump for the proven
|
||||
change.
|
||||
- A full image build happens once, at the end of a work item, never per fix.
|
||||
- Kernel and BSP changes ride one image flash, batched. Validate a `.ko` or
|
||||
overlay change on the image that ships it. Never hot-swap a `.ko` onto a
|
||||
board that is about to be reflashed.
|
||||
|
||||
### Acceptance coverage
|
||||
|
||||
- Evaluate every change against the acceptance catalog in
|
||||
`forgetest/forgetest/suite/`. Does an existing test exercise the changed
|
||||
behavior? Does its `covers` map name the files you touched? If not, add or
|
||||
widen one in the same change. The coverage lint
|
||||
(`python3 -m forgetest.coverage --enforce`) is the floor.
|
||||
- The `SRCREV` of a component and the `PV` that moves with it live in
|
||||
`<recipe>-pin.inc` next to the recipe. Nothing else goes in that file. The
|
||||
image manifest keeps `*-pin.inc` out of the layer content hash. Thus a pin
|
||||
bump invalidates only the acceptance tests that cover that component. A
|
||||
pin in a recipe body counts as a platform change and needs a full
|
||||
acceptance campaign.
|
||||
- Test a harness change yourself, on the host, before you hand it to the
|
||||
operator for a bench run. A harness that fails on its own defect costs the
|
||||
operator a full re-run cycle.
|
||||
- A test declares its `mode`, and the runner switches. Cloud tests stay in
|
||||
cloud mode. Keep `requires` lists minimal, and never gate one test behind a
|
||||
chain of others.
|
||||
- Never exercise a gate or a limit through the `GFCOOL_*` environment
|
||||
overrides in a test. Use the settings API, and restore it in the teardown.
|
||||
|
||||
### CI couplings that set the push order
|
||||
|
||||
- The CI of `grblHAL-glowforge` gets the laser harnesses from `scripts/bench/`
|
||||
at the head of `master`, unpinned. Push a harness change here before the
|
||||
driver change that needs it.
|
||||
- The acceptance page shares its theme and its vendored Bootstrap with the
|
||||
`forgectrl` panel, byte for byte. Land a UI change in `forgectrl` first.
|
||||
Push it and pin it. Only then push this repository.
|
||||
- The CI of `kernel-module-glowforge` cross-builds against the BSP from
|
||||
`meta-openglow`. Push `meta-openglow` before a module change that needs a
|
||||
BSP change.
|
||||
|
||||
### Builds and images
|
||||
|
||||
- Builds run only in the dedicated Yocto build environment that the Build page
|
||||
of the site defines (kas and bitbake). Never use a native host build of a
|
||||
component as proof, not even for a compile check.
|
||||
- Every build makes both images: `forgefirm-image` (the release image) and
|
||||
`forgefirm-image-dev` (the bench image, with `forgetest` on port 8090). The
|
||||
dev image needs an explicit `--target`. Report both artifact names.
|
||||
- After a kernel-module pin bump, rebuild the kernel and the module in the
|
||||
same run: `bitbake -c cleansstate linux-fslc kernel-module-glowforge`, then
|
||||
the images. The kernel version suffix is not reproducible across
|
||||
re-checkouts, and a rootfs whose kernel and module disagree fails.
|
||||
- Read the layer HEAD lines at the top of a build log before you trust a build
|
||||
that moves a pin.
|
||||
|
||||
### The record of the work
|
||||
|
||||
This repository carries no status document and no dated log. Both were
|
||||
retired: every fact about the machine and the firmware is on the
|
||||
documentation site, and **the record of a piece of work is the commit message
|
||||
that carries it**: what was done, how it was proven, and what it replaced, in
|
||||
the same place as the change itself.
|
||||
|
||||
A measurement session or a bench drill is written up in the commit that
|
||||
lands its result. Nothing accumulates in a file.
|
||||
|
||||
### On the bench
|
||||
|
||||
These rules apply whenever an agent works with the operator at a machine.
|
||||
|
||||
- **One armed run per turn.** Start the run, let it finish, stop, report what
|
||||
happened against what was expected, and wait for the operator's
|
||||
confirmation before any further armed run or machine command. Never chain a
|
||||
jog, a fix, or a re-run onto a burn in the same step. Dry runs (latch
|
||||
locked, no emission) can be batched. Say which is which before you start.
|
||||
- **Zero inference during live fire.** Report only what the combined
|
||||
observations confirm: the operator's eyes, the drill output, the logs, the
|
||||
sysfs readbacks. Everything else is labeled "unconfirmed" and is not acted
|
||||
on.
|
||||
- **One Grbl connection.** Never open a second connection to TCP port 23 while
|
||||
a drill, an acceptance test, or a sender holds one. The last connection
|
||||
wins, the displaced client's cleanup never arrives, and its queued commands
|
||||
wait behind the arm gate. Read state through `forgectrl` (HTTP) and sysfs
|
||||
only. After a displaced or aborted drill: soft reset, confirm that the RX
|
||||
buffer reads full, `M5`, confirm that `armed` is false, before anyone
|
||||
touches the button.
|
||||
- **The operator touches the machine.** The operator runs the install, the
|
||||
service restart, and the reflash on the machine. Never modify the software
|
||||
of a running machine unless the operator asks for that exact step.
|
||||
- **Bench hygiene.** Stage test files in `/tmp`. A file that must survive a
|
||||
reboot goes in `/data/bench-scratch/`, and that directory is deleted whole
|
||||
at the end of the session. Nothing goes loose in `/data`. Remove everything
|
||||
you put on the board, in the same session, and list `/data` before you end.
|
||||
A bench tool worth a second use goes in `scripts/bench/`. The dev image
|
||||
installs those tools under `/usr/share/forgetest/bench/`. Run them from
|
||||
there.
|
||||
|
||||
## Project-wide rules
|
||||
|
||||
The sections above are specific to this repository. The rules below apply to
|
||||
every repository of the OpenGlow ForgeFIRM project: the firmware components,
|
||||
the build base, the documentation, and the hardware designs. Where a
|
||||
repository rule and a rule below conflict, the safety, proof, and hygiene
|
||||
rules below win. The documentation site, <https://docs.forgefirm.org/>, is
|
||||
the source of truth for every fact about the machine and the firmware. Read
|
||||
its Developers section before you change code.
|
||||
|
||||
### What ForgeFIRM is
|
||||
|
||||
ForgeFIRM is open firmware for stock Glowforge lasers (Basic, Plus, Pro). It
|
||||
runs on the factory NXP i.MX6 control board with no hardware modification. It
|
||||
replaces the cloud-dependent factory userspace with an open Linux image that
|
||||
controls the machine locally. In GRBL mode the machine is a standard grblHAL
|
||||
controller for LightBurn and other senders on TCP port 23. Cloud mode runs the
|
||||
factory experience through the Glowforge web service, and it is a deliberate,
|
||||
maintained feature. The stack is hardware validated: GRBL mode cuts real jobs,
|
||||
and cloud mode runs end to end.
|
||||
|
||||
One controller runs at a time under `forgectrl`, the machine-services daemon,
|
||||
which owns the pulse device, the cooling engine, the cameras, and the control
|
||||
panel. The kernel module plays the pulse stream into the stepper drivers and
|
||||
owns the laser latch and the safety readbacks. The Technical section of the
|
||||
site describes the whole stack.
|
||||
|
||||
### The repositories
|
||||
|
||||
All repositories live under the OpenGlow organization on GitHub,
|
||||
<https://github.com/openglow-org>. For development, check every repository out
|
||||
as a sibling in one base directory named `openglow-forgefirm`. The build
|
||||
scripts expect that layout. Each repository is its own git repository. The
|
||||
base directory is not.
|
||||
|
||||
| Repository | Role | Default branch | License |
|
||||
|---|---|---|---|
|
||||
| `forgefirm` | The build and release base: the `meta-forgefirm` Yocto layer, the kas configuration, the image recipes, the install and release scripts, the acceptance tool `forgetest/`, the bench tools `scripts/bench/`, and the bench actuator firmware `fixture/`. It carries no project documentation. | `master` | MIT (layer metadata) |
|
||||
| `meta-openglow` | The BSP layers `meta-glowforge-bsp` and `meta-openglow-core`: kernel, device tree, U-Boot, board recipes. | `scarthgap` | MIT (layer metadata) |
|
||||
| `forgectrl` | The machine-services daemon (C) on HTTPS port 443, with the read-only routes on HTTP port 80: supervisor, pulse-device broker, motion-liveness gate, cooling engine, cameras, telemetry, settings, diagnostics, control panel, A/B updates. | `main` | MIT |
|
||||
| `grblHAL-glowforge` | The grblHAL driver for the stock board, the GRBL-mode controller. The core is a submodule at `src/grbl`. Machine constants are in `src/boards/glowforge.h`. | `main` | GPL-3.0-or-later |
|
||||
| `grblHAL-core` | The fork of the grblHAL core that the driver uses. Minimum change, no rationale comments. | `forgefirm` | The upstream grblHAL license |
|
||||
| `kernel-module-glowforge` | `glowforge.ko`: the SDMA + EPIT pulse engine, the laser latch, the safety readbacks, the sensors. | `master` | GPL-2.0-or-later |
|
||||
| `python3-gfhardware` | The `gfhardware` hardware library and the cloud-mode applications in `forgefirm-app/`. | `master` | MIT, with one LGPL-2.1-or-later component |
|
||||
| `Glowforge-Utilities` | `gfutilities` on PyPI: the factory protocol and service layer that cloud mode uses. | `master` | MIT |
|
||||
| `forgefirm-docs` | The documentation site. | `main` | CC BY-SA 4.0 |
|
||||
| `openglow-serial-adapter` | The USB-C serial-console adapter for the control board. | `main` | CC BY-NC-SA 4.0 |
|
||||
|
||||
The full table, the license details, and the component diagram are on the
|
||||
site: <https://docs.forgefirm.org/developers/>.
|
||||
|
||||
### Safety first, and in that order
|
||||
|
||||
The machine is a laser, and the operator is the safety authority at it. These
|
||||
rules bind every change, in every repository, that can affect what the machine
|
||||
does.
|
||||
|
||||
- The hardware chain is the safety boundary. Software only adds gates on top
|
||||
of it. `LASER_ON` is never a bare GPIO. The kernel laser latch is locked by
|
||||
default, and every close of the pulse device locks it again.
|
||||
- Never defeat a switch, an interlock, or a readback, in hardware or in
|
||||
software, not even for a test.
|
||||
- Never change a fire gate, a thermal limit, or a safety default to make a
|
||||
test pass.
|
||||
- The order of work is absolute: emission and motion first, robustness and
|
||||
hygiene after. Review a change that touches emission or motion for that
|
||||
before anything else.
|
||||
- A change that can put energy where it was not commanded gets a regression
|
||||
test with the fix, in the same commit, never after.
|
||||
- Position counters, homing anchors, and a homed flag are not proof of
|
||||
physical motion. The head accelerometer is, and so are the operator's eyes.
|
||||
|
||||
### Proof before done
|
||||
|
||||
A feature or fix is not complete until it is proven. The order of preference:
|
||||
|
||||
1. A host test that runs in CI. The Test page of the site lists the host tests
|
||||
of each repository.
|
||||
2. A bench drill on the bench reference, recorded in the commit message that
|
||||
carries the change.
|
||||
3. Documented reasoning.
|
||||
|
||||
Also:
|
||||
|
||||
- Run the host tests of this repository before every commit. Where the build
|
||||
uses `-Werror`, a warning is a failure.
|
||||
- A component that ships in the image is also evaluated against the release
|
||||
acceptance catalog in the `forgefirm` repository (Test page, "Coverage
|
||||
currency"). A behavior change with no catalog consequence gets a sentence
|
||||
of justification in the commit message.
|
||||
- For a component that ships in the image, the proof build is the Yocto
|
||||
cross-build that the Build page of the site defines, never a native host
|
||||
build.
|
||||
- Report outcomes faithfully. Quote failing output as it is. Name a skipped
|
||||
step as skipped. Never narrate an expected result as an observed one.
|
||||
|
||||
### The workflow: prove locally, push when proven
|
||||
|
||||
- Nothing goes to a public repository until it is proven: in the host tests
|
||||
for host-only changes, on the bench for changes that alter what the machine
|
||||
does.
|
||||
- Iterate locally. Then make one commit and one push for the proven change.
|
||||
No chains of fix-up pushes. Every push runs CI, and CI minutes are a budget.
|
||||
- Commit only finished work, and only when asked. Never commit intermediate
|
||||
findings, working notes, plans, audits, or status files. If a conclusion is
|
||||
still moving, it is not ready for a repository. Unpushed mistakes come out
|
||||
with `git reset --mixed <base>`, which keeps the working tree.
|
||||
- "Commit" and "commit and build" include the push of every touched
|
||||
repository, in dependency order. Never hold a push back to protect earlier
|
||||
unpushed commits on the same branch. The only exception is an explicit "do
|
||||
not push" for that session.
|
||||
- Never push, force-push, tag, cut a release, refresh a lockfile, bump a
|
||||
submodule pointer, or bump a pin unless asked.
|
||||
|
||||
### Pins
|
||||
|
||||
A firmware component reaches the image only through its pin: an exact
|
||||
`SRCREV` in `meta-forgefirm` (in the `forgefirm` repository) for ForgeFIRM
|
||||
components, or in `meta-openglow` for BSP components. There is no `AUTOREV`.
|
||||
A commit in a component repository changes nothing on a machine until its pin
|
||||
moves. The order is always: push the source repository, bump its pin, then
|
||||
run `bitbake -c fetch <recipe>` to make sure that the pin resolves. Some
|
||||
pairs of repositories have a CI coupling that fixes the push order between
|
||||
them. The Release flow page of the site lists them, and each affected
|
||||
repository names its own in the section above.
|
||||
|
||||
### Git conventions
|
||||
|
||||
- A commit is attributed to the human who is responsible for it, in both the
|
||||
author and the committer fields. Never list an AI assistant as an author or
|
||||
a co-author. Never add an AI attribution trailer or a "Generated with" line
|
||||
to a commit message or a pull request. In a fresh clone, set the local
|
||||
`user.name` and `user.email` before the first commit.
|
||||
- A commit message says what changed and why, in the present tense, in
|
||||
Simplified Technical English. The "why" of a change to fork code lives here
|
||||
and nowhere else.
|
||||
- Keep line endings LF in every file that Linux tooling consumes: patches,
|
||||
shell scripts, recipes, device trees. Check the bytes after an edit from a
|
||||
Windows tool. A CRLF patch breaks `do_patch`.
|
||||
- Never commit build output, `__pycache__`, virtual environments, editor
|
||||
files, a local knowledge graph (`graphify-out/`), captured machine data that
|
||||
carries an identity, tokens, keys, or passwords.
|
||||
- Never rewrite published history unless the operator asks for it.
|
||||
- A pull request to an upstream repository is never automatic. The developer
|
||||
decides if and when to send one. You can remind them that upstream
|
||||
candidates exist, but the decision is theirs.
|
||||
|
||||
### Writing rules
|
||||
|
||||
They apply to everything: documentation, README files, code comments,
|
||||
docstrings, log and console text, commit messages, issue text, and replies.
|
||||
|
||||
- **American English.** analyze, behavior, color, center, gray, catalog,
|
||||
license, judgment, program, artifact, percent, toward, among, while,
|
||||
learned. The style lint in `forgefirm-docs` rejects the British forms.
|
||||
- **ASD-STE100 Simplified Technical English.** Short sentences: 20 words in
|
||||
a procedure, 25 in a description. One topic per sentence, one instruction
|
||||
per sentence. Active voice, imperative in procedures, simple present in
|
||||
descriptions. "Must" for necessity, "can" for possibility. No "should",
|
||||
"would", "could", "may". Prefer: do, make sure, use, start, show, occur,
|
||||
before, after, because, for example, that is. No "etc.", no "and/or".
|
||||
Noun clusters of three words at most. Warnings and cautions come before the
|
||||
step they protect. Warm and direct is good. A joke that shades a fact is
|
||||
not.
|
||||
- **No em dashes.** Never, anywhere. Use a colon, a period, or a comma.
|
||||
- **Present tense, present state.** Text describes the code and the machine
|
||||
as they are. No history narrative, no "the old version did", no "evolved
|
||||
from", no story of how the code got here. Upstream copyright and
|
||||
derivation attribution to public projects stays: that is license credit,
|
||||
not narrative.
|
||||
- **No workstation paths.** Nothing outside a repository appears as a path in
|
||||
it: no drive letters, no WSL mounts, no home directories, no sibling-repo
|
||||
relative paths, no private reference directories. Name the artifact
|
||||
instead: "the captured factory pulse files", "that bench session".
|
||||
- **No bench-machine identity.** Never a hostname, a serial number, a fuse
|
||||
value, a private IP address, or a login of the bench machine, in any file,
|
||||
comment, commit message, sample, or test fixture. Use neutral placeholders
|
||||
of the `ABC-123` form. Record the fact of a verification, never the values.
|
||||
- **No factory firmware code.** Never cite the factory firmware binary or its
|
||||
code: no offsets, no addresses, no quoted or decompiled code. State the
|
||||
recovered fact (a formula, a constant, a behavior) and stop. Hardware
|
||||
addresses (IOMUX pad values, sensor registers, boot offsets) are fine.
|
||||
- **No rationale comments in fork code.** Code destined upstream (for
|
||||
example, the grblHAL core fork) carries the minimum change and no "why we
|
||||
did this" comments. The reasoning goes in the commit message. Patches
|
||||
carried in the Yocto layers are exempt. The `ForgeFIRM:` comment convention
|
||||
there is deliberate.
|
||||
- **Interface docs describe the lever, not the policy.** A sysfs attribute
|
||||
doc says what the attribute is, its range, its units, its conversion. How
|
||||
the factory firmware drives it does not belong there.
|
||||
- **No definitive legal or regulatory statements.** The project is not a
|
||||
lawyer. The only permitted form: modifying the machine, including
|
||||
replacing its firmware, may have legal and regulatory ramifications, and it
|
||||
is the end user's responsibility to adhere to the laws, regulations,
|
||||
certifications, and insurance terms that apply where they are. Warranty
|
||||
wording is "may void your warranty", never "voids". Never cite a regulation,
|
||||
a standard, or a certification regime as applying or not applying. The
|
||||
affiliation disclaimer lives in the site footer only. It is also the user's
|
||||
responsibility to make sure that they stay within the vendor's terms of
|
||||
service when they use a feature that calls that vendor's cloud services.
|
||||
- **Copyright.** Copyright in this project belongs to 514 LLC d/b/a
|
||||
OpenGlow. Every new source file carries two lines in its header, in the
|
||||
comment style of the file: `Copyright <year> 514 LLC d/b/a OpenGlow` and
|
||||
`Written by Scott Wiederhold`. Keep the year, or the year range, that
|
||||
the file already has. The copyright notices of other holders stay
|
||||
unchanged.
|
||||
- **SPDX.** Every new source file carries an `SPDX-License-Identifier` line
|
||||
under the license of its repository.
|
||||
|
||||
### Where documentation goes
|
||||
|
||||
- **One home.** A fact lives on the documentation site, or it does not exist.
|
||||
A repository README says what the repository is, how to build and test it,
|
||||
and where the documentation is. Nothing else.
|
||||
- **The currency rule.** A change carries a documentation commit when it adds,
|
||||
removes, or renames an interface (a sysfs attribute, an HTTP route, a
|
||||
settings key, a G-code or `$` setting), or when it corrects a measured
|
||||
fact. A measured fact says how it was obtained, and on what: unless the page
|
||||
says otherwise, a measurement on the site was taken on the bench reference,
|
||||
which the site defines.
|
||||
- **A moved document is deleted.** No stub and no redirect stays at the old
|
||||
path.
|
||||
- **Every diagram is Mermaid.** No ASCII art and no image of a diagram.
|
||||
- There is no roadmap page. Open items are tracked as GitHub issues once the
|
||||
repositories accept them.
|
||||
- Do not add plan files, audit files, working notes, or status files to any
|
||||
repository. The project has **no status document and no dated log**: the
|
||||
site describes the present, and the record of what was done, how it was
|
||||
proven, and what it replaced goes in the commit message that carries the
|
||||
change.
|
||||
|
||||
### Analysis and reporting
|
||||
|
||||
- Rank engineering options on technical merit only: performance, correctness,
|
||||
robustness, verifiability. Process cost (an acceptance campaign, an image
|
||||
flash, a new dependency) is a one-line footnote, never a ranking factor.
|
||||
The operator decides the process cost.
|
||||
- Never infer or assume a hardware fact. Label every unverified claim as
|
||||
unverified, and say how to verify it.
|
||||
- Report only what the observations confirm. Everything else is labeled
|
||||
"unconfirmed" and is not acted on.
|
||||
|
||||
### General practice for agents
|
||||
|
||||
- Read the README of this repository, this file, and the Developers section
|
||||
of the site before you change code.
|
||||
- Orient in the code before you edit. If a local knowledge graph
|
||||
(`graphify-out/`) exists in the working tree or its parent, query it first.
|
||||
It is a local artifact and is never committed.
|
||||
- Keep a change minimal and scoped to the request. No drive-by reformatting,
|
||||
no unrelated cleanups, no renames for taste. Match the style of the
|
||||
surrounding code.
|
||||
- Do not add a dependency without a stated reason. The target is a
|
||||
single-core machine with a small eMMC, and every package rides the image.
|
||||
- Extend an existing test or tool instead of adding a parallel one.
|
||||
- Never create a file in the repository that the request does not need.
|
||||
- Ask before anything destructive or outward-facing: a push, a force-push, a
|
||||
tag, a release, a history rewrite, or any change to a machine.
|
||||
- When a fact is unknown, say so and say how to find out. Never fill a gap
|
||||
with a plausible number.
|
||||
- Use the exact names of the project: kas, bitbake, pin, SRCREV, campaign,
|
||||
fingerprint, drill, sysfs, the latch, the armed window. Do not invent names.
|
||||
|
||||
### Before you commit
|
||||
|
||||
1. The host tests of this repository pass, with `-Werror` where the build
|
||||
uses it.
|
||||
2. The change is proven at the highest level available: CI test, bench drill,
|
||||
or documented reasoning.
|
||||
3. For a component that ships in the image: the acceptance catalog covers the
|
||||
changed behavior, or the commit message says why it has no catalog
|
||||
consequence.
|
||||
4. New files carry the 514 LLC d/b/a OpenGlow copyright, the `Written by`
|
||||
line, and an SPDX line. Line endings are LF.
|
||||
5. No em dashes, no British spellings, no workstation paths, no bench
|
||||
identity, no factory firmware code, no history narrative, no AI
|
||||
attribution.
|
||||
6. Documentation is current: interfaces and measured facts (the currency
|
||||
rule).
|
||||
7. The commit is one settled change, attributed to the human author, and it
|
||||
is pushed only when the operator asks.
|
||||
# AGENTS.md
|
||||
|
||||
Instructions for AI coding agents that work in this repository. A human
|
||||
contributor is welcome to read it too: it is the same set of rules.
|
||||
|
||||
## ForgeFIRM repository rules
|
||||
|
||||
### Safety on the machine
|
||||
|
||||
- The laser never fires with the coolant pump deliberately off. The heater-only
|
||||
flow-calibrate and flow-verify tools are the only pump-off state, and the
|
||||
tube stays dark during them.
|
||||
- The 40 V motor rail stays up while the machine is on. Never cycle it for a
|
||||
controller handover. A rail glitch can leave the stepper drivers
|
||||
unserviceable.
|
||||
|
||||
### The iteration loop
|
||||
|
||||
- Edit, cross-build in the Yocto build environment, hot-deploy to the
|
||||
machine, prove. Then one commit, one push, one pin bump for the proven
|
||||
change.
|
||||
- A full image build happens once, at the end of a work item, never per fix.
|
||||
- Kernel and BSP changes ride one image flash, batched. Validate a `.ko` or
|
||||
overlay change on the image that ships it. Never hot-swap a `.ko` onto a
|
||||
board that is about to be reflashed.
|
||||
|
||||
### Acceptance coverage
|
||||
|
||||
- Evaluate every change against the acceptance catalog in
|
||||
`forgetest/forgetest/suite/`. Does an existing test exercise the changed
|
||||
behavior? Does its `covers` map name the files you touched? If not, add or
|
||||
widen one in the same change. The coverage lint
|
||||
(`python3 -m forgetest.coverage --enforce`) is the floor.
|
||||
- The `SRCREV` of a component and the `PV` that moves with it live in
|
||||
`<recipe>-pin.inc` next to the recipe. Nothing else goes in that file. The
|
||||
image manifest keeps `*-pin.inc` out of the layer content hash. Thus a pin
|
||||
bump invalidates only the acceptance tests that cover that component. A
|
||||
pin in a recipe body counts as a platform change and needs a full
|
||||
acceptance campaign.
|
||||
- Test a harness change yourself, on the host, before you hand it to the
|
||||
operator for a bench run. A harness that fails on its own defect costs the
|
||||
operator a full re-run cycle.
|
||||
- A test declares its `mode`, and the runner switches. Cloud tests stay in
|
||||
cloud mode. Keep `requires` lists minimal, and never gate one test behind a
|
||||
chain of others.
|
||||
- Never exercise a gate or a limit through the `GFCOOL_*` environment
|
||||
overrides in a test. Use the settings API, and restore it in the teardown.
|
||||
|
||||
### CI couplings that set the push order
|
||||
|
||||
- The CI of `grblHAL-glowforge` gets the laser harnesses from `scripts/bench/`
|
||||
at the head of `master`, unpinned. Push a harness change here before the
|
||||
driver change that needs it.
|
||||
- The acceptance page shares its theme and its vendored Bootstrap with the
|
||||
`forgectrl` panel, byte for byte. Land a UI change in `forgectrl` first.
|
||||
Push it and pin it. Only then push this repository.
|
||||
- The CI of `kernel-module-glowforge` cross-builds against the BSP from
|
||||
`meta-openglow`. Push `meta-openglow` before a module change that needs a
|
||||
BSP change.
|
||||
|
||||
### Builds and images
|
||||
|
||||
- Builds run only in the dedicated Yocto build environment that the Build page
|
||||
of the site defines (kas and bitbake). Never use a native host build of a
|
||||
component as proof, not even for a compile check.
|
||||
- Every build makes both images: `forgefirm-image` (the release image) and
|
||||
`forgefirm-image-dev` (the bench image, with `forgetest` on port 8090). The
|
||||
dev image needs an explicit `--target`. Report both artifact names.
|
||||
- After a kernel-module pin bump, rebuild the kernel and the module in the
|
||||
same run: `bitbake -c cleansstate linux-fslc kernel-module-glowforge`, then
|
||||
the images. The kernel version suffix is not reproducible across
|
||||
re-checkouts, and a rootfs whose kernel and module disagree fails.
|
||||
- Read the layer HEAD lines at the top of a build log before you trust a build
|
||||
that moves a pin.
|
||||
|
||||
### The record of the work
|
||||
|
||||
This repository carries no status document and no dated log. Both were
|
||||
retired: every fact about the machine and the firmware is on the
|
||||
documentation site, and **the record of a piece of work is the commit message
|
||||
that carries it**: what was done, how it was proven, and what it replaced, in
|
||||
the same place as the change itself.
|
||||
|
||||
A measurement session or a bench drill is written up in the commit that
|
||||
lands its result. Nothing accumulates in a file.
|
||||
|
||||
### On the bench
|
||||
|
||||
These rules apply whenever an agent works with the operator at a machine.
|
||||
|
||||
- **One armed run per turn.** Start the run, let it finish, stop, report what
|
||||
happened against what was expected, and wait for the operator's
|
||||
confirmation before any further armed run or machine command. Never chain a
|
||||
jog, a fix, or a re-run onto a burn in the same step. Dry runs (latch
|
||||
locked, no emission) can be batched. Say which is which before you start.
|
||||
- **Zero inference during live fire.** Report only what the combined
|
||||
observations confirm: the operator's eyes, the drill output, the logs, the
|
||||
sysfs readbacks. Everything else is labeled "unconfirmed" and is not acted
|
||||
on.
|
||||
- **One Grbl connection.** Never open a second connection to TCP port 23 while
|
||||
a drill, an acceptance test, or a sender holds one. The last connection
|
||||
wins, the displaced client's cleanup never arrives, and its queued commands
|
||||
wait behind the arm gate. Read state through `forgectrl` (HTTP) and sysfs
|
||||
only. After a displaced or aborted drill: soft reset, confirm that the RX
|
||||
buffer reads full, `M5`, confirm that `armed` is false, before anyone
|
||||
touches the button.
|
||||
- **The operator touches the machine.** The operator runs the install, the
|
||||
service restart, and the reflash on the machine. Never modify the software
|
||||
of a running machine unless the operator asks for that exact step.
|
||||
- **Bench hygiene.** Stage test files in `/tmp`. A file that must survive a
|
||||
reboot goes in `/data/bench-scratch/`, and that directory is deleted whole
|
||||
at the end of the session. Nothing goes loose in `/data`. Remove everything
|
||||
you put on the board, in the same session, and list `/data` before you end.
|
||||
A bench tool worth a second use goes in `scripts/bench/`. The dev image
|
||||
installs those tools under `/usr/share/forgetest/bench/`. Run them from
|
||||
there.
|
||||
|
||||
## Project-wide rules
|
||||
|
||||
The sections above are specific to this repository. The rules below apply to
|
||||
every repository of the OpenGlow ForgeFIRM project: the firmware components,
|
||||
the build base, the documentation, and the hardware designs. Where a
|
||||
repository rule and a rule below conflict, the safety, proof, and hygiene
|
||||
rules below win. The documentation site, <https://docs.forgefirm.org/>, is
|
||||
the source of truth for every fact about the machine and the firmware. Read
|
||||
its Developers section before you change code.
|
||||
|
||||
### What ForgeFIRM is
|
||||
|
||||
ForgeFIRM is open firmware for stock Glowforge lasers (Basic, Plus, Pro). It
|
||||
runs on the factory NXP i.MX6 control board with no hardware modification. It
|
||||
replaces the cloud-dependent factory userspace with an open Linux image that
|
||||
controls the machine locally. In GRBL mode the machine is a standard grblHAL
|
||||
controller for LightBurn and other senders on TCP port 23. Cloud mode runs the
|
||||
factory experience through the Glowforge web service, and it is a deliberate,
|
||||
maintained feature. The stack is hardware validated: GRBL mode cuts real jobs,
|
||||
and cloud mode runs end to end.
|
||||
|
||||
One controller runs at a time under `forgectrl`, the machine-services daemon,
|
||||
which owns the pulse device, the cooling engine, the cameras, and the control
|
||||
panel. The kernel module plays the pulse stream into the stepper drivers and
|
||||
owns the laser latch and the safety readbacks. The Technical section of the
|
||||
site describes the whole stack.
|
||||
|
||||
### The repositories
|
||||
|
||||
All repositories live under the OpenGlow organization on GitHub,
|
||||
<https://github.com/openglow-org>. For development, check every repository out
|
||||
as a sibling in one base directory named `openglow-forgefirm`. The build
|
||||
scripts expect that layout. Each repository is its own git repository. The
|
||||
base directory is not.
|
||||
|
||||
| Repository | Role | Default branch | License |
|
||||
|---|---|---|---|
|
||||
| `forgefirm` | The build and release base: the `meta-forgefirm` Yocto layer, the kas configuration, the image recipes, the install and release scripts, the acceptance tool `forgetest/`, the bench tools `scripts/bench/`, and the bench actuator firmware `fixture/`. It carries no project documentation. | `master` | MIT (layer metadata) |
|
||||
| `meta-openglow` | The BSP layers `meta-glowforge-bsp` and `meta-openglow-core`: kernel, device tree, U-Boot, board recipes. | `scarthgap` | MIT (layer metadata) |
|
||||
| `forgectrl` | The machine-services daemon (C) on HTTPS port 443, with the read-only routes on HTTP port 80: supervisor, pulse-device broker, motion-liveness gate, cooling engine, cameras, telemetry, settings, diagnostics, control panel, A/B updates. | `main` | MIT |
|
||||
| `grblHAL-glowforge` | The grblHAL driver for the stock board, the GRBL-mode controller. The core is a submodule at `src/grbl`. Machine constants are in `src/boards/glowforge.h`. | `main` | GPL-3.0-or-later |
|
||||
| `grblHAL-core` | The fork of the grblHAL core that the driver uses. Minimum change, no rationale comments. | `forgefirm` | The upstream grblHAL license |
|
||||
| `kernel-module-glowforge` | `glowforge.ko`: the SDMA + EPIT pulse engine, the laser latch, the safety readbacks, the sensors. | `master` | GPL-2.0-or-later |
|
||||
| `python3-gfhardware` | The `gfhardware` hardware library and the cloud-mode applications in `forgefirm-app/`. | `master` | MIT, with one LGPL-2.1-or-later component |
|
||||
| `Glowforge-Utilities` | `gfutilities` on PyPI: the factory protocol and service layer that cloud mode uses. | `master` | MIT |
|
||||
| `forgefirm-docs` | The documentation site. | `main` | CC BY-SA 4.0 |
|
||||
| `openglow-serial-adapter` | The USB-C serial-console adapter for the control board. | `main` | CC BY-NC-SA 4.0 |
|
||||
|
||||
The full table, the license details, and the component diagram are on the
|
||||
site: <https://docs.forgefirm.org/developers/>.
|
||||
|
||||
### Safety first, and in that order
|
||||
|
||||
The machine is a laser, and the operator is the safety authority at it. These
|
||||
rules bind every change, in every repository, that can affect what the machine
|
||||
does.
|
||||
|
||||
- The hardware chain is the safety boundary. Software only adds gates on top
|
||||
of it. `LASER_ON` is never a bare GPIO. The kernel laser latch is locked by
|
||||
default, and every close of the pulse device locks it again.
|
||||
- Never defeat a switch, an interlock, or a readback, in hardware or in
|
||||
software, not even for a test.
|
||||
- Never change a fire gate, a thermal limit, or a safety default to make a
|
||||
test pass.
|
||||
- The order of work is absolute: emission and motion first, robustness and
|
||||
hygiene after. Review a change that touches emission or motion for that
|
||||
before anything else.
|
||||
- A change that can put energy where it was not commanded gets a regression
|
||||
test with the fix, in the same commit, never after.
|
||||
- Position counters, homing anchors, and a homed flag are not proof of
|
||||
physical motion. The head accelerometer is, and so are the operator's eyes.
|
||||
|
||||
### Proof before done
|
||||
|
||||
A feature or fix is not complete until it is proven. The order of preference:
|
||||
|
||||
1. A host test that runs in CI. The Test page of the site lists the host tests
|
||||
of each repository.
|
||||
2. A bench drill on the bench reference, recorded in the commit message that
|
||||
carries the change.
|
||||
3. Documented reasoning.
|
||||
|
||||
Also:
|
||||
|
||||
- Run the host tests of this repository before every commit. Where the build
|
||||
uses `-Werror`, a warning is a failure.
|
||||
- A component that ships in the image is also evaluated against the release
|
||||
acceptance catalog in the `forgefirm` repository (Test page, "Coverage
|
||||
currency"). A behavior change with no catalog consequence gets a sentence
|
||||
of justification in the commit message.
|
||||
- For a component that ships in the image, the proof build is the Yocto
|
||||
cross-build that the Build page of the site defines, never a native host
|
||||
build.
|
||||
- Report outcomes faithfully. Quote failing output as it is. Name a skipped
|
||||
step as skipped. Never narrate an expected result as an observed one.
|
||||
|
||||
### The workflow: prove locally, push when proven
|
||||
|
||||
- Nothing goes to a public repository until it is proven: in the host tests
|
||||
for host-only changes, on the bench for changes that alter what the machine
|
||||
does.
|
||||
- Iterate locally. Then make one commit and one push for the proven change.
|
||||
No chains of fix-up pushes. Every push runs CI, and CI minutes are a budget.
|
||||
- Commit only finished work, and only when asked. Never commit intermediate
|
||||
findings, working notes, plans, audits, or status files. If a conclusion is
|
||||
still moving, it is not ready for a repository. Unpushed mistakes come out
|
||||
with `git reset --mixed <base>`, which keeps the working tree.
|
||||
- "Commit" and "commit and build" include the push of every touched
|
||||
repository, in dependency order. Never hold a push back to protect earlier
|
||||
unpushed commits on the same branch. The only exception is an explicit "do
|
||||
not push" for that session.
|
||||
- Never push, force-push, tag, cut a release, refresh a lockfile, bump a
|
||||
submodule pointer, or bump a pin unless asked.
|
||||
|
||||
### Pins
|
||||
|
||||
A firmware component reaches the image only through its pin: an exact
|
||||
`SRCREV` in `meta-forgefirm` (in the `forgefirm` repository) for ForgeFIRM
|
||||
components, or in `meta-openglow` for BSP components. There is no `AUTOREV`.
|
||||
A commit in a component repository changes nothing on a machine until its pin
|
||||
moves. The order is always: push the source repository, bump its pin, then
|
||||
run `bitbake -c fetch <recipe>` to make sure that the pin resolves. Some
|
||||
pairs of repositories have a CI coupling that fixes the push order between
|
||||
them. The Release flow page of the site lists them, and each affected
|
||||
repository names its own in the section above.
|
||||
|
||||
### Git conventions
|
||||
|
||||
- A commit is attributed to the human who is responsible for it, in both the
|
||||
author and the committer fields. Never list an AI assistant as an author or
|
||||
a co-author. Never add an AI attribution trailer or a "Generated with" line
|
||||
to a commit message or a pull request. In a fresh clone, set the local
|
||||
`user.name` and `user.email` before the first commit.
|
||||
- A commit message says what changed and why, in the present tense, in
|
||||
Simplified Technical English. The "why" of a change to fork code lives here
|
||||
and nowhere else.
|
||||
- Keep line endings LF in every file that Linux tooling consumes: patches,
|
||||
shell scripts, recipes, device trees. Check the bytes after an edit from a
|
||||
Windows tool. A CRLF patch breaks `do_patch`.
|
||||
- Never commit build output, `__pycache__`, virtual environments, editor
|
||||
files, a local knowledge graph (`graphify-out/`), captured machine data that
|
||||
carries an identity, tokens, keys, or passwords.
|
||||
- Never rewrite published history unless the operator asks for it.
|
||||
- A pull request to an upstream repository is never automatic. The developer
|
||||
decides if and when to send one. You can remind them that upstream
|
||||
candidates exist, but the decision is theirs.
|
||||
|
||||
### Writing rules
|
||||
|
||||
They apply to everything: documentation, README files, code comments,
|
||||
docstrings, log and console text, commit messages, issue text, and replies.
|
||||
|
||||
- **American English.** analyze, behavior, color, center, gray, catalog,
|
||||
license, judgment, program, artifact, percent, toward, among, while,
|
||||
learned. The style lint in `forgefirm-docs` rejects the British forms.
|
||||
- **ASD-STE100 Simplified Technical English.** Short sentences: 20 words in
|
||||
a procedure, 25 in a description. One topic per sentence, one instruction
|
||||
per sentence. Active voice, imperative in procedures, simple present in
|
||||
descriptions. "Must" for necessity, "can" for possibility. No "should",
|
||||
"would", "could", "may". Prefer: do, make sure, use, start, show, occur,
|
||||
before, after, because, for example, that is. No "etc.", no "and/or".
|
||||
Noun clusters of three words at most. Warnings and cautions come before the
|
||||
step they protect. Warm and direct is good. A joke that shades a fact is
|
||||
not.
|
||||
- **No em dashes.** Never, anywhere. Use a colon, a period, or a comma.
|
||||
- **Present tense, present state.** Text describes the code and the machine
|
||||
as they are. No history narrative, no "the old version did", no "evolved
|
||||
from", no story of how the code got here. Upstream copyright and
|
||||
derivation attribution to public projects stays: that is license credit,
|
||||
not narrative.
|
||||
- **No workstation paths.** Nothing outside a repository appears as a path in
|
||||
it: no drive letters, no WSL mounts, no home directories, no sibling-repo
|
||||
relative paths, no private reference directories. Name the artifact
|
||||
instead: "the captured factory pulse files", "that bench session".
|
||||
- **No bench-machine identity.** Never a hostname, a serial number, a fuse
|
||||
value, a private IP address, or a login of the bench machine, in any file,
|
||||
comment, commit message, sample, or test fixture. Use neutral placeholders
|
||||
of the `ABC-123` form. Record the fact of a verification, never the values.
|
||||
- **No factory firmware code.** Never cite the factory firmware binary or its
|
||||
code: no offsets, no addresses, no quoted or decompiled code. State the
|
||||
recovered fact (a formula, a constant, a behavior) and stop. Hardware
|
||||
addresses (IOMUX pad values, sensor registers, boot offsets) are fine.
|
||||
- **No rationale comments in fork code.** Code destined upstream (for
|
||||
example, the grblHAL core fork) carries the minimum change and no "why we
|
||||
did this" comments. The reasoning goes in the commit message. Patches
|
||||
carried in the Yocto layers are exempt. The `ForgeFIRM:` comment convention
|
||||
there is deliberate.
|
||||
- **Interface docs describe the lever, not the policy.** A sysfs attribute
|
||||
doc says what the attribute is, its range, its units, its conversion. How
|
||||
the factory firmware drives it does not belong there.
|
||||
- **No definitive legal or regulatory statements.** The project is not a
|
||||
lawyer. The only permitted form: modifying the machine, including
|
||||
replacing its firmware, may have legal and regulatory ramifications, and it
|
||||
is the end user's responsibility to adhere to the laws, regulations,
|
||||
certifications, and insurance terms that apply where they are. Warranty
|
||||
wording is "may void your warranty", never "voids". Never cite a regulation,
|
||||
a standard, or a certification regime as applying or not applying. The
|
||||
affiliation disclaimer lives in the site footer only. It is also the user's
|
||||
responsibility to make sure that they stay within the vendor's terms of
|
||||
service when they use a feature that calls that vendor's cloud services.
|
||||
- **Copyright.** Copyright in this project belongs to 514 LLC d/b/a
|
||||
OpenGlow. Every new source file carries two lines in its header, in the
|
||||
comment style of the file: `Copyright <year> 514 LLC d/b/a OpenGlow` and
|
||||
`Written by Scott Wiederhold`. Keep the year, or the year range, that
|
||||
the file already has. The copyright notices of other holders stay
|
||||
unchanged.
|
||||
- **SPDX.** Every new source file carries an `SPDX-License-Identifier` line
|
||||
under the license of its repository.
|
||||
|
||||
### Where documentation goes
|
||||
|
||||
- **One home.** A fact lives on the documentation site, or it does not exist.
|
||||
A repository README says what the repository is, how to build and test it,
|
||||
and where the documentation is. Nothing else.
|
||||
- **The currency rule.** A change carries a documentation commit when it adds,
|
||||
removes, or renames an interface (a sysfs attribute, an HTTP route, a
|
||||
settings key, a G-code or `$` setting), or when it corrects a measured
|
||||
fact. A measured fact says how it was obtained, and on what: unless the page
|
||||
says otherwise, a measurement on the site was taken on the bench reference,
|
||||
which the site defines.
|
||||
- **A moved document is deleted.** No stub and no redirect stays at the old
|
||||
path.
|
||||
- **Every diagram is Mermaid.** No ASCII art and no image of a diagram.
|
||||
- There is no roadmap page. Open items are tracked as GitHub issues once the
|
||||
repositories accept them.
|
||||
- Do not add plan files, audit files, working notes, or status files to any
|
||||
repository. The project has **no status document and no dated log**: the
|
||||
site describes the present, and the record of what was done, how it was
|
||||
proven, and what it replaced goes in the commit message that carries the
|
||||
change.
|
||||
|
||||
### Analysis and reporting
|
||||
|
||||
- Rank engineering options on technical merit only: performance, correctness,
|
||||
robustness, verifiability. Process cost (an acceptance campaign, an image
|
||||
flash, a new dependency) is a one-line footnote, never a ranking factor.
|
||||
The operator decides the process cost.
|
||||
- Never infer or assume a hardware fact. Label every unverified claim as
|
||||
unverified, and say how to verify it.
|
||||
- Report only what the observations confirm. Everything else is labeled
|
||||
"unconfirmed" and is not acted on.
|
||||
|
||||
### General practice for agents
|
||||
|
||||
- Read the README of this repository, this file, and the Developers section
|
||||
of the site before you change code.
|
||||
- Orient in the code before you edit. If a local knowledge graph
|
||||
(`graphify-out/`) exists in the working tree or its parent, query it first.
|
||||
It is a local artifact and is never committed.
|
||||
- Keep a change minimal and scoped to the request. No drive-by reformatting,
|
||||
no unrelated cleanups, no renames for taste. Match the style of the
|
||||
surrounding code.
|
||||
- Do not add a dependency without a stated reason. The target is a
|
||||
single-core machine with a small eMMC, and every package rides the image.
|
||||
- Extend an existing test or tool instead of adding a parallel one.
|
||||
- Never create a file in the repository that the request does not need.
|
||||
- Ask before anything destructive or outward-facing: a push, a force-push, a
|
||||
tag, a release, a history rewrite, or any change to a machine.
|
||||
- When a fact is unknown, say so and say how to find out. Never fill a gap
|
||||
with a plausible number.
|
||||
- Use the exact names of the project: kas, bitbake, pin, SRCREV, campaign,
|
||||
fingerprint, drill, sysfs, the latch, the armed window. Do not invent names.
|
||||
|
||||
### Before you commit
|
||||
|
||||
1. The host tests of this repository pass, with `-Werror` where the build
|
||||
uses it.
|
||||
2. The change is proven at the highest level available: CI test, bench drill,
|
||||
or documented reasoning.
|
||||
3. For a component that ships in the image: the acceptance catalog covers the
|
||||
changed behavior, or the commit message says why it has no catalog
|
||||
consequence.
|
||||
4. New files carry the 514 LLC d/b/a OpenGlow copyright, the `Written by`
|
||||
line, and an SPDX line. Line endings are LF.
|
||||
5. No em dashes, no British spellings, no workstation paths, no bench
|
||||
identity, no factory firmware code, no history narrative, no AI
|
||||
attribution.
|
||||
6. Documentation is current: interfaces and measured facts (the currency
|
||||
rule).
|
||||
7. The commit is one settled change, attributed to the human author, and it
|
||||
is pushed only when the operator asks.
|
||||
|
||||
+677
-677
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user