When we moved Claude onto Agent Bridge's shared ACP runtime, most of the work looked like a provider integration problem.
Sessions had to resume. Models and modes had to map correctly. Cancellation had to work. Repository instructions still had to be available. The new runtime needed to look, from the outside, like the Claude integration it was replacing.
Then we found a more important problem.
Claude could read permission rules from its own settings files. Agent Bridge also had a permission boundary. That meant the same tool call could be governed by two systems.
Both systems made sense on their own. Together, they made authority ambiguous.
Two reasonable permission systems make one unreasonable boundary
Agent Bridge has to know what authority a Run has.
That matters because a message may arrive from Telegram or Discord, because different execution modes deliberately have different limits, and because an unattended Run should not quietly acquire more authority than the Run that created it.
Claude also has a mature permission system. A user can put allow rules in Claude settings files at user, project or local scope. In a normal Claude Code session, that is useful.
The problem appeared when we composed the two.
If a project-level Claude setting could pre-authorise a tool before Agent Bridge's ACP permission callback saw it, then Agent Bridge was no longer the final authority for a Run. The Bridge could believe it had constrained the agent while a provider-specific settings file had already said yes.
That is a particularly unpleasant class of security bug because nothing has to look broken.
The tool works. Claude is behaving exactly as configured. Agent Bridge is behaving exactly as configured. The failure is in the relationship between them.
We fixed the Claude path by disabling file-backed Claude setting sources for the ACP session and leaving tool decisions with the Bridge permission boundary. Claude still does the work. Agent Bridge decides what that particular Run is authorised to let it do.
That sounds obvious after the fact. It was not obvious while integrating two individually sensible systems.
Configuration and authority are different things
I think I had been treating provider configuration as mostly additive.
If Claude has a useful setting, preserve it. If Agent Bridge has a useful policy, preserve that too. The integration job is to make both sets of behaviour survive the migration.
Permission is different.
There are parts of a system where composition is the feature. Repository instructions are one example. We still wanted Claude to inspect the project's CLAUDE.md, AGENTS.md and referenced files. We added Bridge-owned repository grounding so that information remained available through the normal tools.
But authority does not compose so casually.
At the point where an irreversible or privileged action is allowed or denied, somebody has to own the answer.
The same principle has shown up elsewhere in Farstax. The Platform owns workspace infrastructure. The agent owns the software inside the workspace. Provider credentials stay with the boundary that can actually use them. We keep ending up with cleaner systems when the final decision has one owner and the other layers provide evidence or capability rather than another competing veto or bypass.
The tempting fix was reconciliation
There is a more elaborate architecture available here.
We could have imported Claude's settings, merged them with Agent Bridge policy, defined precedence rules, explained conflicts in the UI and maintained an adapter as both systems evolved.
That would have looked thorough.
It would also have created a new permission language whose main purpose was reconciling two existing permission languages.
Instead, the fix was subtraction. For a Bridge-owned Run, provider-local allow rules do not get to expand Bridge authority.
That does not mean provider settings are bad. It means their authority has a scope. A direct Claude Code session can have one owner. A Claude session being driven as part of an Agent Bridge Run has another.
The important part is being explicit about which boundary we are in.
This changed how I review integrations
I now look for authority collisions before I look for missing features.
When we add a provider, connector or runtime, I want to know which side owns authentication, which side owns the session, which side can approve tools, which side persists state and which side decides that an operation is complete.
If the answer to one of those questions is "both", I am increasingly suspicious.
Sometimes two layers genuinely need checks. Defence in depth is useful. A provider can reject an invalid request after Agent Bridge has authorised it. The operating system can deny a filesystem operation even if the agent asked for it.
But that is different from having two policy engines independently capable of granting the same authority.
A useful distinction is that several layers may say no. Only one layer should be able to say yes on behalf of the product boundary.
What I am watching next
The difficult cases are the ones where the provider's native safety model is richer than ours.
Throwing that model away would be foolish. Duplicating it would be worse. The work is to preserve useful provider capability without accidentally handing the final authority back and forth depending on which code path happened to run.
For now the rule I trust is simple:
At every consequential boundary, one system has to own permission.
The other systems can constrain it further. They can supply context. They can fail closed. They should not quietly widen it.
That is less flexible than merging every available setting.
It is also much easier to know who said yes.