Project member and invitation contract preview
Development fixture contract. This standalone OpenAPI specification and schemas let frontend work proceed with generated clients and fixture responses. The same shapes are now present in the active HTTP contract; the isolated preview client and fixture assets remain available for development.
The preview supplies contract sources, two isolated Orval outputs and Go API fixture bindings. An explicitly configured development API serves these examples through the normal HTTP/session path. The default API uses the real Project owner reads. The field names are agreed independently of the broader #1066 naming work.
Both reads require a current Party identity and a canonical Project UUID.
Browsers use the Party session; the enrolled MCP gateway can use its
operation-bound assertion. The preview retains
the active contract's authentication declarations and current owner checks.
Responses use Cache-Control: private, no-store. Neither operation accepts query
parameters. These reads add no mutation, realtime subscription or automatic polling.
Members
GET /api/v1/projects/{projectId}/members, operation getProjectMembers, requires
current project.view authority. The backend checks Project/source authority
before and after the read.
All properties are required. asserted_name and asserted_name_revision are
nullable. The name is the existing trusted AAI identity projection's name;
editable profile names and email addresses are never used as fallbacks.
A current projection with no name has a null name and its non-null revision.
A pending, superseded or currently ineligible identity projection has both fields
null. A missing name preserves the membership row and roles. Infrastructure or
corrupt-authority failures fail the whole query.
revision versions Project membership. asserted_name_revision independently
versions the Party identity projection and must not be compared between Parties.
An unchanged Project revision does not imply unchanged names.
items is ordered by party_id. The complete source observation contains at
most 256 member bindings; several qualified bindings can contribute to one Party
row. Every observed binding is resolved into a row or counted in
pending_member_count. membership_complete equals pending_member_count === 0.
This flag concerns membership resolution; it says nothing about missing names.
The count is diagnostic information and must never be labelled as invitations.
Show available rows and a simple incomplete-membership state when the flag is
false. Use a neutral missing-name label or shortened canonical Party ID.
Invitations
GET /api/v1/projects/{projectId}/invitations, operation listProjectInvitations,
requires current project.access.manage authority, currently granted to Project
administrators. Show this separate section only to those administrators.
All properties are required and non-null. invitation_id is an opaque reference,
not a canonical Party ID or acceptance credential. The backend derives this scoped reference without exposing the provider invitation locator. recipient_email is available
only through this administrator-authorised read and is not an asserted name.
Roles use the same canonical values as members.
This slice returns only provider-pending invitations whose expiry is after
read_at. It excludes accepted, cancelled, rejected and expired invitations.
There is no historical invitation list or acceptance/revocation operation.
expires_at is the provider's actual expiry, not a locally invented lifetime.
The backend resolves the exact canonical Project/provider binding, traverses all
provider pages and checks Project scope and customer itself. It rechecks current
administrator authority after acquisition. items contains the complete result,
bounded to 256 invitations and sorted by expires_at, then invitation_id.
There is no cursor. Overflow, missing pages, unsupported roles and malformed
provider data fail the whole request; partial results are never returned.
project_revision versions the Project authority used for this read, not the
invitation collection. read_at is successful acquisition completion time.
The provider supplies no atomic collection revision; this timestamp does not
establish a transactional snapshot across provider pages.
Clients, errors and rollout
The existing Orval configuration generates two preview clients from this same
specification. The retained frontend uses plain fetch functions; services/web
also receives TanStack Query v5 hooks, query keys and options.
Both preview clients export:
The import is relative to the active frontend's src alias. Outputs are under
services/web/src/adaptors/generated/project-member-invitation-preview/ and
compat/themelio/services/web/src/adaptors/generated/project-member-invitation-preview/.
The canonical web client also exports useGetProjectMembers and
useListProjectInvitations; these need its existing QueryClientProvider.
Compat has no TanStack dependency or provider and can keep using fetch functions.
Query-cache values must be cleared when the Party
session or Project access changes; HTTP no-store is not a React Query cache policy.
The generated clients make ordinary HTTP requests. Start the Go API with its normal runtime environment loaded, including Valkey, broker and AAI/session configuration, and enable the preview on that API process:
Use the normal frontend origin and route its /auth and /api/v1 requests to this
API instance through the existing development proxy. Sign in normally and select a
canonical Project returned by GET /api/v1/me/projects. Browser requests carry the
existing pharos_session cookie. Server-rendered reads use the existing server
adaptor to forward that cookie. The frontend needs no mock installation, extra
request parameters or fixture credential.
Fixture mode overlays only these two operations. The API authenticates the current
Party and asks the existing Project owner for project.view or
project.access.manage before returning sample data. The broker and existing
Project/Party authority must be available; the new member-name and invitation
backend queries are unnecessary for this mode. Operator/CLI credentials cannot
replace a Party session. Denied or stale authority produces the normal error.
API_PROJECT_READ_FIXTURES defaults to disabled. Only the literal true enables it,
and only with APP_ENV=local or development. Invalid flags/scenarios fail startup.
The API logs that Project read fixtures contain synthetic data. Scenario settings
are server configuration and require an API restart to change.
Member scenarios are members (named and unnamed rows with incomplete
membership), members-empty and members-name-absent (a current projection with
no name). Invitation scenarios are invitations and invitations-empty.
Either section can independently use status 401, 404 or 503 to exercise
its error state after the real permission check. Omitting a scenario selects
members or invitations. Supplying scenarios while fixture mode is disabled
fails startup.
The JSON examples remain the source of truth. Generation copies them into the API
binary, which validates them against the embedded preview schema during startup.
project_id is replaced with the requested authorised Project. Invitation
project_revision comes from the real permission decision; read_at is the current
fixture response time, and each expiry preserves the example's remaining lifetime.
Names, roles, member revisions, counts and invitation contents remain synthetic;
they make no claim about that Project's actual contents. The canonical web
generation also retains its standard Orval MSW/Faker test outputs.
The generated fetch adaptor rejects non-success responses with the existing
HTTPError; inspect its status. Keep the two sections' loading/error states
independent. An invitation read failure does not invalidate a successful member
read.
With fixture mode disabled, both operations use the real owner queries and the agreed contract. There is no automatic fixture fallback on an owner failure. Enable the new frontend feature only against the fixture API or a backend deployment that supports the agreed contract.
The implementation preserves the existing member query/reply
wire shape and adds a distinct owner query for member details, allowing the new
owner to run with the old API. The new API rejects an unsupported owner response.
Follow the active contract's deployment order,
then disable fixture mode and remove the two scenario settings. The frontend keeps the same operation calls, paths
and payloads; its preview client continues to work against the implemented API.
When consolidating generated outputs, switch the import to
@/adaptors/generated/http/project/project and remove the preview.
Clear cached fixture data or reload the development session during this switch. In a
mixed-version rollout, absent fields are unsupported-contract evidence, not
equivalent to null, false or zero. An old backend's invitation-route 404 must
not be converted into an empty invitation list. Frontend fixture work can proceed
against fixtures/members.json,
fixtures/invitations.json and
fixtures/invitations-empty.json; these are
contract examples, not live deployment evidence.
Regeneration and verification
From the repository root, with frozen pnpm dependencies installed:
Normal contract generation includes the preview: it validates OpenAPI and all five
examples, copies the Go API assets and runs the pinned Orval configuration into
both preview directories alongside the active HTTP and Protobuf outputs. A
contract regression checks equality of the active and preview HTTP shapes.
For a focused regeneration, run
uv run --no-project --with openapi-spec-validator==0.7.2 python contracts/project/http_api/member-invitation-preview/generate.py.
Add --check to compare all three generated output sets. pnpm verify includes
this drift check in its contracts group.
Exercise the HTTP bindings, owner-decision validation and development composition:
The real Valkey session lane (pnpm test:party:sessions) covers fixture HTTP
access through the normal session/AUP checks and revocation. Contract examples,
HTTP tests and disposable infrastructure qualification remain separate from live
member-name/provider behaviour. Once the implementation is deployed and frontend
consumers use the active client, remove the preview sources, generation/check
hooks, API fixture bindings/configuration and generated outputs.
Page source
Source snapshot ยท Revision f928834c8.