Skip to content

Release: steam-achievements 0.1.0, and plugin thumbnails - #4

Merged
helloyork merged 4 commits into
masterfrom
develop
Aug 2, 2026
Merged

Release: steam-achievements 0.1.0, and plugin thumbnails#4
helloyork merged 4 commits into
masterfrom
develop

Conversation

@helloyork

Copy link
Copy Markdown
Contributor

Release merge for narraleaf.steam-achievements@0.1.0. Tag goes on master once this lands, per README → Releasing (maintainers).

What lands

Steam Achievements 0.1.0 — now actually shippable. Its Rust half had never been compiled and its manifest carried six placeholder sha256 digests, so every game build failed on a file that was not there. The stated blocker — that the Steamworks SDK needs a Valve partner account — turned out not to be true: steamworks-sys vendors the SDK under its own lib/steam/ and falls back to it whenever STEAM_SDK_LOCATION is unset. So the bridge builds with a Rust toolchain and nothing else, and contributes.buildDependencies is gone: the Steam shared library ships beside the executable, copied out of cargo's own output so the two cannot drift apart.

Verified against a live Steam client on windows-x64 using Spacewar (App ID 480): init reports the real App ID and language; an unknown API name is refused by Steam rather than silently succeeding, which is how you know the call reaches Steam; unlock / setStat / indicateProgress / resetAll all land through StoreStats. Studio's own artifact compiler stages the package into a preview compile with both digests matching.

Digests are computed, never authored. manifest.json declares no sidecar at all — a sidecar target claims a package carries certain bytes, and that is only true of a package that has the binary, which this repository does not (they are build output). sidecar/contribution.json holds what is not a property of a compiled artifact; build.mjs includes each platform whose files are present, hashes them, and writes the block into dist/manifest.json. With no binaries it emits a mirror-only package: every node still works, nothing reaches Steam. That is the shape CI packages on every push, because it is the one that must never break.

Plugin thumbnails (cc27e69, db806ea, b5d97ed) — already on develop, riding along.

Registry tooling

  • release.yml gains a three-platform Rust build matrix. Each runner builds the sidecar for its own platform; the release job collects them and packages. Packaging the macOS image on Linux is safe: no zip writer records POSIX modes, so Studio repairs the executable bit when it spawns a sidecar (sidecarHost.ensureExecutable).
  • ci.yml runs a plugin's test script when it has one.
  • package-plugin.mjs now validates dist/manifest.json too, and re-hashes every declared sidecar file. The shipped manifest is generated, and generation was the one part of the package nothing checked.

Checks

  • CI green on develop — the first green run since the plugin landed; it had been failing on a narraleaf-studio devDependency pinned to a version that predates the APIs the plugin uses.
  • yarn typecheck 0 errors, 29 unit tests, validate.mjs, generate-index.mjs --check, and the registry tooling tests all pass.

Merge with a merge commit, not squash or rebasemaster only advances through release merges.

🤖 Generated with Claude Code

helloyork and others added 4 commits July 31, 2026 13:07
Studio now shows a plugin's own icon beside its name in the Launcher list, so
`manifest.json` gains `icon`: a package-relative image the store and the
installed list both draw. index.json carries it as an absolute raw URL pinned
to the release tag, for the same reason the download URL is - an index entry
describes one immutable version, and the picture it shows should not change
under it when the next version lands.

The validator enforces every rule Studio enforces, one step earlier: square,
64x64 to 512x512, at most 512 KB, .png/.webp/.jpg/.jpeg only, and the bytes
must really be the format the name claims. scripts/lib/image.mjs is a port of
Studio's reader and carries the same standing obligation as the manifest
validator next to it - if Studio's rules move, this moves with them.

The icon has to reach dist/ the way manifest.json does, so the template's
build script copies it and package-plugin.mjs refuses a zip that is missing
it. The starter ships a placeholder piece, which is also what keeps the whole
path exercised by `node scripts/validate.mjs` on every run.

Also fills in index.schema.json's `contributes.locales`, which the generator
has been emitting since language packs landed without the schema knowing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n sync

The starter no longer declares an icon. A template that ships one hands every
new plugin a placeholder to forget to replace, and the fallback it was standing
in for is better anyway — Studio draws a monogram from the plugin's name, which
is a fine place for a plugin to stay.

That left the icon rules with nothing in the repository exercising them, so
they get their own tests (`node --test scripts/lib/*.test.mjs`, no dependency,
wired into CI ahead of the validator). Without them the port would be code CI
never runs until the first contributor's plugin is accepted or refused by rules
nobody has executed.

Also regenerates index.json, which has been stale since the Steam achievements
plugin landed without it — `generate-index.mjs --check` runs in CI, so the
registry job has been failing on develop since that merge.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… nobody can check

The plugin's Rust half had never been compiled and its manifest carried six
placeholder sha256 digests, so every game build failed on a file that was not
there. The stated reason — that the Steamworks SDK needs a Valve partner
account — is not true: steamworks-sys vendors the SDK under its own lib/steam/
and falls back to it whenever STEAM_SDK_LOCATION is unset. So the bridge now
builds with a Rust toolchain and nothing else, and contributes.buildDependencies
is gone: the shared library ships beside the executable, copied out of cargo's
own output so the two can never drift apart.

Verified against a live Steam client on windows-x64 with Spacewar (App ID 480):
init reports the real App ID and language, an unknown API name is refused by
Steam rather than silently succeeding, and unlock / setStat / indicateProgress /
resetAll all land through StoreStats. Studio's own artifact compiler stages the
package into a preview compile with both digests matching.

Digests are now computed, never authored. A sidecar target claims that a package
carries certain bytes, and that is only true of a package that has the binary —
which this repository does not, they are build output. So manifest.json declares
no sidecar at all; sidecar/contribution.json holds what is not a property of a
compiled artifact, and build.mjs includes each platform whose files are present,
hashes them, and writes the block into dist/manifest.json. With no binaries at
all it emits a mirror-only package: every node still works, nothing reaches
Steam. That shape is what CI packages on every push, because it is the one that
must never break.

Also in the TypeScript half:

- narraleaf-studio ^0.2.0 -> ^0.5.0. The plugin was written against APIs that
  version predates, so `yarn typecheck` had failed since the plugin landed and
  CI has been red ever since. It is green now, with 0 errors.
- Join the freeze/reload contract. The store mutated memory before awaiting a
  write that a frozen project discards, and never re-read after a restore — so
  the author's next edit wrote pre-restore memory over the version they had just
  restored. commit() now bails before touching memory, every writing control
  goes through FreezeGuard, and the store registers a reloader.
- The Steam App ID field wrote the whole catalog to disk on every keystroke; it
  is a DraftInput like every other field now.
- 29 tests for the catalog's pure half, and a plugin thumbnail.

package-plugin.mjs validates dist/manifest.json too, not just the committed one:
the shipped manifest is now generated, and generation was the one part of the
package nothing checked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@helloyork
helloyork merged commit 629de09 into master Aug 2, 2026
8 checks passed
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