Skip to content

Octatrack base: a store for every module's state, per project (ot_store) - #1320

Draft
repeat98 wants to merge 4 commits into
mainfrom
claude/module-store
Draft

repeat98 wants to merge 4 commits into
mainfrom
claude/module-store

Conversation

@repeat98

@repeat98 repeat98 commented Nov 8, 2026 •

Copy link
Copy Markdown
Owner

Draft: waits for the owner's unit test. Not mergeable before that.

What

Modwerk's own per-module settings store, in the base (owner, 8 November 2026: our own implementation, not octabam's).

  • The table. A module that keeps state contributes one record to a new base table ot_store (events.json index 38, 16 bytes, room 32): {abi, block, bytes, version, loaded(how, version, bytes)}. The module keeps its state in its own block. The base saves the block with the project and hands it back.
  • The project file. Each block goes into project.work as #MWS=module:version:bytes, #MWD=module:offset:hex (16 bytes a line, at most 53 characters with CR LF; the base's own #MODWERK_FX= lines already reach about 45 on hardware) and #MWE=module:sum (FNV-1a). Stock's loader skips # lines, so stock's SAVE, .work→.strd copy, RELOAD and project change carry them. The store rides fxmap's existing project-file seams (fxmap.c calls in), so the table owns no stock site.
  • Loading. Only complete entries whose sum holds are staged, and only a successful load commits them. Every installed module is then called with LOADED, or with NEW when its project has no block for it.
  • Modules not installed keep their blocks: the writer puts them back out as read. A module removed and re-added gets its block back (RESTORED).
  • Power cycles. A power cycle reads no project file, so the base keeps a copy in battery RAM at 0x100f9800-0x100fc000 (10,232 bytes for all blocks). It copies changed blocks there once a second, so a power cut can lose the last second. The first range I picked overlapped the Micro Timing draft's battery table (0x100f8ffc-0x100f97ff, Align Micro Timing's screen claim, battery notes and index #1262), so the store starts after it.
  • One task changes the store: the UI tick (modwerk_store_tick, ev_tick order 94). The loader pass only stages lines; the tick commits them, calls the modules and keeps battery RAM up to date. The writer only reads: the live blocks of installed modules, then the kept blocks of the rest.
  • Guides. system.md ("Persistence is honest") and the module guides' README ("A module owns what it saves", the Module settings row) now name ot_store. HOOKS.md is regenerated from events.json.

Base rebuilt with --beta --write. Base-bound packages relinked (octatrack:module-packages -- --base-only); the write-lease entries did not move.

How I checked

  • test_store.c (in sdk/tests/test_machine_events.py, address and undefined-behaviour sanitizers) covers:

    • a first start;
    • battery RAM after edits;
    • the writer's lines and their length;
    • a load with a module that is not installed, a wrong sum, a short entry and an out-of-order one;
    • a project without store lines;
    • a failed load;
    • remove and reinstall;
    • a power cycle;
    • a size change;
    • a full store.

    I broke the sum check on purpose and the test failed.

  • npm run pr:ready: Ready (SDK suite, site tests, base pins).

Not tested

  • Hardware: not tested. No unit was available.
  • Emulator (ot_emu, 8 November; emulator evidence, not hardware). The base was built from 35498b4 (--beta --out), with a probe module (40-byte block, version 1) on the private TEMPLATE card:
    • Passed: install gives NEW. PROJECT > SAVE writes one #MWS, three #MWD and one #MWE line with the right bytes and sum (longest line 53 characters) and leaves stock's lines alone. PROJECT > RELOAD gives LOADED with the saved bytes. A restart with battery RAM kept (--cs1-in) gives RESTORED with the battery's newer block. Removing the probe keeps its lines through a SAVE, and re-adding it gives RESTORED.
    • Failed: PROJECT > CHANGE > CREATE EMPTY PROJECT never runs the project loader. The probe kept the last project's block, and stock wrote it into the new project's project.work. The store needs the new-project path; that fix comes before this can merge.
    • Seen in passing, not the store's: OS_VERSION in the saved file went from the fixture's R0177 1.40B to R.
  • No module uses the store yet. The first adopter needs its author's OK.

Base batch

#1321 (crash naming) also regenerates the base files. Whichever merges second merges main, then rebuilds the base (--beta --write) and the base-bound packages.

Guides

Battery RAM has no ledger, so nothing caught the overlap with Micro Timing. A check for it follows as its own Improvement PR.

🤖 Generated with Claude Code

repeat98 and others added 4 commits November 8, 2026 10:33
A module that keeps state contributes one ot_store record (table index 38)
naming a block in its own memory. The base keeps every block in project.work
as '#MWS=', '#MWD=' and '#MWE=' lines (16 bytes a line, 53 characters at
most) through fxmap's existing project-file seams, so the table owns no
stock site. It also keeps them in battery RAM (0x100f8f08, 12 KB), because a
power cycle reads no project file.

The loader's storing pass only stages complete entries whose sum holds. The
UI tick commits them after a successful load and calls each module:
LOADED, NEW for a project without its block, or RESTORED after a power-on
or a reinstall. Once a second it copies changed blocks to battery RAM. The
writer only reads: installed modules' blocks as they are, then the kept
blocks of modules that are not installed, written back unchanged.

Base rebuilt with --beta --write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm run octatrack:module-packages -- --base-only (the write-lease entries did
not move). system.md and the module guides' README tell authors to keep
settings in ot_store instead of a format or battery RAM of their own.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Micro Timing draft (#1262) keeps its table at 0x100f8ffc-0x100f97ff, inside
the range the store took. The store now fills 0x100f9800-0x100fc000: 10,232
bytes for every kept block. Base rebuilt, base-bound packages relinked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/module-guides/README.md

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant