Start with the narrow test that owns your change, add integrity suites when D1 and R2 can diverge, and finish with the repository gates. If the public contract moved, also drive the affected path through the isolated local harness. This workflow applies to operator-fork changes and owner-requested upstream patches because each test layer should prove only the behavior it can actually observe.
Develop and verify
Each layer proves a different claim
No single test environment covers Energon’s whole contract. Node unit tests are fast and isolate helpers. Worker tests exercise routing with D1 and R2 bindings. Failure injection forces rare rollback branches. Mock-free mutation suites validate real binding behavior. Page tests protect DOM contracts. The verify harness starts a real local Worker and observes it as a human or agent would. For project-specific mutation and identity terms, use the glossary.
1. Set up local state
Run local migrations before starting the development server. The checked-in example identity defaults to dev@example.com; localhost skips Cloudflare Access.
npm install
npx wrangler d1 migrations apply energon --local
cp .dev.vars.example .dev.vars
npm run devDo not attach verification to an unknown process on port 8787, delete .wrangler/state, or kill Wrangler/workerd by process-name pattern. That state and process may belong to another local session.
2. Start with the owning test
| Change | First test |
|---|---|
Pure helper under src/ | npm run test:unit -- test/unit/NAME.spec.ts |
helpBody, /llms.txt, Markdown HTML | npm run test:unit -- test/unit/golden.spec.ts |
openapi/v1.json, /v1 routes, or API error codes | npm run test:unit -- test/unit/openapi-drift.spec.ts |
| Templates, renderer, instance skill | npm run test:unit -- test/unit/skill-render.spec.ts |
| Hub/tokens/setup/about/stats HTML or client JS | npx vitest run test/pages.spec.ts |
| Router, host rules, account API | npx vitest run test/routes.spec.ts |
| Publish/read/delete/list/auth DB/gate/Markdown | npx vitest run test/api.spec.ts |
| Loose-file write, rename, or delete failures | npx vitest run test/files.spec.ts |
| Site import/copy/delete rollback | npx vitest run test/site-integrity.spec.ts |
| Expiry and write-claim races | npx vitest run test/api.purge-claim.spec.ts |
For example, a catalog cursor change begins with:
npm run test:unit -- test/unit/catalog.spec.ts
npx vitest run test/api.spec.tsDo not run the full suite after every small edit. Focused tests make the edit loop useful; the full gate still runs before handoff.
3. Prove D1/R2 integrity twice
An integrity change needs both controlled failure and real-binding convergence evidence.
Use failure-injection suites to force the precise branch:
test/files.spec.tsfor loose-file R2/D1/cache failures.test/site-integrity.spec.tsfor import, duplicate, path delete, and whole-site restore behavior.test/api.purge-claim.spec.tsfor claim acquisition, stale reclaim, PATCH races, and purge ordering.
Then run the relevant mock-free suite:
test/d1-r2-mutation.spec.tsfor rename/delete catalog, keys, and quota.test/d1-r2-claim-mutation.spec.tsfor claim rollback and competing replacements.test/d1-r2-site-mutation.spec.tsfor site PUT/import/delete abort recovery.test/d1-r2-scheduled-mutation.spec.tsfor sweep boundaries and stale claims.
npx vitest run test/files.spec.ts test/d1-r2-mutation.spec.ts test/d1-r2-claim-mutation.spec.tsThe injected suite proves your recovery logic is reachable. The mock-free suite proves the same invariant with actual local D1/R2 semantics. Do not treat one as a substitute for the other.
4. Change schema in all three places
Add a new numbered migration, update runtime bootstrap/legacy upgrades, and update src/schema.sql. Create indexes only after every referenced column exists.
npx wrangler d1 migrations apply energon --local
npm run test:unit -- test/unit/db.spec.ts test/unit/schema-drift.spec.ts
npx wrangler types
npm run typecheckNever rewrite old migrations. Comments in older migrations about stamping d1_migrations are historical human recovery notes for a verified deploy-first state. They do not permit agents to stamp migration history, run production d1 execute, or improvise repair SQL. For a production duplicate-column incident, stop and use the human-only upgrade and recovery procedure.
5. Keep the agent protocol coherent
When a /v1 route, field, header, error, limit, or decision rule changes, review these together:
- Canonical HTTP schema in
openapi/v1.json. - Runtime JSON in
src/auth.tshelpBody. - Runtime plain text in
src/llms.ts. - Agent workflow and reference under
templates/skill/. - Runtime golden fixtures under
test/golden/. - Rendered plugin output under
plugins/.
npm run test:unit -- test/unit/openapi-drift.spec.ts
UPDATE_GOLDENS=1 npm run test:unit -- test/unit/golden.spec.ts
npm run skill:render
npm run test:unit -- test/unit/skill-render.spec.ts
git diff -- openapi test/golden templates pluginsThe drift test compares OpenAPI operations with the router and help.routes, checks application error codes, and protects the unauthenticated discovery boundary. Review the remaining diff semantically: separate snapshots can all pass while two documents disagree about a method or field. For example, loose-file rename is implemented as PUT /v1/files/{id} with X-Filename; PATCH changes password, TTL, or write policy and does not rename. If a verification recipe says otherwise, update the recipe rather than documenting stale behavior.
The renderer’s outputs can be summarized as the instance plugin tree plus real-host marketplace catalogs and updated instance identity. Do not hand-edit plugins/{name}/, and do not install the skill under this repository’s .agents/skills or .claude/skills directories because those locations autoload it while you are changing the Worker.
6. Pair page contracts with user-path proof
test/pages.spec.ts checks signed-in page structure, expected copy, navigation, and that every JavaScript element lookup has matching markup. It does not judge pixels, responsive behavior, or whether a person can complete the flow.
The .cursor/skills/verify-energon/ harness complements it by launching an isolated Wrangler instance with real local D1/R2, proving process and port ownership, and driving HTTP or browser behavior. Use both when a hub control or workflow moves.
export ENERGON_VERIFY_RUN="contributor-check"
export ENERGON_VERIFY_PORT="18787"
.cursor/skills/verify-energon/bin/launch
.cursor/skills/verify-energon/bin/doctor
.cursor/skills/verify-energon/bin/mint-token contributor-checkAfter Doctor succeeds, follow the matching file under .cursor/skills/verify-energon/features/. Save the request/status/body and a second read-only view. Browser drives also require a screenshot showing the signed-in identity and resulting catalog state. The harness calls the real local Worker; do not use SELF.fetch or direct binding access as user proof.
Cleanup targets only this run’s process group, port, and isolated persist directory, while preserving evidence:
.cursor/skills/verify-energon/bin/cleanupIf a named route, public URL shape, header, token prefix/environment, element id, ARIA label, or proof string changes, update the feature map in the same change. A stale map is fixed to match verified runtime behavior; it is never treated as authority over the product.
7. Run the full gate
Before handoff, schema/shared-type changes always receive the full gate. It is also the safe final check for any coherent change:
npx wrangler types
npm run typecheck
npm run lint
npm test
npm run fuzz:regression
npm run skill:render -- --checkCI uses Node 22 and runs npm ci, Wrangler type generation, typecheck, lint, unit tests, ZIP fuzz regression, and Worker tests. Production deployment is a separate opt-in job after tests; contributors must not run deployment or remote database commands as part of ordinary verification.
For ZIP parser changes, fuzz:regression replays the committed corpus. A local coverage-guided campaign is npm run fuzz:zip -- -max_total_time=60; minimize a crash, add it to the committed corpus, and add a focused unit regression with the fix.
Verification failures
Do not call a change verified because one layer is green. Persistence work needs injected and mock-free suites; UI work needs page contracts and a user-path drive; /v1 work needs the OpenAPI drift test plus semantic review across runtime docs, templates, goldens, and rendered output. Never invent a token, touch production D1 state, reuse the default local database for verification, or conceal a product failure by editing the feature map.
Report or hand off
For an upstream bug, open an issue with reproduction evidence and no secrets. Do not open an unsolicited pull request. For a vulnerability, use private reporting and include the tested revision, reproduction, and impact without tokens, credentials, passwords, customer content, or other people’s email addresses.
When handing off an authorized code change, state which focused tests ran, which cross-layer invariants they cover, whether the user path was driven, and the full-gate result. Read Architecture and integrity when deciding which boundaries changed, Contribute to Energon for the repository policy, and Security model before reporting an auth or sharing defect.