Currently running v2.0.1984
🐛 🐛 Fixes
- The last floating tag in the build chain is pinned too. The multi-architecture jobs spin up their builder from a rolling channel tag; that builder is now pinned to a fixed version, with a matching update rule so the bump still arrives as a reviewed merge request instead of freezing in place. Completes the toolchain pinning — no part of the image build now changes underneath an unattended nightly run.
🐛 🐛 Fixes
- Release numbering only ever moves forward. When two changes are merged at nearly the same moment, the release rendered second is now numbered above the one already published rather than possibly below it — keeping version order, changelog order and update checks consistent.
- Release numbering stays reliable even with an unusual tag in history. A version tag that does not follow the numeric scheme can no longer disable the ordering guarantee for its release series, and a release that cannot be ordered is now reported instead of passing quietly.
🐛 🐛 Fixes
- The whole CI docker toolchain is pinned, not floating. Every job that talks to a docker daemon now runs a pinned client and daemon instead of a rolling tag, so an upstream release cannot change the build toolchain unattended overnight — which is exactly how the nightly image builds broke. Renovate proposes the version bumps so they arrive in a reviewed merge request. The three unattended jobs that could previously fail invisibly now alert the operators.
📝 🔧 Improvements
- The Branding and Welcome pages move into their own modules. Both now live in dedicated modules, verified to leave every page byte-identical, continuing the split of the admin surface into self-contained sections.
📝 🔧 Improvements
- The Tokens and Subscription pages move into their own modules. Both admin sections now live in dedicated modules, verified to leave every page byte-identical, continuing the split of the admin surface into self-contained sections.
📝 🔧 Improvements
- The Organization and Overview pages move into their own modules. Both admin sections now live in dedicated modules, verified to leave every page byte-identical, continuing the split of the admin surface into self-contained sections.
🐛 🐛 Fixes
- Image builds push a single manifest again. Every job that builds an image for the internal registry now runs with BuildKit's default provenance attestation switched off, so a push stays one plain manifest instead of an OCI index. A Docker 29.7.0 client refuses to push that index against the registry — verified side by side, 29.6.2 pushed the identical index fine — which had been failing the nightly prototype warm-image builds since 1 August. The setting, the pinned Docker version and a Slack alert on failure now live in one shared job template, so an image job cannot silently miss them.
📝 🔧 Improvements
- Admin pages render substantially faster. The admin panel now reuses work within a page render instead of repeating it: unwrapped data-encryption keys are held briefly in memory, the built-in hook inventory and the registered-user count are memoised, the template engine is shared rather than rebuilt per request, and a request authenticates once instead of twice. On instances using KMS-backed encryption this takes seconds off the Hooks and Organization pages and roughly a second off every other admin page.
🐛 🐛 Fixes
- Hardens the admin render-path caches. The unwrapped-key cache is now scoped to the master key it was authorised under, a failed identity lookup is no longer reused for the rest of a request, and a missing cache backend no longer pins a zero user count.
- Tightens the key-cache lifetime. The unwrapped-key cache now defaults to a one-minute lifetime instead of five, keeping the full speed-up while shortening the window in which a revoked encryption key would still be honoured.
📝 🔧 Improvements
- Faster service detail page. The action-table behaviour on a service's detail page now loads as a cacheable static script instead of being re-sent inside every page render, cutting the page's transfer size by about 23% and letting the browser reuse it across visits.
- The service detail page moves into its own module. Its markup now renders from a template and its routes live in a dedicated module, verified to leave every page byte-identical — the largest step so far in splitting the admin surface into self-contained sections.
- Steadier admin section rendering under the section split. Helper lookups inside the newly split-out admin sections now resolve at call time, so each section always sees the same instance configuration as the rest of the panel.
- The services list moves into its own module. The service inventory page and its status feed now live in a dedicated module, verified to leave every page byte-identical, continuing the split of the admin surface into self-contained sections.
✨ 🚀 Features
- A user is told when their own connection has fallen behind. A token issued before a permission was added to what an install requests keeps working without it — a refresh renews a grant but never widens it, so only a fresh sign-in helps. The admin surface reported this fleet-wide; the person who could act on it found out from a failing call. Their AI client now surfaces it at the start of a session, naming the actions affected. It stays quiet unless another connection to the same service already carries the permission, which is what proves that signing in again will actually obtain it.
🐛 🐛 Fixes
- Saving an action change no longer rebuilds every connector's tool schema. A save touches one service, but the tool schemas and executor specs of all fifty were rebuilt to reach entries identical to the ones already installed. The unaffected ones are now reused, and any doubt — a connector added or removed since the last build — falls back to building everything.
🐛 🐛 Fixes
- Saving an action change is roughly ten times faster. The layer that stamps each action's risk category re-derived it on every save — twenty thousand actions re-matched against every category rule, to arrive at the answer already recorded on the action. It is now derived once, since it depends only on connector data that has not changed. The layer also restores what it displaced before deciding again, so a category whose policy is switched off releases the actions it had escalated.
🐛 🐛 Fixes
- The permission marker on an action row is drawn instead of typed. The warning character came from whatever font the system fell back to, which draws it small inside its em box — raising the font size barely changed what the eye saw. It is now an inline SVG at a fixed size in the row's warning colour.
🐛 🐛 Fixes
- A governance save no longer costs the other container a full catalogue re-parse. Two re-bake paths listened to the same config broadcast with overlapping key sets, so switching one action on made every peer re-read and re-parse all connectors to arrive at a catalogue the cheap in-place pass had already produced. Saves also report their own phase timings now — lock, apply, publish, rebuild — so a slow one can be diagnosed instead of guessed at.
🐛 🐛 Fixes
- The permission marker on an action row is legible and no longer clipped. The warning glyph beside an action name rendered below the readable-text floor, and a narrow column could truncate it away together with the name — leaving a row that looked clean while carrying a finding. It now sits beside the name rather than inside it, at the type scale's meta size.
🐛 🐛 Fixes
- Context Map sync re-scans the configured discovery groups. The manual sync, and saving a changed discovery-group list, now refresh the cached group scan first — so a newly added group takes effect immediately instead of waiting for the next scheduled scan.
🐛 🐛 Fixes
- Connectors can declare that a provider's scope lists are permission tiers. Some providers put a whole permission tier into one endpoint's scope list, where any single one authorises the call; others list several scopes because the call needs all of them, and the specs are written identically. A connector now states which it is via
auth.scope_semantics, defaulting to the strict reading. For a tier provider the permission checks no longer report working actions as blocked, and a proposed fix names the narrowest scope that suits the action rather than the whole tier. A write action is never settled or proposed for by a read-only rung, and a permission rejection at call time names the alternatives a tier provider actually accepts instead of reciting the list.
🐛 🐛 Fixes
- The affected-actions list now matches the permission it is shown next to. A service can have two different permission findings at once — one for a permission the install never requests, one for a permission it requests but no connection carries. The banner named the first and listed the actions affected by either, so an action waiting only for someone to reconnect was presented as needing a permission that has nothing to do with it.
🐛 🐛 Fixes
- Clearer signal when a parameter is not applied. Warnings about ignored parameters no longer fire on calls that did apply them, an unreadable export format now falls back to the action's default instead of failing the request, and project job listings no longer accept a pipeline filter they cannot honour.
- A governance change now reaches every container, and the fix button only appears where it can act. Switching actions on or off updated the configuration on all containers but re-applied it to the catalogue on only one, so the next request landing elsewhere still answered with the previous state — a change could look undone seconds after it was saved. Peers now re-apply the override layer when the change reaches them, on the same ordered channel that carries it. Separately, a connector that exposes no operator-editable permission list no longer offers a fix button that has nowhere to write; the banner says what can be done instead.
🐛 🐛 Fixes
- Service tiles now receive the permission status the server computes for them. The page merges the status feed through an allowlist of field names, and the capability fields were missing from it — so the amber dot, the fix link and the attention filter all read an empty value while the server-side data was correct, which looked exactly like the feature not working. The same gap was dropping the field that drives the registration guidance for self-registering OAuth connectors.
📝 🔧 Improvements
- Every place that reports an unconfirmed permission now offers the next step. The service tile carries the finding as a highlighted line with a link to the fix instead of a sentence in muted setup guidance, and the overview counter opens the service list filtered to the affected services rather than all of them. The list also understands deep links — one service expanded, or only the services needing attention — with the reason for the short list shown rather than applied invisibly.
- The per-connection drift line is gone from the service page. It reported how many connections hold a token older than a permission the install now requests — true, and not something an operator can act on: they cannot reconnect on anyone else s behalf, and on a real team the count does not reach zero for weeks. The signal belongs to the person whose token is old, who already gets a reconnect prompt when a call needs the missing permission. The data and its endpoint are unchanged.
🐛 🐛 Fixes
- Workspace exports honour the format you ask for. Exporting a Doc, Sheet or Slides deck now accepts both
format and export_format, and short names like pptx, docx or xlsx alongside full MIME types, so the file you get back is the format you requested.
🐛 🐛 Fixes
- Four parameters that were being silently ignored now take effect. Scoping a code search to a branch, filtering pipelines by branch, filtering jobs by state, and asking a new merge request to delete its source branch all reach the upstream API instead of being dropped while the call reported success.
- Paging through starred projects. The starred-projects view caps its page size, and now accepts a page number so callers with more starred projects than the cap can reach all of them.
📝 🔧 Improvements
- The action summary opens with what is switched on. The blocked count sits at the end of the line rather than the front: it spans the whole catalogue including actions the AI only reaches by searching, so on a large connector it does not reach zero, and leading with it would make every visit open on a number that never improves. It remains the way into those rows.
📝 🔧 Improvements
- Permissions can be requested for on-demand actions without promoting them. A connector can ship capabilities as actions the AI only finds by searching, precisely so their extra permissions are not requested by default. Until now the only way to grant those permissions through the interface was to move the actions into the tool list, which is the opposite of that intent. The blocked view now offers to request exactly what those actions need, leaving them where they are. Dialogs also name the permission field the way the form labels it rather than by its environment variable.
📝 🔧 Improvements
- The action table asks one question fewer. The per-permission dropdown and the header line counting blocked actions are gone; both are replaced by a "blocked" entry in the toolbar summary that filters to exactly those rows, next to the other states. Searching inside an active filter now says how many matches sit outside it and offers one click to widen, instead of an empty list. And the segment for actions that are not in the tool list is labelled "still callable", because unticking a row is a demotion — the AI can still find and call it.
🐛 🐛 Fixes
- Four corrections to the permission-adoption flow. The one-click undo no longer expires while the change it reverses is still in place; a rollback that would discard an edit someone else made in the meantime now says so instead of overwriting it; the confirmation step checks the permissions that step actually added rather than the whole service, so an unrelated older permission can no longer keep it unfinishable; and a save whose catalogue refresh lagged still asks about the permissions the new actions need. Reset to defaults now also revokes the high-risk approvals it always claimed to reset, and names every revert before it runs.
📝 🔧 Improvements
- Switching actions on or off is now near-instant. Changing which actions a service exposes, reclassifying one, or approving a high-risk capability re-applies the operator override layer over the catalogue already in memory instead of re-reading and re-parsing every connector from disk. On a large catalogue that was the difference between seconds and milliseconds, and it removes the case where a save reported that the container had kept its previous tool list. A change that touches a file on disk still triggers the full reload.
📝 🔧 Improvements
- A published version number describes exactly one release. A version that already appears in the release notes is treated as taken when the next release is assembled, the same as one that has already been tagged — so a change from a long-lived branch can no longer be filed under a number that is already in use. Three numbers that had picked up a second entry were merged back into one.
🐛 🐛 Fixes
- A promoted action no longer disappears on the next save. The stamp recording an action shipped default could be overwritten with its current effective state, after which saving an already-enabled action was read as choosing the default and quietly removed the override keeping it enabled — reported as applied. The stamp is now written once, so the override survives however often the catalogue is re-applied.
📝 🔧 Improvements
- Unconfirmed permissions are visible where an operator looks for work. A service whose switched-on actions rely on a permission no connection carries now says so in three places that agree with each other: a count on the overview that reaches zero, the amber state on the service tile, and a line on the service page naming the affected actions with a one-click fix. The connection check has stopped claiming that the provider app and the personal connection are working — it tests the token you already hold and no permission at all, so it now says exactly that, and it no longer clears a permission finding it cannot answer.
🐛 🐛 Fixes
- A Google permission failure reaching the connections tile is now pinned by tests. The check that mirrors runtime permission failures onto the dashboard skips Google by design — that layer cannot derive the per-sub-service key the tile reads — and the path Google actually takes writes the flag itself. Nothing tested that arrangement, so removing either half would have gone unnoticed.
📝 🔧 Improvements
- Permissions for newly enabled actions are derived, not retyped. Switching actions on that need permissions beyond what a connector ships used to mean reading a list out of a browser dialog and typing it back into a field on another page. The gateway now derives the exact set from the actions themselves and offers to add it after the save — with a link to the provider console for the one step it cannot do, a reconnect that proves the provider actually issues the permissions, and a one-click undo that restores the previous value verbatim until that proof arrives.
📝 🔧 Improvements
- Every published version number now identifies a build you can pull. A release run collects all pending changes into a single version with a single image, instead of publishing release headings for versions that were never tagged. A gateway with no VERSION file also reports
unknown rather than a hardcoded number from an old release.
🐛 🐛 Fixes
- File transfers now validate every redirect they follow. A download that an upstream redirects is re-checked at each hop against the same internal-address rules as the original URL, so a redirect cannot steer an authenticated fetch at an address inside the deployment. A connector that accepts a caller-supplied download URL must also declare which host prefix it allows.
- Configuration writes now survive a host crash, and an empty environment variable no longer shadows a configured value. The configuration file is flushed to disk before and after being moved into place, so a crash cannot leave a durable filename pointing at unwritten content. A variable that is present but empty — which the deployment templates produce for every credential whose value is undefined — is treated the same way everywhere: as not set, so a value configured through the setup wizard is applied both at startup and on a running container.
📝 🔧 Improvements
- A service tile stops reporting green once a capability it exposes is unproven. The green dot rested on a flag written once, on any completed OAuth dance, and never re-examined — so a single connection made at any point in the past kept a service green forever, including right after an operator switched on actions needing a permission nobody had ever been granted. The dot is now evaluated against the permissions the switched-on actions actually need and the grants that actually exist, so it re-arms by itself and clears as soon as any one person connects successfully. Services that declare no per-action permissions, and grants a provider reports nothing about, read as no-evidence rather than as a problem.
🐛 🐛 Fixes
- The shared configuration file now has the last word in every case. A container that receives configuration out of sequence applies what it was told and then reads the file, so a late message can no longer reinstate a setting the file has already changed, and a peer's removal is picked up even for a container that learned the setting only from a broadcast. A file that is behind the state a container has already applied is reported rather than treated as a removal, and the staging file each write goes through is unique per container and owner-readable only.
- The magic-link confirmation step is bound to the browser that opened it. The confirmation page's proof is now carried both in the page and in a same-site cookie, so a login can only be completed from the browser the link was opened in — while a deployment behind a proxy that suppresses the origin header keeps working without any configuration. The CORS zero-config default also reads the same public-URL setting as everything else.
📝 🔧 Improvements
- Action table columns line up, and the summary line is now a filter. Every row in a service's action table shares one column grid, so the description and endpoint columns start at the same position in each row instead of shifting with the content of the row above. The counts in the toolbar — visible, tool search, unsupported, high-risk — are clickable: each one filters the table to the set it counts, and clicking it again releases the filter. The pending-changes count moved onto the Save button so the filter stays reachable while edits are unsaved, and the redundant high-risk chip is gone from the type filter. OAuth permission lists are now edited one entry per line, and a pasted multi-line value no longer fails the save.
🐛 🐛 Fixes
- Configuration ordering across containers is now anchored on the file, not the broadcast. A container that receives a configuration snapshot out of sequence reads the shared file instead of composing the message, so no setting a single message alone carried can be lost, and a peer's removal is picked up as well. The file is written by rename so a reader never sees it half-written, the cross-process lock is released before the broadcast rather than held through it, and it is held per saving request rather than per process.
- Public URL resolution now honours
APP_BASE_URL everywhere. The setting the setup guide documents is read by the URL resolver as well as the same-origin guard, so a deployment configured with only that name gets working links rather than localhost ones. The magic-link confirmation page also carries its own proof, so a browser behind a proxy that suppresses the origin header can still complete a login.
🐛 🐛 Fixes
- A configuration save that cannot get the shared lock no longer writes the file. The change stays live in the running container and the save reports that it was not persisted, rather than rewriting the file without the lock and discarding a change another container had just made. Startup and command-line callers wait for the lock as long as needed; only a request that would otherwise block the server has a short limit.
🐛 🐛 Fixes
- A configuration file that is behind what a container already applied is now left alone entirely. Values from a restored or unshared file are no longer adopted, the record of which settings the file manages survives unrelated reads, and a failed write reports the real disk error rather than a follow-on one.
🐛 🐛 Fixes
- The magic-link confirmation step now requires proof that the request came from the gateway's own page. Completing a login from a page on a neighbouring host of the same domain is refused, and the CORS default for the streaming endpoint reads the same public-URL setting as everything else.
🐛 🐛 Fixes
- Configuration saves are now ordered across containers. In a multi-container deployment sharing one data volume, governance writes serialise on the configuration file itself and each write carries a version. Two administrators saving within the same moment from different containers keep both changes, and a container applies peer snapshots in the order they were made rather than the order they arrive.
🐛 🐛 Fixes
- Magic-link login now shares the hardened same-origin rule. The login flow compares scheme, host and effective port like every other state-changing surface, and takes the public origin from configuration rather than from a request header — including for the address the emailed login link points at. Requests without an
Origin header (native clients, curl) are unaffected.
📝 🔧 Improvements
- A playbook for diagnosing slow test runs. Records how to tell a slow machine from slow code, which measurements mislead, and what has already been tried.
📝 🔧 Improvements
- Service definitions are read once per test process instead of repeatedly. Test discovery parses each file a single time rather than once per module that needs it.
🐛 🐛 Fixes
- Configuration saves inside one container stay strictly ordered. The lock that orders governance saves could be replaced while a save still held it, in a process running more than one event loop, allowing two saves to proceed at once. Each loop now keeps its own lock for its lifetime. Ordering across separate containers is a different problem and is unchanged.
🐛 🐛 Fixes
- Switching read-only mode now tells you whether it actually took effect everywhere. The confirmation for read-only mode and the WebMCP settings previously appeared as soon as the change was applied in the receiving container. It now reports whether the setting reached disk and went out to the other containers, and says plainly what did not happen instead of claiming success — so a safety switch cannot appear to be on while writes continue elsewhere or the setting is lost on restart.
📝 🔧 Improvements
- An automatically filed bug report now says when it is one of several from the same failure. One failure often surfaces at more than one layer, and each log line became its own report. Reports about the same subject within a few seconds now cross-reference each other, and a report whose condition appears to have cleared since says so. Both are annotations — no report is ever withheld on the strength of them.
📝 🔧 Improvements
- Test collection no longer waits on a Redis that is not there. The test flag is set when the suite is loaded rather than per test, so importing the modules to discover tests takes the test path instead of the production one.
🐛 🐛 Fixes
- Importing a spec through the admin interface now routes each API family correctly. A service that serves several API families from one host — platform and agile endpoints, for example — previously had every imported endpoint given the same routing prefix, so one family resolved to a doubled path and failed at call time in a way that looked like a missing upstream endpoint. Both import paths now read one declaration of which family routes where.
📝 🔧 Improvements
- Test collection parses service definitions with the fast YAML reader. The parser the project already ships is now used where every worker pays for it, cutting the preparation each test run does before the first test executes.
🐛 🐛 Fixes
- Log searches survive more regular-expression shapes, and a self-healing change parked for review is now reported. The pattern screen for log queries recognises further constructs that the log backend rejects, so a search containing one is escaped rather than failing the query. A proposed fix routed to human review now appears in operational reporting instead of being invisible to it, while still not counting against the daily automated-change budget. A reviewer that could only see part of a change now says so.
🐛 🐛 Fixes
- A parameter alias can no longer shadow another parameter. An alias is now rejected when any of its accepted spellings collides with a different parameter of the same action, so one supplied value cannot reach two upstream fields. An unparseable result limit falls back to the default consistently instead of failing the call in one configuration shape and not the other, and every refusal now carries a stable code so one class of rejection groups as one entry in operational reporting.
📝 🔧 Improvements
- Test runs report how much parallel capacity they were given. A run that shares its machine is now distinguishable from one that had it to itself, which a single-core reading could not show.
🐛 🐛 Fixes
- A guessed action name can no longer run a different operation. Action names are matched forgivingly so a client that guesses a plausible name still works, but a name that differs by a whole noun is now treated as a different request rather than a misspelling — asking to comment on an issue can no longer resolve to creating one. A missing verb is still filled in. Unmatched names get the real answer: the allowed list plus a pointer to action search.
📝 🔧 Improvements
- Release numbering no longer needs a hand-picked version. A change now carries only its changelog entry; the release version is assigned when the release is built. Concurrent work can no longer collide over the version file, and a major or minor increment is refused at release time unless it is explicitly approved.
📝 🔧 Improvements
- Merge validation checks the specifications this repository ships. The vendored API specifications are used during review instead of fetching from each provider, so validation no longer depends on third-party availability; the live comparison runs nightly, where a provider changing their specification is the actual question.
📝 🔧 Improvements
- Admin pages are verified to parse in a browser before release. Every script block the admin interface serves is now parsed by a real JavaScript engine as part of the test suite, across all rendered pages. Interface code assembled on the server can no longer reach a release in a state a browser would refuse to run.
🐛 🐛 Fixes
- The service configuration page recovers its interactive controls. A stray line break in inlined page code stopped the browser from running the whole script block on a service's detail page, leaving its action table stuck on the loading placeholder and its filter, risk badges and Save button inert. A new check parses every inlined script at build time so a broken escape cannot ship again.
🐛 🐛 Fixes
- An expired connection is reported as a reconnect prompt, not as a gateway fault. When a provider no longer recognises a stored authorization, the gateway records it as an expected condition naming the remedy instead of an internal error — so it no longer files an automated bug report for something only the affected user can resolve. Genuine failures of the token endpoint are unchanged.
📝 🔧 Improvements
- The OpenAPI import checks parse their catalogue once instead of four times. The same verification now runs in a fraction of the time, shortening every full test run.
🐛 🐛 Fixes
- Credential and permission fields are no longer spell-checked. Values such as OAuth permission names are exact identifiers, not prose; browsers marked them as misspelled and touch keyboards could autocorrect them, so a value that had to be copied exactly looked wrong or arrived changed.
📝 🔧 Improvements
- The dependency audit runs when dependencies change, and nightly. It no longer competes with the test run on every commit, while still covering advisories published against locks that have not moved.
🐛 🐛 Fixes
- Signing in with Google works again on every entry point. A recent hardening change required an existing session on the route that also serves sign-in, which blocked the SSO, MCP and ChatGPT login flows; the requirement now applies only to connecting a service to an already signed-in account.
- Saved-question and dashboard reads keep their timeout retries. A request carrying no SQL of its own is recognised as a read again, so it is retried on a timeout and no longer warned that it might have modified data.
- Connector connect links no longer advertise a narrower scope than they grant. Links that appeared to request read-only access now reflect the scope set the connector actually asks for.
📝 🔧 Improvements
- Page-output verification runs alongside the rest of the suite again. Splitting it into a separate parallel job did not remove the work, only moved it into a lane competing for the same machine.
🐛 🐛 Fixes
- Configuration saves report what the gateway actually observed. A multi-container save now reports that the change was stored and sent to the other containers, instead of inferring from a subscriber count that they received it, and a send that did not complete keeps the change pending so an identical retry sends it again.
🐛 🐛 Fixes
- Two follow-ups from an external review of the same batch. A statement whose read-like prefix hides a write — materialising a table, or a second statement after a semicolon — is no longer eligible for a timeout retry. And where a curated action already owns a generated name, only the operation it covers is skipped: other operations sharing that name keep their own, recovering 34 further endpoints.
🐛 🐛 Fixes
- A long-running statement that can modify data is no longer retried after a timeout. A native query is submitted the same way whether it reads or writes, so a write that exceeded the timeout could be re-executed while the first one was still running; read-only statements keep their retries, and a timed-out write now answers that it may still be running. Operational errors returned by an upstream service are recorded as such instead of as gateway faults, and the self-healing agent records the model that actually produced a fix rather than a placeholder.
🐛 🐛 Fixes
- A wrong-typed action parameter now gets an actionable answer instead of an internal error. Passing an object, list or number where an action name belongs previously surfaced as a generic execution failure; it now returns a clear message naming the received type and example actions. Published tool schemas also stop marking a parameter required where the handler supplies a default, so what a tool advertises matches what it enforces.
🐛 🐛 Fixes
- Every operation in an imported specification now reaches the action catalogue. Operations whose generated name collided previously replaced one another, so 1356 of them were absent while the surviving name could describe the operation that lost; colliding names are disambiguated by the distinguishing part of their path instead. Existing action names keep resolving to the same endpoint, with two deliberate exceptions recorded in the importer. An import through the admin interface now applies the connector's routing prefix and resolving hooks to the long-tail as well as the curated set, so services whose host is filled in per user are reachable either way.
🐛 🐛 Fixes
- Tightens what a governance Save confirms. The service governance pages now report success only once the change reached disk, another gateway container received it on the config-reload channel, and every part of a reset-to-defaults completed — otherwise the response names the step that did not happen.
🐛 🐛 Fixes
- The gateway's public URL is configuration, not something a request can set. A deployment that never had
BASE_URL set no longer adopts the host of whichever request happens to arrive first: the same-origin guard, the CORS default and the token audience now follow only what the operator configured, and a log line names the variable when it is missing.
📝 🔧 Improvements
- The permission-surface checks build their catalogue once instead of per assertion. Verification of what an operator can see is unchanged; the setup behind it is shared, taking about nine seconds off a full test run.
📝 🔧 Improvements
- Page-output verification runs when a page can have changed, not on every commit. The render freezes moved into the design-system gate, which already runs whenever markup, styles or routes are touched, taking their cost out of runs that cannot affect them.
🐛 🐛 Fixes
- A contact write is refused when an identifying field still holds an unresolved placeholder. Where a pseudonym cannot be resolved back to the real value, the gateway no longer stores the placeholder as the person's actual data; the call is refused with guidance to re-read the record for a current reference. Scoped by configuration to the fields that identify a record, so free-text fields and searches are unaffected.
🐛 🐛 Fixes
- Same-origin check now derives the gateway's own origin from the request the browser addressed. The check that protects state-changing admin actions reads the scheme the web server settled on and the browser's own
Host header, falling back to the configured BASE_URL, instead of the X-Forwarded-Host header any client can set for itself. The reverse-proxy guide now shows proxies overwriting that header, and the rejection log points operators whose proxy rewrites Host at the setting that fixes it.
🐛 🐛 Fixes
- Governance saves report what actually happened, on every route. The single-action promote, reclassify, approve and reset-to-defaults endpoints now report the same earned outcome the multi-action Save does: a green result means the change reached disk, the other containers were told and this one re-loaded its catalogue, and anything short of that comes back naming exactly what did not happen. The catalogue re-load these saves perform is now time-bounded, so an unresponsive data volume can no longer hold later saves behind it, and both endpoints that were missing the browser cross-origin check now carry it.
🐛 🐛 Fixes
- Contact notes sent as HTML are allow-listed before they leave the gateway. The note body is now filtered through a maintained HTML5 sanitizer, keeping the intended formatting (emphasis, links, lists) while removing scripting constructs, event handlers and unsafe URL schemes; links additionally get
rel=noopener. Plain-text notes are unaffected. If the sanitizer is unavailable the body is escaped rather than passed through.
🐛 🐛 Fixes
- Connector scope sets are chosen by the gateway, not by the caller. The GitLab and Google connect routes accepted a scope list from the request, so the consent screen could be asked for privileges the connector never sanctioned; the scope set now comes from the connector's own configuration, and the Google connect route requires a signed-in session like the other connectors. Imported specs that offer several scope options for one auth method are recorded as unknown rather than as the broadest option, so scope advice no longer asks for more privilege than an operation needs. A connection whose stored grant carries a scope value the provider rejects now answers with clear reconnect guidance instead of a bare API error.
- Confirmation now covers writes that destroy content or lower a security control, not only deletes. Operations such as content redaction and data-classification changes are recognised from the endpoint, so an imported specification is covered on arrival rather than when someone remembers to annotate it; read operations on the same paths are unaffected. Administrative elevation endpoints are excluded from the imported long-tail entirely, since they are not content operations.
🐛 🐛 Fixes
- Governance saves report a truthful, ordered outcome. Overlapping action-governance saves are now serialised end to end, so peer gateways always end on the most recent snapshot; a save that cannot reach a configured Redis is reported as such instead of as a single-container deployment; and a configuration file that cannot be read is left untouched and reported rather than overwritten.
📝 🔧 Improvements
- Test runs report the machine they got at both ends, not just the start. A second fixed-workload sample after the suite makes it visible when a run began on an idle host and was crowded out partway through.
🐛 🐛 Fixes
- Stricter same-origin checks on state-changing admin endpoints. The CSRF guard now compares the full request origin — scheme, host and port — instead of the hostname alone, so a page served from a different port or scheme on the same hostname can no longer drive admin writes. The externally-visible origin is derived from the reverse-proxy forwarding headers and the configured base URL, so TLS-terminating proxy deployments are unaffected. The admin extension, compliance, notification, context-map and demo routes now share one implementation of the check.
📝 🔧 Improvements
- Startup stops resolving a Redis hostname it cannot reach. Configuration checks use addresses rather than unresolvable names, so a slow DNS lookup no longer holds up a run on hosts whose resolver retries.
📝 🔧 Improvements
- Startup no longer waits out a full retry budget it has already spent. The Redis readiness probe skips the pause after its final attempt, and the number of attempts is settable via OAUTH_REDIS_READINESS_ATTEMPTS for instances whose Redis takes longer to accept connections.
🐛 🐛 Fixes
- Bulk action-governance saves now confirm success only when the save is complete. Saving several action changes on a service page reports success once the changes are written to disk, broadcast to the other gateway containers, recorded in the audit trail and applied to this container's tool catalogue — and names any of those steps that did not complete, so a green Save always means a durable one.
- Tightens the everyday/on-demand and high-risk approval toggles to real booleans. Those endpoints now reject a non-boolean value instead of interpreting it, so an approval can only be granted by a request that literally asks for one.
📝 🔧 Improvements
- Continuous-integration runs record the CPU they were given. Each test run now reports the cores it can see, the container's actual CPU allowance and a fixed benchmark, and full timing data instead of a top-20 excerpt, so a run that takes longer can be told apart from a run that was given less machine.
🐛 🐛 Fixes
- Self-healing reports why a commit was refused, and stops repeating a refusal it cannot clear. A fix whose commit is rejected by a write-side gate now records the reason on the issue — including which files were in the commit — instead of a bare log line, and the consumed attempt is persisted so the existing attempt breaker engages. A rejection that depends on the file set rather than the attempt is no longer retried at all; it is handed to a maintainer with the reason attached. Issues are only labelled for automated retest when a replayable retest definition actually exists, and an issue armed for retest without one now leaves the retest queue instead of being reported as a failed test on every deployment. Per-issue notes are posted once rather than once per scan.
🐛 🐛 Fixes
- Multi-action saves on a service page apply in one request. Promotions, risk reclassifications and high-risk approvals selected together now save as one operation that rebuilds the action catalogue once instead of once per action — a save touching several actions finishes in seconds, and every individual change is still recorded in the override audit trail.
🐛 🐛 Fixes
- The per-service tool-search switch can no longer be routed around. When an operator restricts a service to its directly-visible actions, that restriction now also holds for calls that reach an extended action through an alias or a corrected spelling, and an action whose classification cannot be established is refused rather than served while the restriction is in place.
🐛 🐛 Fixes
- Sharper permission diagnostics and honest permission reporting. A 403 the provider does not explain now names both possible remedies without asserting either, so a reconnect is only advised when the response actually evidences a missing OAuth permission — including on non-English provider instances, where structured provider codes are now read. All error fields of a provider response are considered together, so permission wording no longer hides the permission evidence beside it. Stored grants keep the provider's own permission list verbatim, making the connection drift report reflect real grants. The service detail page now describes the permissions an install requests at sign-in rather than implying it knows what each connection holds, and saving a connector's extra-permissions field warns about permissions it newly introduces.
✨ 🚀 Features
- Service pages now say which actions a connection cannot run. Each service's detail page carries a permission status line in its header: how many actions cannot run with the permissions a connection currently carries, and — where a connector records nothing about permissions — that the gateway cannot tell. Clicking the line filters the action table to exactly those actions, and a new permission filter alongside the read/write chips answers the question that comes before granting one: which actions depend on it. The page also names connections whose granted permissions have drifted from what the install now requests, in either direction.
- The OAuth scope field now shows the whole set, not just your additions. Connectors that accept extra OAuth scopes paint the effective list above the box, marking each entry as shipped by the connector or added by you. Only the additions are stored, so scopes a later connector update ships are still requested automatically.
- The permission warning moved to the moment it matters. It now appears where the reachable surface actually changes — switching tool search on, or making an action directly visible — instead of on every credential save, and states the one ordering hazard: a permission the provider's app has not granted yet can make the provider reject the whole sign-in.
✨ 🚀 Features
- Admins can now see which connected users hold an out-of-date OAuth grant. A connector's scope list only governs the next consent — users who connected earlier keep the grant they were issued, and a scope removed from configuration stays live in every grant already given out. A new admin-only endpoint reads the scopes actually stored per user and reports both mismatches: users whose grant lacks something the install now requests, and users still holding a scope it no longer asks for. Grants a provider never described are reported as unknown rather than counted as complete.
🐛 🐛 Fixes
- The per-service tool-search switch now bounds what runs, not just what is found. Turning off "Find more by tool search" for a service keeps assistants to that service's directly-visible actions: its extended, search-only endpoints are no longer callable by name either, and an attempt to call one returns a clear message naming the service and the switch. Operator promote/demote choices still decide which actions count as directly visible.
🐛 🐛 Fixes
- 403 responses now say which side has to grant the permission. A rejection the provider raised in its own authorization model — a project or space permission, a role behind an API token, a per-object access rule — is reported as such instead of asking for a reconnect that would return a credential with identical rights. Scope rejections keep the reconnect flow and the scope diagnosis they had.
🐛 🐛 Fixes
- Curated action lists now only advertise actions a stock install can call. Connector actions that need OAuth scopes beyond the default consent are kept out of the default tool list — they stay callable by name and findable through action search — and a contract test now enforces that rule for every connector.
✨ 🚀 Features
- Saving a service now warns when its actions need OAuth permissions the installation does not request. The connector's actions carry the permissions they require, so the setup form can name what is still missing at the moment it can be fixed — with the provider console tab where applicable — instead of leaving it to a failed call later. Advisory only: saving is never blocked, and connectors without permission data are unaffected.
📝 🔧 Improvements
- Page-level verification reaches the parameterised admin pages. The sign-in provider pages and the service detail page are covered by the same output freeze as the rest of the admin surface, and a new parameterised page can no longer be added without being covered.
📝 🔧 Improvements
- The admin surface begins splitting into per-section modules. The log viewer's routes now live in their own module, verified to leave every page byte-identical, as the first step of a structural split that keeps each admin section self-contained.
📝 🔧 Improvements
- The log viewer inherits the shared admin layout. It now renders through the same base template as every other admin page, picking up the shared title, document shell and accessibility invariants instead of restating them, and page templates can rely on conditionals behaving correctly.
🐛 🐛 Fixes
- The additional-scopes field is checked against the page an operator actually sees. A connector that reads an operator-supplied setting now has its input field verified in the rendered setup page, not only in the connector definition — a field declared but never painted would leave the connector asking for permissions that cannot be granted.
🐛 🐛 Fixes
- The admin page-rendering gate no longer expires overnight. Frozen page output ignores calendar dates, so a check that compares rendered pages stays valid across a date change instead of failing the next day. Scope advice for Atlassian services now names both steps an operator needs — granting the permission and entering it in the connector's additional-scopes field — and the importer's report of operations dropped through a name collision states which one was kept.
🐛 🐛 Fixes
- Confluence installs can now request scopes beyond the connector default. About half of Confluence's imported action catalogue needs permissions the connector does not request by default; an *Additional OAuth scopes* field makes them reachable, matching the Jira connector. Imported specifications also report operations whose derived action name collides with an earlier one, so an action missing from a catalogue is visible instead of silent, and a retired setting an installation still sets is now reported rather than ignored.
🐛 🐛 Fixes
- Imported API specifications are validated more strictly. A malformed scope declaration in an imported specification is now ignored rather than misread, and the contract test covering an API family's routing fails loudly instead of skipping if its specification is no longer vendored. Connectors that read operator-supplied settings are checked to expose a matching field in the setup wizard.
🐛 🐛 Fixes
- Scope metadata reads an API spec's "no scope required" marker correctly. An operation that declares it needs no OAuth scope no longer inherits the specification's default scopes, and Jira's non-platform endpoint families are filtered consistently in both the bulk and admin-UI import paths. A connector that declines to request a scope can now state why in its own configuration instead of relying on wording built into the gateway.
🐛 🐛 Fixes
- Permission advice no longer points at site administration, and 17 dead Jira actions are gone. When a call fails for a missing permission the gateway names it — but never one the connector deliberately refuses to request, such as site-wide Jira administration; those actions are reported as out of reach by design instead. Seventeen imported Jira actions whose paths sit outside the platform API were dropped: they could only ever return a 404 that looked like an upstream problem. Eighteen Slack actions no longer claim to require the pseudo-scope 'none'. The five board actions now carry their own requirement, so a rejection names it.