Commit Graph
9 Commits
Author SHA1 Message Date
Oleksandr ShyshatskyiandClaude Opus 5 6a4067e863 VFA calibration is one import away, and that import stops klippy booting
The stock app's "magic" VFA calibration is not app-side math at all. Traced
from CommMgr::calibration_vfa (0x0075e694), it sends five G-code strings and
the last one is STEPPER_RESONANCE_FACTORY_CALIBRATE -- a macro in FlashForge's
printer.vibration.cfg over klippy/extras/stepper_resonance_tester.py, which
searches amplitude and phase for the motor torque-ripple compensation the
closed-loop driver applies at 1x/2x/4x the electrical frequency. Every piece is
already here: the module is byte-identical in our vendored fork, the mclib
sections are in our printer.base.cfg, and mclib_set_resonance_damp is in the
stock mainBoardGD dictionary we never rebuild.

WHAT THE TRACE TURNED UP ON THE WAY. Two comments in this repo said the numpy
gap was a small item because the module guards its own import. It does not:
line 1 of stepper_resonance_tester.py is a bare `import numpy as np`, and
klippy.py:122 loads EVERY config section through a load_object that does not
catch ImportError -- the default=None argument only covers a missing file.
printer.base.cfg:19 includes printer.vibration.cfg, which declares
[stepper_resonance_tester]. So on the CPython 3.13 klippy has run on since
53292b4 this is not a lost feature, it is a klippy that never reaches ready.
Confirmed by importing the module with numpy blocked at sys.meta_path.

Both comments are corrected rather than deleted, because the claim they made is
the one a reader would otherwise make again.

Documentation only -- no behaviour changes here. The note lays out the chain
address by address and the three ways out (package numpy, drop numpy from the
module, or guard the import), and says which to reach for first. Nothing should
be built on it before a real printer.log confirms the boot failure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EJr2x7aDaY889qwSxbdLt8
2026-08-31 13:42:57 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 42d876d5eb The auto-PA sweep is host code; only the per-line verdict is in the eBoard
firmwareExe runs a real PA calibration at print start, inside the nozzle clean.
CommMgr::paTestMgr @0x791640 prints one slow-fast-slow line per candidate at
Y = 50 + 5*i for seven fixed PA values, asks the eBoard to score each line, keeps
the smallest that scores 9, repeats until three sweeps have produced a winner and
averages those three. All of that is host code and is recovered here in full.

Only the verdict crosses into the closed toolhead MCU, over two commands that
pa_adjust.py already forwards -- and pa_adjust.py is in our tree byte-identical
to FlashForge's, with [pa_adjust] already in printer.base.cfg. So PA_ACTION and
PA_GET should answer on a Reforge machine today; that is step one of the port.

This supersedes "sensor unknown" in 60-background.md and "callable, not
reproducible" in 25-app-vs-klipper-ownership.md. What stays unknown is narrower:
which sensor the eBoard samples, what ACTION=11 and PC=666 select, and what the
non-9 verdicts mean.

Two findings that shape the port. PA_GET only respond_info()s its value, so the
sweep cannot be a G-code macro -- it needs an extra that queries the eBoard
directly. And PA_ACTION is an immediate MCU command, so FlashForge never
synchronises the measurement window with motion; the port must copy that timing
rather than improve it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kw2rehvQJ7LSc28hPikfqk
2026-08-30 23:03:37 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 f2e0e5fcb2 The manifest protected three files, and none of them needed it
Proposal: on update, rm -rf $MODDIR and extract into the empty
directory. No manifest, no selective delete, no compatibility branch.

The audit is the note. Against a full wipe .install-manifest was
protecting backup/stock, .prev-root-hash and config-installed, and
each one falls: nothing restores from backup/stock and the documented
recovery is a stock reflash; .prev-root-hash is removed by the
password branch; config-installed is a twelve-file snapshot serving
one md5, for a compare whose stated rationale -- "there is no
include-and-override seam" -- stopped being true when
moonraker-custom.conf got included last, and which inverts its own
purpose by keeping the webcam block and API lockdown off any printer
that trips it.

So moonraker.conf joins ff-*.cfg and is simply overwritten, owner-
dropped files under $MODDIR become an accepted loss, and phase 2's
on-printer half in 85-packaging.md is cancelled rather than owed: a
printer that never deletes selectively needs no file database for it.

