Skip to content

feat: make managed Profile negotiation the default - #60

Merged
igrigorik merged 1 commit into
mainfrom
feat/managed-profile
Sep 12, 2026
Merged

feat: make managed Profile negotiation the default#60
igrigorik merged 1 commit into
mainfrom
feat/managed-profile

Conversation

@igrigorik

Copy link
Copy Markdown
Contributor

Why

Profile resolution now defaults to a managed Profile. This improves CLI ergonomics: catalog, cart, checkout, and order commands work immediately while preserving exact UCP release negotiation and intentional DIY Profiles for customization.

The new Profile contract

Managed by default

  • The managed Profile contains one rendering for every UCP release installed with the CLI.
  • The CLI discovers the Business offer first, then selects the newest exact release supported by both sides.
  • Only the selected rendering URL is advertised for that exchange.
  • Compatibility remains exact: release dates are not treated as ranges, and a failure after selection does not silently downgrade the request.

DIY when requested

  • ucp profile init creates a Profile pinned to one exact release and activates it only with --activate.
  • Named DIY Profiles continue to use their authored profile.json, including when a Profile URL override is present.
  • Profile body and URL provenance resolve independently, so changing the advertised URL does not replace local capabilities or services.
  • Explicit missing, corrupt, or inconsistent selections fail closed instead of unexpectedly changing identity.

Existing Profiles remain safe

Untouched Profiles generated by earlier releases are recognized as named managed aliases. Their names, active selection, defaults, headers, and historical files remain intact; classification adds metadata best-effort without rewriting profile.json or headers.json.

Edited, custom-hosted, and explicitly DIY Profiles remain authored snapshots. Once an untouched alias is marked managed, current runtime resolution no longer depends on its historical profile.json.

Consistent routing and diagnostics

  • Profile names resolve through per-call arguments, environment, persisted selection, then the managed default. Profile URL overrides use a separate precedence chain.
  • MCP mode ignores Profile and Business values in active.yaml, while explicit tool arguments and environment variables continue to work.
  • Business discovery caches are partitioned by canonical full Profile URL and verify that each stored envelope belongs to the requested URL. Legacy origin-keyed entries are used only when the primary entry is absent or invalid, never merely expired.
  • Profile commands and Doctor resolve the same effective Profile. Doctor audits every managed rendering with bounded parallel probes and preserves the recovery CTA from the original failure.
  • Schema-error provenance ensures that only operation-input failures recommend --input-schema.

Deliberate boundaries

This does not add automatic release walking or hidden resource-to-rendering affinity. A workflow that must remain on one release can create a DIY pin before creating the resource and pass that Profile explicitly on later operations.

Documentation and release

The README and agent skill describe the ordinary shopping path without Profile setup. Intentional authoring, precedence, persistent headers, and release pinning live in the advanced setup and reference docs. The included changeset requests a minor release.

Validation

  • pnpm gen:schemas && git diff --exit-code src/core/generated/
  • pnpm typecheck
  • pnpm lint
  • pnpm test — 962 passed
  • pnpm test:integration — 33 passed, 1 expected live test skipped
  • Push hook pnpm test:full — 995 passed, 1 expected live test skipped
  • Independent implementation and documentation reviews: SHIP

Change Profile resolution from requiring an initialized local Profile to
using a managed Profile by default. This improves CLI ergonomics: catalog,
cart, checkout, and order commands work immediately while preserving exact
release negotiation and explicit DIY Profiles for customization.

Derive one managed rendering from every installed RELEASES entry. Discover
the Business offer first, select the newest exact release shared by both
sides, and advertise only that rendering's URL. Compatibility remains an
exact release intersection; selected document, endpoint, and operation
failures surface directly instead of triggering an implicit downgrade.

Establish one runtime Profile contract:

- managed Profiles expose all installed release renderings
- DIY Profiles retain one exact authored document and release
- Profile body and URL provenance resolve independently, so a URL override
  never replaces a named DIY Profile's capabilities or services
- explicit missing, corrupt, or inconsistent selections fail closed rather
  than unexpectedly changing identity

Keep Profile authoring intentional. `profile init` creates an exact DIY
snapshot, activates it only with `--activate`, preserves owned URLs during
forced regeneration when possible, and validates the complete runtime
Profile before changing active state. Profile names resolve through
per-call arguments, environment, persisted selection, then the managed
default; Profile URL overrides follow their own precedence chain.

Recognize untouched Profiles generated by earlier releases as named
managed aliases. Preserve their names, active selection, defaults,
headers, and historical files while adding classification metadata
best-effort. Once metadata marks an alias managed, current runtime
resolution no longer depends on its historical profile.json; edited,
custom-hosted, and explicitly DIY Profiles remain authored snapshots.

Keep MCP routing isolated from operator-global state by ignoring Profile
and Business values in active.yaml while continuing to honor explicit
tool arguments and environment variables.

Partition persisted Business discovery by canonical full Profile URL and
verify each cache envelope owns the requested URL. Read legacy
origin-keyed entries only when the primary entry is absent or invalid,
never when a valid primary entry merely expired. Continue to honor
Business Cache-Control and the separate tools/list TTL.

Make Profile commands and Doctor reflect the same effective runtime state.
Expose the managed selection through list, show, and use --managed; keep
inactive Profile scans read-only; audit every managed rendering with
bounded parallel probes; and preserve authoritative recovery CTAs through
Doctor output. Attach schema-error provenance so only operation-input
failures recommend --input-schema.

Align the README, agent skill, setup reference, and release notes with the
new default while keeping DIY authoring, precedence, persistent headers,
and release pinning in the advanced references.

Keep version stability explicit rather than persisting hidden
resource-to-rendering affinity or silently walking older releases.
Workflows that must remain on one release can create a DIY pin before the
resource and pass it explicitly on every subsequent operation.

Validated with schema regeneration, typecheck, lint, 962 unit tests, and
33 compiled integration tests with one expected live test skipped.
@igrigorik
igrigorik merged commit 8d2d6b1 into main Sep 12, 2026
13 checks passed
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.

1 participant