.opencode/skills/add-autogate/SKILL.md
Autogates enable gradual rollout of risky code changes independent of binary releases. Unlike compatibility flags (which are permanent, date-based behavioral changes), autogates are temporary gates that can be toggled on/off via internal tooling during rollout, then removed once the change is stable.
| Use an autogate when... | Use a compat flag when... |
|---|---|
| Rolling out a risky internal change gradually | Changing user-visible behavior permanently |
| You need a kill switch during rollout | The change is tied to a compatibility date |
| The gate will be removed once stable | Users need to opt in or out explicitly |
Autogates and compat flags are separate mechanisms — an autogate does not become a compat flag.
Edit src/workerd/util/autogate.h. Add a new entry to the AutogateKey enum before NumOfKeys:
enum class AutogateKey {
TEST_WORKERD,
// ... existing gates ...
// Brief description of what this gate controls.
MY_NEW_FEATURE,
NumOfKeys // Reserved for iteration.
};
Naming convention: SCREAMING_SNAKE_CASE for the enum value.
Edit src/workerd/util/autogate.c++. Add a case to the KJ_STRINGIFY switch before the NumOfKeys case:
kj::StringPtr KJ_STRINGIFY(AutogateKey key) {
switch (key) {
// ... existing cases ...
case AutogateKey::MY_NEW_FEATURE:
return "my-new-feature"_kj;
case AutogateKey::NumOfKeys:
KJ_FAIL_ASSERT("NumOfKeys should not be used in getName");
}
}
Naming convention: kebab-case for the string name. This string is what appears in runtime configuration (prefixed with workerd-autogate-). The enum name and string name should match to avoid confusion.
Use Autogate::isEnabled() to conditionally execute the new code path:
#include <workerd/util/autogate.h>
// At the point where behavior should change:
if (util::Autogate::isEnabled(util::AutogateKey::MY_NEW_FEATURE)) {
// New code path
} else {
// Old code path (keep until gate is removed)
}
Three ways to test autogated code:
A. The @all-autogates test variant (automatic):
Every wd_test() and kj_test() generates a @all-autogates variant that enables all gates. If your feature is tested by existing tests, they'll automatically run with the gate enabled:
just stream-test //src/workerd/api/tests:my-test@all-autogates
B. Targeted C++ test setup:
In a C++ test file, enable specific gates:
#include <workerd/util/autogate.h>
// In test setup:
util::Autogate::initAutogateNamesForTest({"my-new-feature"_kj});
// In test teardown:
util::Autogate::deinitAutogate();
C. Environment variable:
Set WORKERD_ALL_AUTOGATES=1 to enable all gates when no explicit config is provided.
just build
just stream-test //path/to:my-test@ # Old behavior (gate off)
just stream-test //path/to:my-test@all-autogates # New behavior (gate on)
Once the human user explicitly confirms that the feature is stable and fully rolled out:
AutogateKey enum value from autogate.hcase from KJ_STRINGIFY in autogate.c++Autogate::isEnabled() checks, keeping only the new code pathAutogateKey in autogate.h (before NumOfKeys)KJ_STRINGIFY in autogate.c++Autogate::isEnabled()@all-autogates test variant passes| File | What to do |
|---|---|
src/workerd/util/autogate.h | Add enum value with comment |
src/workerd/util/autogate.c++ | Add case to KJ_STRINGIFY |
| Your feature file(s) | Guard code with Autogate::isEnabled() |