Phases 1 and 2 in 80-s6-migration.md and 85-packaging.md point here.
No code has moved.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KXKk3zz8LAUPUDZkVEZ9fR
2026-08-30 15:38:25 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 4850be432c Docs: say what is there now, not what the last three commits removed
Deleting test/ and anvil.conf left claims behind in nine files. Every one of
these was present tense about something that no longer exists.

THE CHELPER SYMBOL GATE, in three places that each described it as live:

  notes/20-klipper-fork.md   said pkgs/klipper gates the .so on "both the ELF
                             flags and test-chelper.py's symbol check". It
                             gates on neither now: the ABI is asked once of the
                             installed filesystem in qa/replica/test_abi.py,
                             and the single-source rule -- the .so is compiled
                             from the chelper sources of the tree it ships -- is
                             the whole defence. The page says that instead.
  notes/85-packaging.md      described the KLIPPER_FORK fallback the script no
                             longer had; the script itself is gone too.
  qa-migration.md            "What survives in test/" listed both the 28 unit
                             tests and test-chelper.py as surviving. It is
                             "What survived in test/, and then did not", and it
                             names what went uncovered rather than leaving that
                             to be discovered.

ANVIL.CONF, in the two notes that still read it at runtime:

  notes/80-s6-migration.md   is a dated plan, so it keeps its phases and gains
                             a "Where this ended up" note: anvil.conf and every
                             MOD_* switch were removed, so the phases that
                             source it describe an intermediate state, and the
                             case-*.sh gates they name are qa/replica/ modules.
  notes/85-packaging.md      predicted seed/ emptying via a postinst that seeds
                             anvil.conf.default. It emptied by anvil.conf being
                             deleted instead. Its payload allowlist said six
                             entries including anvil.conf and bin/busybox; the
                             real one in test_ipk.py has five and neither.

THE TWO STANDALONE TREES under tools/, stale before any of this and now fixed:
tools/python/README.md and tools/supervisor/README.md both told the reader to
run their gate with `test/integration/printer-exec.py` over a case-*.sh, and
one offered a `make test-python` target. None of the three has existed for
several commits. Both now point at qa/replica/, which installs the real package
and asks more than hand-feeding a tarball to a case script could. The four
places tools/python/README.md narrates what case-python.sh proves are past
tense, since that is now a record of how the measurements were taken.
versions.env's sqlite3 note pointed at the same dead target.

NAV: every notes/ page was listed except 85-packaging.md, and qa-migration.md
was reachable only by following a link out of Testing, Building or How it
works. Both are in the nav now, which also clears the two "not included in the
nav" lines from every build. .venv/ is gitignored -- docs/requirements.txt
tells you to make one right there in the tree.

Verified: `mkdocs build --strict` is clean and no longer reports pages outside
the nav. qa/static is 123 passed, its 110 errors the same missing
vendor/opkg-utils as before. No .md file is read by any lane, so the check that
matters here is the build.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bLGYpVAeVzAX6NeAxcU42
2026-08-30 01:16:54 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 133e43e05e The layout that took twenty rounds to find was written down nowhere.
The docs were restructured into three sections yesterday. The reasoning lived
in one conversation: which section a page belongs to, what may appear in it,
and which things were removed on purpose. The next session would have opened
mkdocs.yml, seen a nav, and guessed.

.claude/skills/reforge-docs is the map. Three audiences with the test that
decides between them, what belongs in each section and what must never appear
there, and where a new page goes. The rules in it are the ones review actually
produced: an owner page assumes the machine works, says when a step is
optional, and names a file only when the reader has to type it -- which is why
[ff_legacy], SAVE_CONFIG, dropbear, config inventories, theory sections,
expected-output dumps and symptom tables all came out of pages they had been
written into.

It also records what cost time rather than only what was decided. A gain claim
has to be checked against notes/25-app-vs-klipper-ownership.md, because tool
calibration and input shaping are the touchscreen's own sequences reimplemented
and were credited to the mod anyway. --strict validates markdown links and
nothing else, which is how three render bugs reached master through raw HTML,
images and task lists. The README hook is imported once, so mkdocs serve keeps
running stale code after it is edited. Edits anchor on unique strings, because
a file rewritten earlier in a session invalidates every line number -- that one
was a real bug, not a hypothetical. And docs get written against the branch
whose code ships: pages assembled on qa-suite-bridgehead described s6-rc
services that master does not have.

