Back to Ideas 4 min read

The Gate Always Asks

E
EkoHacks Team
·
The Gate Always Asks

Encoding the release, part 4 of 8.

Automating a release means automating things you cannot take back. A merged PR, a published GitHub Release, an approved deployment that ships to npm — none of these un-happen. The interesting design question was never how to automate them; it was which of them a machine should be allowed to do without looking a human in the eye.

Our answer has three parts.

Every irreversible step pauses. Before the merge, the command states what it is about to do and waits: merge pr #198? (y/n). Before the GitHub Release: cut release v0.4.1? (y/n). Before approving the deployment gate: approve the release gate for run #16? (y/n). The pause is not ceremony — it is the last moment at which stopping costs nothing. Answer n and the rail stops with a named reason, leaving a state the next preflight can assess.

--yes exists, but it is not a skeleton key. Run ekohacks release 0.4.1 --yes and the merge and Release pauses answer themselves — appropriate for the steps that are pure rail once CI is green. The gate is different. The gate is the moment the package actually ships to the registry, the step our manual process always reserved for a maintainer's explicit approval. So the rule is absolute: the gate always asks.--yes does not reach it. Nothing does.

The rule is pinned by a test, not a code review. In the suite there is a test whose whole job is to answer "no" at the gate and then assert that the approvals tracker is empty:

const result = await runShip({ gh, confirm: () => Promise.resolve(false) });

expect(result).toEqual({ stopped: 'gate not approved' });
expect(approvals.data).toEqual([]);

And a second one that runs the entire chain with --yes and asserts the only question ever asked was the gate's. These are the most valuable tests in the repository, and neither tests that something works. They test that something refuses to happen. If a future refactor ever lets an approval sneak past a "no", or lets --yes leak into the gate, the suite fails before the code reaches main.

Mechanically it is all very boring, which is the point: the confirm is an injectable function, the policies take it as a parameter, and the chain decides which pauses --yes may auto-answer. No framework, no prompt library — a function returning a promise of a boolean, and a design decision about who gets to call it.

The human decision was the first requirement we wrote down for this stage, before any code: the one human decision stays human. Everything else in the series is machinery for deleting toil. This part is the machinery for keeping the judgement.

Next: One Command, Three Policies, Zero Duplication.

E

Written by

EkoHacks Team

More from Ideas

·6 min read

What the Nullable Gave Back

One file, seven behaviours held fixed, the database swapped for a Nullable: about 180 times less time inside the tests, and coverage flat to two decimals.

E
EkoHacks Team
·6 min read

Twenty Six More Tests, Four Fewer Behaviours

Removing the mocks grew the suite from 44 tests to 70 and quietly deleted four behaviours, every one of them a failure path. Test count is not coverage.

E
EkoHacks Team
·6 min read

The Best Coverage Number in the Room

Same commit, same spec, same test count. The mocked suite ran 5.6 times faster, covered 3.5 fewer points of real code, and posted the best branch coverage.

E
EkoHacks Team