Found while applying maintainer guidance on PR #510 (dropping priority: 100 from openaiUpstreamPreset()). Filed without neutral:fix because the only fixes change a documented ownership rule, so it wants a decision rather than a worker.
The seam
When an operator configures an openai upstream in TOML, config wins the name outright and the merged entry carries no match() - so the x-hypaware-upstream steering rung disappears along with the preset, and a steered /chat/completions routes nowhere.
Verified directly: with a configured openai, the compiled table is byte-identical at priority: 100 and at the default 0. The preset never reaches the table at all, so this is pre-existing and priority-independent - not a regression from #510's change, and not something dropping the priority introduces or fixes.
Why it was not fixed in place
The obvious remedy is to let a preset contribute its match() to a same-named config upstream. That changes the operator-owns-config rule (ai-gateway/src/source.js's merge, where config beats a same-named preset), which is a deliberate design decision, not an implementation detail. Silently changing it inside a change set about OpenClaw capture would be the wrong place for that call.
Rather than delete the shape from the tests or paper over it, #510 records it as an explicitly-labelled test asserting current behaviour, and documents it in LLP 0161 §3.4 as out of scope.
Why it matters
The steering plugin sets x-hypaware-upstream to route a shadowed provider's turn through the gateway. An operator who configures their own openai upstream - a reasonable thing to do - silently loses that rung, and the steered turn 404s. It fails the user's request, which is the class LLP 0157 R5 forbids ("the user's turn MUST NOT fail because of capture"), though by a different mechanism than the T6 registration race already analysed on #510.
Options
- (a) Let a preset contribute
match() to a same-named config upstream when the config entry declares none. Narrow, but amends the ownership rule.
- (b) Have the steering plugin detect that its target upstream carries no steering rung and refuse to steer, so the turn falls back rather than 404s.
- (c) Document it as a known operator footgun and surface it in
hyp init / config validation when an openai upstream is declared alongside the OpenClaw client.
- (d) Accept as designed: an operator who configures an upstream owns its routing entirely.
Related: PR #510 (the change set), PR #537 (fix/issue-536, hardening the same slot on master), LLP 0157 R5, LLP 0161 §3.4.
Found while applying maintainer guidance on PR #510 (dropping
priority: 100fromopenaiUpstreamPreset()). Filed withoutneutral:fixbecause the only fixes change a documented ownership rule, so it wants a decision rather than a worker.The seam
When an operator configures an
openaiupstream in TOML, config wins the name outright and the merged entry carries nomatch()- so thex-hypaware-upstreamsteering rung disappears along with the preset, and a steered/chat/completionsroutes nowhere.Verified directly: with a configured
openai, the compiled table is byte-identical atpriority: 100and at the default0. The preset never reaches the table at all, so this is pre-existing and priority-independent - not a regression from #510's change, and not something dropping the priority introduces or fixes.Why it was not fixed in place
The obvious remedy is to let a preset contribute its
match()to a same-named config upstream. That changes the operator-owns-config rule (ai-gateway/src/source.js's merge, where config beats a same-named preset), which is a deliberate design decision, not an implementation detail. Silently changing it inside a change set about OpenClaw capture would be the wrong place for that call.Rather than delete the shape from the tests or paper over it, #510 records it as an explicitly-labelled test asserting current behaviour, and documents it in LLP 0161 §3.4 as out of scope.
Why it matters
The steering plugin sets
x-hypaware-upstreamto route a shadowed provider's turn through the gateway. An operator who configures their ownopenaiupstream - a reasonable thing to do - silently loses that rung, and the steered turn 404s. It fails the user's request, which is the class LLP 0157 R5 forbids ("the user's turn MUST NOT fail because of capture"), though by a different mechanism than the T6 registration race already analysed on #510.Options
match()to a same-named config upstream when the config entry declares none. Narrow, but amends the ownership rule.hyp init/ config validation when anopenaiupstream is declared alongside the OpenClaw client.Related: PR #510 (the change set), PR #537 (
fix/issue-536, hardening the same slot onmaster), LLP 0157 R5, LLP 0161 §3.4.