docs-writer.md comes back with it. It was written at the start of that session,
never committed, and swept into a stash by `git stash -u` during a branch move;
it holds the generic craft -- who the reader is, the voice, claim nothing you
have not checked -- and now defers to the skill for the layout instead of
describing a two-section nav that no longer exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3XZh4SqPdXAowVndz6tMr
2026-08-29 00:47:54 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 65a7528e09 The manual assumed a printer that arrives broken. It arrives calibrated.
The pages written on top of the old README inherited its premise: that after
flashing you face a gauntlet -- go/no-go checks, a safety net, a mandatory
calibration -- before the machine can be trusted with a model. That is not
what happens. The factory numbers import themselves on the first boot, a bed
mesh loads at the start of every print, and the mod is built to run the files
the stock firmware ran. A printer that printed before the flash prints after
it. "Your first print" now says so in its first line, and everything under it
is optional, ordered by what you would actually reach for: check the offsets
are applied, and calibrate only if that disagrees with the machine.

Three audiences now, because owner and contributor was not the real split.
"For experienced users" sits between them: How a print runs, and How
calibration works. Someone who knows Klipper and wants to change what happens
around their file has a place to go that is neither a walkthrough nor the
build system.

What was cut is most of what changed. Pages that were cryptic to the reader
they were aimed at -- Printing, Upgrading -- are out of the nav and kept on
disk to be rewritten rather than deleted. Within the pages that stayed:
Expected output, Probe sampling, What is being measured, the symptom table,
the dark-screen diagnosis, TOOL_Z_ADJUST, PLATE_CHECK, the ff-*.cfg
inventory, [ff_legacy] and SAVE_CONFIG in a paragraph about factory
calibration. Filenames and internals earn their place on an owner page only
when the reader has to type them or look at them; the rest moved to the
experienced pages or to Internals, where the audience wants them.

Calibration became a section rather than a page, because tool offsets are not
the only calibration: bed mesh has its own page now, and its own gotcha
written down -- BED_MESH_CALIBRATE writes `default`, a print loads
`MESH_DATA`, so a mesh probed by hand is a mesh no print will use. Input
shaping is described but has no procedure page yet.

Several claims came out because they were not true. "Is this for you" listed
calibration and input shaping as things the mod gives you; both are the
touchscreen's own sequences, recovered and reimplemented, and the notes say
so plainly. What the mod actually gives you is that anything speaking
Moonraker can drive the machine -- on stock, FlashForge's Klipper does not
toolchange at all, it raises a flag and waits for an application that is not
watching a Mainsail print, and the print hangs at the first T2. The page now
opens by telling most owners not to flash it.

Contributors are still deferred. Their pages are regrouped, not rewritten.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3XZh4SqPdXAowVndz6tMr
2026-08-28 23:33:29 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 0af15a549a The front page shipped with broken images and literal [x] boxes.
Three defects, all in the site machinery rather than the prose, all visible
on the first page anyone opens.

`r"\1=\""` is not an escaped quote. In a raw string that backslash is a
character, so every `src="docs/…"` the README hook rewrote came out as
`src=\"helixscreen-print-status.webp"` and the browser asked for a file whose
name began with a quote. Both screenshots on the front page 404'd.

`- [x]` is python-markdown's plain list syntax unless pymdownx.tasklist is
enabled, so the Status checkboxes -- the list this project leads with, and
the thing a reader decides on -- rendered as the literal text `[x]`. Enabled,
with custom_checkbox so they get the theme's own mark. The checklists in
is-this-for-you.md and after-the-flash.md were the same bug.

And mkdocs rewrites markdown links but not raw HTML attributes, which is
invisible until a page with an `<img>` in it is not the front page: with
directory URLs, is-this-for-you.md is served from /is-this-for-you/, one level
below the image it names. The hook now rewrites relative src/href in raw HTML
too, prefixing `../` per level of the page's own URL, so an `<img>` works from
wherever the page ends up -- and it points at a file that must exist, so a
typo stays a broken link the build can still catch.

