Skip to content

Split the legacy-positron-enforced layer behind an independent loader option - #47

Merged
wch merged 2 commits into
mainfrom
kdh-legacy-enforced-split
Aug 7, 2026
Merged

Split the legacy-positron-enforced layer behind an independent loader option#47
wch merged 2 commits into
mainfrom
kdh-legacy-enforced-split

Conversation

@khusmann

@khusmann khusmann commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Companion PRs (land after this one): assistant posit-dev/assistant#1996, positron posit-dev/positron#15375 (supersedes #15371). Both pin their submodule at this branch's tip — bump the pins to the merged sha if this squashes.

Problem

The legacyPositronSettings loader option enables both legacy layers at once: legacy-positron (user-set legacy settings, below user) and legacy-positron-enforced (POSITRON_ENFORCED_SETTINGS, above user). Positron ≥ 2026.08 migrates the legacy provider settings — authentication.* connection/credential settings, provider enablement toggles, and model overrides — into providers.json (posit-dev/positron#15121) without clearing them (old builds and settings sync still read the legacy channel), so on migrated hosts the reader layer resurrects stale values: clear a migrated providers.json setting and the old settings.json value silently comes back. Users have to clear both places. But the enforced admin channel must keep working there — and dropping the reader today drops both.

What this does

Each layer becomes an independent opt-in on LoadCatalogOptions (and therefore WatchCatalogOptions):

Option Controls
legacyPositronSettings (existing reader) Only the legacy-positron layer now
legacyPositronEnforcedSettings (new boolean) The legacy-positron-enforced layer: unset/false → off, true → on
  • createLegacyPositronSourceProviders (loader-internal, sources.ts) takes { reader?, enforcedSettings } and stays the single assembly point, so the load and watch paths can't diverge on which options enable which layers.
  • Rank order unchanged: enforced > legacy-positron-enforced > env > user > legacy-positron > default.
  • Semantic break, deliberate: a reader alone no longer enables the enforced layer. Safe because ai-lib is consumed only via submodule pins updated in lockstep; standalone consumers (Notebooks) pass neither option.

Who passes what after the companion PRs: Positron core catalogs (always ≥ 2026.08) pass only the flag; the assistant extension passes the flag on any Positron plus the reader only on pre-migration Positron (< 2026.08); every other host passes neither.

Testing

  • npm run build, check-types (incl. shape guard), lint, format:check all clean.
  • npm test: ai-config 284 passed (new pins: reader alone does not enable the enforced layer; flag alone folds the enforced layer into the load path without a reader; flag folds it into watch-path rebuilds), ai-credentials 134 passed.
  • ai-provider-bridge has 6 pre-existing failures on main (1c47e7e) in openai-explicit-prompt-caching-wire.test.ts — reproduced on a clean checkout, unrelated to this change.
  • Validated live through the companion branches in a dev Positron (2026.08): with only legacyPositronEnforcedSettings: true, clearing a migrated providers.json value no longer falls back to the stale legacy setting, and POSITRON_ENFORCED_SETTINGS still applies above the user file.

@wch wch left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. Just some small findings from automated review:

  • important (simplification)packages/ai-config/src/legacy-positron-settings/sources.ts:42: The shared factory renames the two public options to reader / enforcedSettings, so both load-catalog.ts:58 and watch-catalog.ts:58 must duplicate the same adapter object and ?? false policy. Accepting the public-shaped fields (legacyPositronSettings?, legacyPositronEnforcedSettings?) and passing each caller's opts directly would delete both copies and make this factory the actual single source of truth for which option enables which layer.

  • minor (clarity)packages/ai-config/src/node/watch-catalog.ts:8: Several source-level API comments still describe legacyPositronSettings as the opt-in for the legacy layers, despite the new independent flag (watch-catalog.ts:39, node/types.ts:107, and src/index.ts:132 repeat the old reader-only wording). Update these comments alongside the README/memory-bank edits so maintainers are not told that the reader still controls both layers.

@melissa-barca melissa-barca left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

… directly; update stale reader-only comments
@wch
wch merged commit d04e8c6 into main Aug 7, 2026
@wch
wch deleted the kdh-legacy-enforced-split branch August 7, 2026 01:27
melissa-barca added a commit to posit-dev/positron that referenced this pull request Aug 7, 2026
…alogs (#15375)

Fixes #15377

Copied over from fork contributed by @khusmann in #15371


Companion to posit-dev/ai-lib#47 (splits the `legacy-positron-enforced`
layer behind an independent `legacyPositronEnforcedSettings` flag) —
**depends on it**; the `ai-lib` submodule pin points at its tip
(`52f2f5f`), so bump the pin to the merged sha if ai-lib squashes. The
assistant-extension adoption is posit-dev/assistant#1996.

## Problem

The providers.json migration (#15121) copies the legacy provider
settings — `authentication.*` connection/credential settings, provider
enablement toggles, and model overrides — into providers.json but
deliberately does not clear them (old builds and settings sync still
read the legacy channel). Every catalog site also passes a
`legacyPositronSettings` reader, which folds those same settings back in
**below** providers.json — so clearing a migrated providers.json value
silently resurrects the stale settings.json value it was copied from.
Users have to clear the setting in both places to actually clear it.

## What this does

This Positron always has the migration, so its catalogs stop reading
user-set legacy settings entirely and keep only the admin-enforced
channel:

- **Both `createConfigurationLegacySettingsReader` helpers are deleted**
— the configuration-service one in
`platform/positronAiProvider/node/aiProviderCatalog.ts` (used by the
shared-process and server catalog sites) and the extension-host one in
`extensions/authentication/src/providerCatalog.ts`.
`AiProviderCatalog`'s and `ProviderCatalogOptions`'
`legacyPositronSettings` members go with them.
- **Every catalog site passes `legacyPositronEnforcedSettings: true`**
(hardcoded inside `AiProviderCatalog.loadOptions()` and the auth
extension's `loadCatalog`/`initProviderCatalog`), so
`POSITRON_ENFORCED_SETTINGS` keeps applying above the user file, below
the canonical `POSIT_AI_PROVIDERS_ENFORCED`. Tests still control the
payload via the existing `envVars` seam.
- **The migration is untouched** — it keeps using ai-config's shared
translator with its own `MigrationSettingsReader`. The stale "order
matters" rationale on `migrateSettingsAndPrimeCatalog` (the catalog no
longer reads the keys the AWS/Snowflake migrations write) is rewritten;
those keys now reach the catalog only via the providers.json migration +
the catalog's file watch.
- **Pin bump fallout:** none — the new pin (ai-lib main + the flag
split) changes no API this repo consumes besides the loader options.

**Accepted behavior change**: user-set legacy settings that the
auto-migration skipped — populated-providers.json users, and
workspace-scoped legacy settings (never migrated; no providers.json
equivalent) — stop applying. The manual "Migrate AI Provider Settings to
providers.json" command covers the global case.

### Release Notes

#### New Features

- N/A

#### Bug Fixes

- Clearing an AI provider setting in `providers.json` no longer silently
falls back to the old `authentication.*` value the migration copied it
from. Legacy user settings are no longer read by the provider catalogs;
the `POSITRON_ENFORCED_SETTINGS` admin channel still applies.

### Validation Steps

Automated (run on this branch):

- `npx vitest run src/vs/platform/positronAiProvider/` — 10 passed.
`configurationLegacySettingsReader.vitest.ts` is deleted; the
pass-through block collapses to one test pinning that
`POSITRON_ENFORCED_SETTINGS` applies above the user file with no reader
wiring.
- `npx tsc -p src/tsconfig.json --noEmit` and `npx tsc -p
extensions/authentication/tsconfig.json` — clean.
- `npm run test-extension -- -l authentication` — 188 passing, 0
failures. Includes the reworked pins: `providerCatalog.test.ts`
("POSITRON_ENFORCED_SETTINGS applies above the user file without any
reader wiring") and `activationOrdering.test.ts` ("the primed catalog
does not read legacy `authentication.*` settings" — fails if anyone
re-adds a reader; hermetic via `envVars: {}` so ambient
`AWS_PROFILE`/`AWS_REGION` can't leak in).
Manual (run in a dev Positron):

1. **Cleared values stay cleared** ✓: with a legacy
`authentication.<provider>.baseUrl` set, migrated it to providers.json,
then cleared the value in providers.json → the stale legacy value did
NOT resurface in the catalog (on `main` it falls back).
2. **Enforced channel still live** ✓: launched with
`POSITRON_ENFORCED_SETTINGS` carrying a provider key → it still beats
the user's providers.json.

---------

Co-authored-by: Kyle Husmann <kyle.husmann@posit.co>
melissa-barca pushed a commit to posit-dev/positron that referenced this pull request Aug 7, 2026
Follow-up to #15375: its `ai-lib` submodule pin (`52f2f5f`) is the
pre-squash tip of the posit-dev/ai-lib#47 branch, which is not reachable
from ai-lib `main` — it resolves today only because the PR branch still
exists, and deleting that branch would break fresh recursive clones and
CI (submodule fetches don't pull `refs/pull/*`). This bumps the pin to
the merged squash sha `d04e8c674`, which additionally picks up
posit-dev/ai-lib#47's review refactor (behavior-neutral) and ai-lib#48
(Kimi K3 context length).

### Release Notes

#### New Features

- N/A

#### Bug Fixes

- N/A

### Validation Steps

- `npm run build:ai-lib` at the new pin, `npx vitest run
src/vs/platform/positronAiProvider/` (10 passed), and `npx tsc -p
extensions/authentication/tsconfig.json` — clean.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants