Skip to content

[Decision] Pre-auth discovery/bootstrap payloads: inside BaseResponseSchema (coordinated objectui flip) or ruled exempt with reasons — today they are neither #9389

Description

@os-steve

Split out of #9364 at triage (2026-08-17): the error-path half of that card is queued as mechanical envelope conformance; THIS card carries the half that is a genuine wire-contract ruling and must not be decided by a dev mid-PR.

The question

Three route families answer bare payloads (no success flag, fields at top level) and are read that way by SPAs before first paint / before authentication:

  • POST/GET /api/v1/runtime/config (packages/cloud-connection/src/runtime-config-plugin.ts) — objectui app-shell/src/runtime-config.ts reads body.cloudUrl / body.features / body.branding off the top level;
  • packages/plugins/plugin-hono-server/src/current-user-endpoints.ts — nine { authenticated, userId, … } bodies, read bare by the Account/Console SPAs;
  • plugin-auth's /bootstrap-status — three { hasOwner: … } bodies, same consumption shape.

scripts/check-route-envelope.mjs (third gate surface, added by #9267) counts all of them as unenveloped; the counters are pinned. Today they are neither enveloped nor ruled exempt — the state #9364 names as the one worth ending.

Options

A. Envelope them — coordinated breaking wire change: server flips + objectui SPA readers flip, with a compatibility story for consoles/runtimes on different versions (contract-first split: spec/server first, Blocked-by: objectui halves).

B. Rule them exempt — a written ruling that discovery/bootstrap endpoints read before authentication are outside BaseResponseSchema; each file gets an exempt entry with the reason in the gate. Precedent exists: hmr-routes.ts already carries exactly such an exempt-with-reason.

Four-prism block

  1. Platform long-term coherence — A maximizes envelope uniformity but buys a version-skew compatibility obligation on the least-versionable seam (pre-auth bootstrap). B draws a principled boundary ("pre-auth bootstrap surfaces are bare by design") rather than an accidental one; a written boundary with per-file reasons is coherent, an unruled gap is not. Either ends the "neither" state.
  2. Measured business pull — zero user-facing pull for A: no consumer wants these enveloped (the only readers are our SPAs, which read them bare today and would have to be changed to tolerate both shapes through a skew window). The pull is for the gap to be closed on paper, which B does at ~zero migration cost.
  3. AI-agent error-resistance — the envelope's value is highest where agent-authored apps consume responses; these three surfaces are consumed by our own shells, not by authored apps. A closed exempt list with mandatory reasons is itself a loud contract; silent non-conformance (status quo) is the only wrong answer.
  4. Startup scope discipline — A is a cross-repo breaking migration with a skew story, for surfaces only we read; B is a bounded ruling plus gate entries. Remove/declare-small beats maintain-a-migration.

Recommendation: B — rule pre-auth discovery/bootstrap payloads outside BaseResponseSchema, add exempt entries with reasons for exactly these three files (closed list; any NEW pre-auth bare surface needs its own exempt entry to pass the gate, so the boundary stays enumerated), and record the ruling where the gate's docs point. If the maintainer instead wants A, it must be contract-first split (spec/server first, objectui halves Blocked-by:) and version-skew handling named before any dispatch.

Refs: #9364 (worklist + measurements) · #9267 (gate surface) · #3843 (envelope guard founding class) · hmr-routes.ts exempt precedent.

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions