npm Staged Publishing, Copilot PR Approvals, and the Rule That Decides Which Way the Gate Swings
Two GitHub changelogs, 48 hours apart, moved the approval bit in opposite directions. The variable that separates them is not how good the model is.
On September 1, GitHub shipped a feature that lets Copilot approve a pull request in a way that counts toward the repository's required-approvals rule. On September 3, the same company shipped a set of npm publishing changes whose stated recommendation is to keep every trusted publishing configuration staging-only, so that a compromised workflow "can't push straight to the registry," and which disable the approval button in the staged queue while malware scanning is still running.
Two changelogs. One company. Forty-eight hours. In one, an agent gains a signature. In the other, a human is inserted into a path that used to be automatic. Neither post mentions the other, and the obvious reading is that a big organization shipped two things from two teams and nobody noticed the tension.
I do not think that is what happened. I think both decisions are correct, and the rule that makes them both correct is sitting in plain sight in npm's own documentation, in a policy page nobody reads.
The stakes: everyone is asking the wrong question
The debate about agent permissions is almost always framed as a capability question. Can the model be trusted? Is it good enough yet? Should we wait for the next version? That framing feels rigorous and it produces terrible policy, because it has no stopping condition. There is no benchmark score at which "trust it" becomes obviously true, and every team draws the line somewhere different based on vibes and recent incidents.
The two GitHub releases suggest a different question, and it is one you can actually answer for any given action in about ten seconds: if this goes wrong, what does it cost to undo?
That is a property of the action, not of the agent. It does not move when a new model ships. You can compute it once per action type and put the answer in a config file.
The mechanism: what "undo" actually costs in each case
Here is the part that makes the rule concrete rather than philosophical.
A Copilot approval on a pull request is cheap to undo. GitHub's changelog is specific: when new commits are pushed after Copilot approves, its approval is dismissed exactly as a human reviewer's would be, and you can request a fresh review. Approvals are off by default and configurable at enterprise, organization, and repository level. Repository admins can choose which file paths Copilot is allowed to approve at all. A bad approval that gets merged still lands in a version control system built entirely around reverting things. The cost of the mistake is a revert commit and an awkward retro.
An npm publish is not cheap to undo, and npm says so in unusually blunt language. From the unpublish policy: "Registry data is immutable, meaning once published, a package cannot change." If you have ever published a package called "bob" at version 1.1.0, no other package can ever hold that name at that version. That remains true even after the package is unpublished. You get an unconditional unpublish window of 72 hours, but only if no other package in the public registry depends on yours. After 72 hours you can still unpublish, but only if all three conditions hold: nothing depends on it, it had fewer than 300 downloads in the last week, and it has a single owner. Miss any of those and your remedy is deprecation, which leaves the package downloadable and adds a warning message. And the policy adds one more line worth reading twice: "Once you have unpublished a package, you will not be able to undo the unpublish."
So the two decisions line up perfectly. Reversible action, agent may hold the signature. Irreversible action, a human holds the signature, and the button is disabled until the evidence arrives.
That last detail is my favorite thing GitHub shipped last week, and it is the smallest. In the staged publishing queue, the approve button is now greyed out while malware scanning runs, with the page polling status every minute. They did not add a warning. They did not add a confirm dialog. They removed the ability to approve before the evidence existed. That is what a gate is supposed to do, and almost nobody builds them that way, because building them that way means admitting your users will click through anything you let them click through.
Where this rule comes from, and who else is using it
Cloudflare shipped the third instance of this on the same September 3. Its new Vulnerability Discovery and Remediation service, in invitation-only early access inside Managed Defense, runs OpenAI's GPT-5.6 Cyber for reconnaissance, hunting, and validation against customer-authorized code. The architecture section reads like a list of denials. No model inference runs at Cloudflare's edge. Every tool call is logged and checked against the investigation's access policy before it runs. Every proposed patch or WAF rule has to pass checks implemented outside the model, and a failed check kills the workflow before a human ever sees the proposal. And the line that matters: the model "cannot apply any patch or rule it proposes."
Applying a WAF rule to production traffic is not reversible in the way a git revert is reversible. The window during which the wrong rule was live is gone. So the agent proposes and does not apply, and that is a permission boundary rather than a policy statement.
There is a second Cloudflare sentence I have been quoting to people all week, which belongs to a slightly different problem but comes from the same instinct. The harness "treats source code, logs, and request metadata as evidence to inspect, rather than instructions to follow." That is prompt-injection defense expressed as an architectural property of the system instead of a paragraph in a system prompt, and unlike the rest of the product, it costs nothing to adopt in a harness you already own.
Put this into practice
You do not need a security team or a new vendor to use this. You need a list and about an hour.
Step one: inventory the actions, not the tools. Open whatever config governs your agent's permissions and ignore the tool names. Write down what the agent can actually cause to happen in the world. Not "Bash" but "run a command that can hit the network." Not "GitHub MCP" but "merge a pull request," "create a release," "close an issue." Tool-level permissions hide the thing you care about, because one tool spans a dozen actions with wildly different consequences.
Step two: score each action with one question. If this fires wrongly at 3am and nobody notices until morning, what does it cost to undo? Sort into three buckets.
- Free to undo. A revert, a re-run, a deleted branch, a dismissed approval. Let the agent do it unattended.
- Expensive to undo. A message sent to a customer, a record updated in a system with no history, a deploy to staging. Let the agent propose it and require a signature, but do not make the signature ceremonial.
- Impossible to undo. A package published, a payment sent, a production DNS change, an email that has left the building, a row hard-deleted. Human hand on the button, every time, no exceptions for convenience.
Step three: for anything in bucket three, disable the button until the evidence lands. This is the npm move and it is the one people skip. If your approval step is a button that is always clickable, you have built a formality. Make the approve path structurally unavailable until the check has finished: tests green, scan complete, diff rendered, cost estimated. The point is not to inform the human. The point is to remove the option to skip the reading.
Step four: adopt the evidence-not-instructions rule in your own harness today. Anything your agent reads from the outside world, meaning fetched pages, tool output, file contents, log lines, issue bodies, is evidence about the world. It is never a source of instructions. If your architecture cannot express that difference, and most cannot, the nearest approximation is to wrap untrusted content in a delimiter your system prompt names explicitly and to route anything that looks like an instruction inside that content to a refusal. Weak, but better than the nothing most harnesses ship with.
Step five, if you publish anything to npm: add one trusted publishing configuration per release track and leave direct publishing off on all of them. Configurations are independent and additive, a publish is authorized if the incoming OIDC token matches any one of them, and evaluation order is not guaranteed, so do not build logic that depends on which configuration matched. This is the cheapest supply-chain hardening available to a maintainer right now and it costs one click per release.
Honest limitations
Reversibility is not always knowable in advance. "Send a Slack message" looks reversible because you can delete it. It is not, because someone read it. "Update a CRM record" looks reversible if the system has history and is not if it does not. You will get some of these wrong, and the failure mode is that you discover an action was in bucket three only after it fires. Budget for re-sorting the list after every incident.
A gate that is always in the way gets removed. This is the honest risk in npm's own design. Staging inserts a person into every publish, which means a release train that fired on tag push is no longer automatic. I would bet money that a meaningful number of maintainers turn direct publishing back on within a month, and the ones who do will not be careless people. They will be people shipping four times a day who found the friction genuinely worse than the risk. If you install a bucket-three gate on something that fires twenty times a day, you have not made it safer. You have scheduled its removal.
The whole framework assumes a human who reads. This is the assumption that a benchmark submitted to arXiv on September 4 puts a number on, and the number is not comforting. τ^τ-bench makes agent construction itself the task: a developer agent is handed the records a business keeps, a client holding requirements, a production API, a codebase to inherit, and limits on serving cost and models, then has to deliver a working customer-service agent scored against held-out simulated users. Across 53 tasks in four domains, the strongest configuration tested, Claude Opus 5 under Claude Code, passed 23.9 percent of evaluation simulations. The expert-authored reference ceiling scored 82.2 percent. The failures the authors report are not coding failures. They are shallow queries instead of comprehension, almost no communication back to the client who holds the requirements, and too little exploration of architecture and serving spend before shipping the first design that runs. Those are exactly the judgments a signature is supposed to represent.
And I have not tested Copilot's approval feature at scale. It is in public preview, it is off by default, and my read of the risk comes from the design and from the changelog rather than from six months of watching a team live with it. The specific thing I would watch for, and cannot yet prove, is the approval assessment line: every Copilot review now carries a read on whether the pull request is ready, whether or not an admin has enabled actual approvals. That text will function as an approval for a tired reviewer at 6pm regardless of what the documentation says about it counting for nothing.
What to do with this
Go find the single most irreversible thing your agent can do right now. Not the scariest sounding one, the least undoable one. Then check whether the approval step in front of it is a real gate or a button that is always clickable.
If it is the second kind, you have not built a control. You have built a place to put the blame.
Sources: npm trusted publishing changes, Copilot code review approvals, npm Unpublish Policy, Cloudflare Vulnerability Discovery and Remediation, τ^τ-bench, arXiv 2609.04611.
Medium metadata
- Title: npm Staged Publishing, Copilot PR Approvals, and the Rule That Decides Which Way the Gate Swings
- Subtitle: Two GitHub changelogs, 48 hours apart, moved the approval bit in opposite directions. The variable that separates them is not how good the model is.
- Tags: AI Agents, Software Development, Devops, Security, Github
- Suggested kicker image: a single greyed-out approve button
- Canonical: import from the fervorai.dev URL after publish