Contributing
Development setup, change guidelines, and architectural boundaries.
Thanks for improving pixelkiln. The project is deliberately conservative around paid generation, file ownership, and provider state: a convenient failure must never widen a paid run, overwrite hand-edited art, or lose the identity of work that already cost money.
Development setup
Requires Node.js 20 or newer.
npm ci
npm run typecheck
npm run test:docs
npm test
npm run build
npm run test:package
The Next.js marketing/documentation site is isolated in website/:
npm ci --prefix website
npm run website:check
npm run website:lint
npm run website:build
npm run website:dev
The site reads canonical Markdown from docs/ and the root policy files at
build time. Do not create a second documentation copy under website/.
Website sprites are generated from website/art/pixelkiln.manifest.json.
Treat those requests as paid work: run plan and audit first, use an explicit
hard budget, and commit the manifest, lockfile, and reviewed outputs together.
The Review UI showcase should be captured at 1280×720 only after every asset
loads. Give a replacement capture a new public filename and update the page
reference so deployed image caches cannot retain the old version.
Run the source CLI without a global install:
npm run pixelkiln -- help
npm run pixelkiln -- plan --manifest examples/minimal/pixelkiln.manifest.json
Tests must not require a live provider account or API key. Use FakeProvider
for pipeline behavior and mocked HTTP responses for PixelLab or Retro Diffusion
wire contracts.
Change guidelines
- Start from
mainand keep one coherent change per pull request. - Use conventional commit subjects (
feat:,fix:,docs:,refactor:,test:,chore:). Semantic Release derives versions from them. - Scope website-only work as
chore(website):.website/is not in the packagefilesallowlist, so nothing under it can reach the published tarball. Semantic Release cannot see that, and afeat(website):subject cuts a minor release whose contents are byte-identical to the one before it. That happened once already: 0.2.0 is a favicon and an Open Graph image. Anywebsitescope is also refused a release byreleaseRulesin.releaserc.json, so a slip is caught rather than published, but the right subject keeps the changelog honest. - Add regression coverage for bug fixes and behavior coverage for new public options or exports.
- Update README/help text and focused docs in the same change as user-facing behavior.
- Keep README as the concise product landing page. Put durable reference and
workflow detail in
docs/, and link every guide fromdocs/README.md. - Run
npm run test:docsafter changing Markdown or the CLI command/flag surface. It checks local links, the docs index, README size, and CLI coverage. - Run the website lint and build checks when changing
website/or the canonical Markdown it renders. - Run the package smoke test when changing exports, build configuration, the
executable, package metadata, or the
filesallowlist.
Architectural boundaries
- Provider-specific URLs, authentication, response schemas, and quirks belong
below
Provider. - Planning stays offline. Provider
supports()andestimate()must perform no I/O and estimates must carry their cost unit. - Provider responses are untrusted input and need runtime validation before entering the lockfile.
- Lockfile writes must remain atomic and resumable. Additive defaults should preserve valid v2 files when extending lock entries.
- Unknown flags and ambiguous output selection are errors. Silent widening is especially dangerous when a command can spend provider quota.
- Never overwrite a generated file whose current bytes differ from its recorded hash, and never make deletion an implicit side effect of recovery/triage.
Pull requests
Describe the user-visible outcome, any compatibility impact, and the exact
checks run. Keep generated dist/ files out of commits; packaging builds them
from source. If hosted CI is unavailable, include the local Node version and
full check results in the PR description.
Security-sensitive findings should follow SECURITY.md, not a public issue with exploit details.
Releases
Merging to main is the release. Semantic Release derives the version from the
conventional commit subjects in the range, publishes to npm, tags the commit,
writes the GitHub release, prepends to CHANGELOG.md, and commits the changelog
and version back to main with [skip ci]. Contributors do not run anything.
There is no npm token. Publishing authenticates over OIDC trusted
publishing: the workflow grants id-token: write, npm exchanges that for a
short-lived credential, and the npm CLI performs the exchange itself during
npm publish. A side effect worth keeping is that every release carries a
signed provenance attestation linking the tarball to its source commit and
workflow run.
Two conditions have to hold on the npm side, and neither lives in this repository:
- the package must exist on the registry, and
- it must have a Trusted Publisher entry naming this repository and the
release.ymlworkflow.
When one is missing, the token exchange reports `404 OIDC token exchange error
- package not found
, and Semantic Release then falls through to token auth and fails withEINVALIDNPMTOKEN`. That 404 does not necessarily mean the package is absent. It reads identically when the package is published but has no Trusted Publisher entry, which is the more likely cause once a release has ever succeeded. Check the entry before doubting the publish.
Because the exchange cannot authenticate against a package that does not exist
yet, 0.1.0 was published by hand to bootstrap that trust, and its changelog
section was written by hand for the same reason. Every release from 0.2.0
onward is automated.
A failed release opens an issue labelled semantic-release, which the next
successful run closes. That label must exist in the repository or the reporting
step itself fails with a validation error and hides the original failure.
See the website scoping rule under change guidelines for the one commit convention that changes whether a release happens at all.