Found by serving the site and looking at it, which is the only way any of
these three surface: --strict validates markdown links, and every one of
these was in HTML or in text it had no reason to read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3XZh4SqPdXAowVndz6tMr
2026-08-28 22:24:39 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 a6098c7f64 The README was the manual. It is a front door now, and the manual is nine pages.
Fourteen sections, install through troubleshooting, in the one file that
existed when there was nowhere else to put them. The site gave the manual a
home; this moves it there and orders it by what the reader is doing rather
than by which subsystem owns the answer. Someone whose printer will not boot
should not have to know that the symptom table lives in a file called
hardware-testing.md.

Three topics were documented two and three times over, and each copy was free
to drift: install in README and toolchange.md, calibration in README,
calibration.md and toolchange.md, slicer setup in README and toolchange.md,
"before you start" in all three. Each is now in one place.

The owner path is Is this for you -> Installing -> After the flash -> Your
first print -> Nozzle-offset calibration -> Printing -> Upgrading -> When
something goes wrong, with error codes, versions and how-it-works behind a
Reference group. The README keeps what a front door keeps: what this is, the
status checkboxes, what you give up, which file to download, and where to go
next.

toolchange.md was four documents in one 38 KB file. The owner half -- slicer
setup, filament, runout, the offset check before a first print -- is in
Printing and Your first print; the design notes, input shaper, roadmap,
HelixScreen and reverse-engineering notes stay, and the page moves to
Internals. hardware-testing.md was split down the middle by audience the same
way: the go/no-go checks and the symptom table were the owner's, and only
keeping the two verification files honest as the macros change is ours.

Two things this fixes rather than moves. The error-code table was
notes/70-error-codes.md, an internals note the README sent owners to; it is
docs/error-codes.md now, a page. And toolchange.md still opened "Not yet run
on hardware" while the README's checkboxes described a Creator 5 Pro that has
been printing for weeks -- the two pages have disagreed since the first
hardware run.

Contributors are deferred, as agreed: those pages are regrouped, not
rewritten. The nav says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3XZh4SqPdXAowVndz6tMr
2026-08-28 22:20:19 +03:00
Oleksandr ShyshatskyiandClaude Opus 5 7347f56418 docs/ rendered on GitHub and nowhere else. It is a built, versioned site now.
The alternative was a wiki, and a wiki is a separate git repo: a page would
stop moving in the same commit and the same review as the code it describes,
with nothing able to catch the drift, and there would be exactly one live
version of it. This project ships a package per tag and tells owners which
checkboxes have touched hardware; the docs for the build someone flashed have
to be readable, so Read the Docs builds this repo per tag and `latest` off
master, with the version switcher that follows from that.

Nothing moved. The audience split that the pages already have -- owner pages
against build-and-test pages -- is the nav in mkdocs.yml, so a reader lands in
their own section and never falls out of it, while the 30+ relative links
between README.md, docs/*.md and docs/notes/ keep working in the repo view and
the links people have already shared keep resolving. notes/70-error-codes.md
sits in the owner section because README.md was already sending owners there.

Two things had to hold for that to be true, and hooks/mkdocs_readme.py is
both. Links out of docs/ -- `../payload/klipper/extras/ff_tool.py` and 18
others in toolchange.md -- are written relative because that is what works
where a contributor reads them, in the repo; they become GitHub URLs at build
time, and a link whose target does not exist is left alone so the build still
fails on it. README.md is the front door and stays it: docs/index.md is
generated from it on every build and gitignored, rather than being a second
front page free to drift from the first.

Anchors use GitHub's slug rules for the same reason. `#runout--clog` is what
GitHub makes of `## Runout / clog`, python-markdown would have made
`#runout-clog`, and toolchange.md:340 already pointed at the first.

CI builds it with --strict, which is the cheap half of keeping docs true: a
rename in payload/ now breaks the build instead of quietly leaving a page
pointing at nothing. It cannot check that the prose is still right. Only a
reader can.

The site is not up yet -- the project has to be created on readthedocs.org and
its versions activated -- so nothing links to it from README.md. The slug is
free; mkdocs.yml claims it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3XZh4SqPdXAowVndz6tMr
2026-08-28 22:02:43 +03:00