


Change a Public Server Package That Others Extend Without Breaking Them
Use when changing a publicly released server package that others extend through hooks. It keeps secrets out, keeps callbacks, hooks, and middleware order working, adds new migrations instead of editing old ones, and runs the package's real tests and lint.
To use this AI rule
## Check What Ships Publicly and Keep Secrets Out
Before editing, identify the package's publication target, license, module system, language, runtime role, and synchronization path. Treat every file inside a publicly synchronized package as public release content, even when development happens in a private workspace.
- Never place secrets, license-gated behavior, or internal-only fixtures inside the public package.
- Keep server-only packages headless; do not introduce browser or DOM dependencies.
- Match the package's actual module and language conventions instead of importing patterns from neighboring packages.
- Record direct workspace dependents and assume additional external package consumers exist.
Public distribution increases the blast radius even when only one in-workspace dependent is visible.
## Keep Old Callback Styles and Deep Import Paths Working
Do not silently modernize callbacks into promises or async functions. In mixed-era code, an extension hook may depend on the callback shape even when nearby call sites are synchronous or promise-based.
Before changing an invocation:
1. Enumerate every implementation of the extension point.
2. Compare argument order, return behavior, callback timing, and error semantics.
3. Match the style of the file being changed.
4. Add or update tests for each supported invocation shape.
Do not rename or move internal-looking utility, middleware, or model files until downstream repositories have been searched for deep imports. A source-path import is an implicit public API and can break without a compiler warning.
## Keep Middleware Order and Hook Names Exactly Right
Preserve middleware dependencies explicitly. Authentication or token resolution must run before permission checks when authorization reads the populated user context. Treat ordering changes as contract changes, not formatting cleanup.
For new middleware, copy the package's established factory shape, such as a package-aware factory returning a request handler, and register it at the intended point in the central chain.
Use the existing alteration-hook mechanism as the extension surface. Search all current hook names and implementations before adding a call site. Confirm the exact name, input value, callback or return shape, and default behavior; a misspelled hook can silently return the default.
Document every new extension point where downstream implementers can discover it.
## Add New Migrations Instead of Editing Shipped Ones
Follow established base abstractions instead of inventing parallel patterns. New data models should use the shared model plugin or base shape. New top-level REST resources should extend the common resource base and register routes through the central entry point.
Treat shipped database migrations as immutable and one-way:
- never edit an existing migration after release;
- add a new numbered migration for every later correction;
- make each migration idempotent;
- account for interruption and repeated startup execution;
- verify behavior against both fresh and already-migrated databases.
When downstream packages inject model fields or behavior through hooks, audit those injection points before changing schemas, defaults, serialization, or hydration.
## Find Other Packages a Change Affects and Test Both Sides
Map a change to its consumers before implementation. Coordinate with downstream callers when changing authentication middleware, permission handling, session boundaries, shared utilities, encryption helpers, model hooks, or extension-point signatures.
Treat deeply imported helpers as public APIs. Treat token-versus-session ownership as an architectural boundary. When a value is stored as a reference and hydrated on read, audit both the writer and the dereference processor before changing its representation.
For every cross-cutting edit:
1. Read the relevant architecture, dependency, and known-gotcha notes.
2. Search downstream implementations and call sites.
3. State the contract that must remain true.
4. Test both sides of the boundary.
5. Document any intentional contract change and its migration path.
## Test Values That Cross the VM Sandbox Boundary
Changes to virtual-machine bundles, shims, evaluator instances, resource limits, or error semantics require a full client-server contract audit. Keep host and sandbox shims in parity, rebuild the bundle, and verify that values survive the runtime boundary.
Do not assume live database objects retain identity or prototypes after serialization through an isolated runtime. Test identifiers, references, dates, buffers, errors, and hydrated values in both directions.
Be alert to process-wide host mutation caused by compatibility shims initialized at module load. A fake browser context can create global state even when the package is intended to remain headless.
Treat bundle code excluded from coverage as a known test gap and add targeted execution checks rather than assuming ordinary coverage proves it.
## Check Database Features at Startup and Keep a Fallback
Support multiple database implementations by probing capabilities at startup. Gate advanced collation, lookup pipelines, compound nested-path indexes, and other beyond-baseline features on the probe result, and ship a tested fallback.
Never infer compatibility from a product name or version string when the runtime provides a capability map. Repeatedly reverted changes are evidence that the probe-and-fallback path is part of the contract.
When public-package behavior depends on a feature flag but the package cannot import the private registry, call the established extension hook with the flag's exact key string. Verify the key against the registry and test both enabled and disabled paths. Do not embed private registry logic in the public package.
## Run the Tests, Lint, and Build the Package Really Has
Define “green” from scripts the package actually provides. For a JavaScript-only package, this may mean the complete test suite and linter, without inventing a type-check step that does not exist. Run the build when the change affects bundled runtime output.
Register every new test file in the suite's central importer when discovery is not automatic; an unregistered test file provides no protection. Use the supported test-name filter to narrow investigation, but run the complete required suite before completion.
Treat auto-fixing lint commands carefully and review their diff. Confirm that tests, lint, and the relevant bundle build pass, and preserve warnings that reveal runtime or compatibility problems.
## Confirm the Full Impact Before Calling the Change Done
Start with the affected contract, not the nearest file. Search known gotchas and downstream dependencies, make the smallest compatible change, and verify public-release safety before reporting completion.
The final review must confirm:
- no secret, gated, or internal-only material entered the public package;
- callback, hook, deep-import, and middleware-order contracts still hold;
- migrations are additive and idempotent;
- model, reference, VM, and database capability boundaries were tested when affected;
- new tests are registered and the package's real quality gates pass;
- cross-package callers and documentation were updated where required;
- a breaking or major release change is coordinated with all publication targets.
Do not call a change isolated merely because workspace dependency counts are small; public consumers remain part of the risk model.
