> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# Project queries and integration events, version 1

Sources: `proto/pharos/project/v1/requests.proto` (the envelope), `queries.proto`
(directory and canonical query inputs) and `replies.proto` (their typed results). Run `pnpm generate:contracts`
after edits. Generated Go bindings live in Beto's outer contract package and the
API's messaging adaptor. Metallo receives generated Python query/event bindings.
Each service compiles schemas into its own package; consumer wiring remains
explicit and is deferred in this slice.

`GetProjects` declares its owner, public visibility and `Projects` success response
through the `pharos.contracts.v1.contract` schema option. These build-time annotations
preserve the existing wire fields. Generation validates the association against
`ProjectReply.payload`, including its `RequestError` alternative.

The retained directory operation is `GetProjects`. A `ProjectRequest` has contract version 1,
context `project`, a UUID `request_id` and `query.get_projects`. The query has
no parameters. A `ProjectReply` repeats the version, context and request identity,
with either `projects.items` or `error.code`.

## Canonical owner boundary

Canonical Project IDs are independent UUIDs. Provider IDs remain opaque bindings.
These operations preserve the existing directory fields and wire numbers:

| Query                             | Success and missing-resource semantics                                                                                                                                                                                                 |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ResolveProjectProviderBinding`   | Exact provider/instance/external ID to Project ID and revision; `unresolved` until a complete canonical snapshot exists.                                                                                                               |
| `GetProjectIntegrationSnapshot`   | Complete canonical Project plus separate source status; `not_found` for an absent canonical Project.                                                                                                                                   |
| `CheckProjectCapability`          | Correlated Party/Project/capability, allowed/denied, Project revision and Party eligibility evidence; `stale_authority` when source authority is pending, failed or expired.                                                           |
| `ListProjectIntegrationSnapshots` | Immutable, paginated inventory for one authorised provider/instance/scope. The first request opens a 24-hour checkpoint; subsequent pages supply its ID and returned cursor.                                                           |
| `GetProjectMembers`               | Current actor’s bounded Project membership, canonical Party IDs/roles, nullable trusted AAI names with independent revisions, membership completeness and diagnostic unresolved-member count; requires fresh `project.view` authority. |
| `ListProjectInvitations`          | Complete bounded pending, unexpired Project invitations; requires current `project.access.manage` before and after the provider read.                                                                                                  |
| `ListMyProjects`                  | Current actor's Projects with `project.view`, including UUID, revision, name, organisation and lifecycle. An account without accessible Projects returns an empty page.                                                                |

The published capability matrix is:

| Capability              | Decision                                                               | `developer` | `project-administrator` |
| ----------------------- | ---------------------------------------------------------------------- | ----------- | ----------------------- |
| `project.view`          | Read basic Project information.                                        | allow       | allow                   |
| `runs.view`             | List Runs and read operational state, initiator and resource use.      | allow       | allow                   |
| `runs.output.view`      | Read logs and published results, which may contain repository content. | allow       | allow                   |
| `runs.launch`           | Accept a new Run.                                                      | allow       | allow                   |
| `runs.retry`            | Request a manual retry.                                                | allow       | allow                   |
| `runs.cancel`           | Request cancellation as the current person.                            | allow       | allow                   |
| `workloads.view`        | Read Workloads and immutable revisions.                                | allow       | allow                   |
| `workloads.manage`      | Create, revise or retire a Workload.                                   | allow       | allow                   |
| `pipelines.view`        | Read a Pipeline and operational progress.                              | allow       | allow                   |
| `pipelines.start`       | Accept a new Pipeline.                                                 | allow       | allow                   |
| `pipelines.cancel`      | Request Pipeline cancellation as the current person.                   | allow       | allow                   |
| `repositories.delete`   | Request deletion of a Project repository through Source Control.       | deny        | allow                   |
| `project.access.manage` | Administer Project access.                                             | deny        | allow                   |

`runs.view` grants operational visibility without granting repository, private
workspace, log or result contents. Source Control and Site Runtime retain their
own repository and workspace rules. `project.read`, `project.execute` and
`project.manage` remain accepted transition aliases for #794's current callers.
They are removed after the three Metallo consumers use the explicit capabilities,
`metallo-requests` has an enrolled Beto caller and the real broker lane qualifies
each caller. New call sites must use the explicit matrix.

Roles are `developer` and `project-administrator`. Each membership row retains its
qualified Party binding ID/revision and opaque external account ID. Several valid
rows for the same Party contribute their roles; unresolved members have no
canonical membership row. Unsupported roles grant nothing. `pending_member_count`
and `governance_issues` expose incomplete identity resolution and missing managers.
Source snapshots are complete and limited to 256 members. Exceeding the limit
rejects the refresh and preserves prior state; there is no truncation.

Integration queries require an authenticated service principal and an explicitly
configured provider/instance/source scope. Actor-bearing requests cannot use
the integration snapshot, inventory or binding-resolution queries. Invalid input, forbidden scope, missing authority and
temporary failures retain distinct outcomes. Authority failure is never an
allowed capability decision. A positive decision requires fresh complete source
evidence, a currently established exact binding and current Party eligibility.
It is evidence for the current operation, not a reusable permission token.

`ListMyProjects` accepts only the API's authenticated Party actor. The Party UUID
and authentication version come from broker-validated actor headers; the payload
contains only an optional `after_project_id` and `page_size` (default 25, maximum
100\). This actor may also check its own capabilities. Every returned Project
passes the current `project.view` decision, including exact binding, eligibility,
source freshness and revision checks. Pending, failed or stale candidate authority
fails the page; it never becomes an empty membership result. The handler rechecks
Party eligibility and source expiry before returning the page.

Pages sort by canonical UUID and return `next_after_project_id` only when another
authorised Project exists. The cursor is the last returned UUID. Each page reads
current authority; there is no frozen inventory checkpoint. Restart the listing
to include membership changes before the cursor. The API exposes this query as
`GET /api/v1/me/projects`, with `cursor`, `page_size`, `items` and nullable
`next_cursor`, using a canonical Party browser session. The web page loads it
through the generated OpenAPI infinite-query client, with explicit refresh and
load-more actions. Technical operator sessions have no Party membership.

`ProjectSourceStatus` binds its acquisition ID, sequence, scope and material
snapshot fingerprint to the returned Project. `last_complete_read_at` records a
successful complete read, including unchanged reads. `valid_until` uses the
owner's configured freshness budget; retries/replay preserve the original time.
`authority_revision` and `changed_at` order and date owner authority transitions
independently of material Project revisions. State is `pending`, `ready`, `failed`
or `stale`. An admitted native notice within the retry budget invalidates the source until its corresponding
complete read is committed. Routine scans preserve the last complete evidence
and its original expiry while another read is in progress. They cannot clear a
failure or a pending notice, and scheduling alone emits no renewal. Exhausted
sources retain failed authority through notices and periodic five-minute probes.
Operator resume preserves the failure while making work due; subsequent admitted notices can mark it pending.
Only a successful complete read can restore ready authority.

| Public event                                        | Meaning                                                                                                                                                        |
| --------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `project.ProjectChanged`                            | Complete material canonical snapshot, including metadata, provider binding and memberships. Payload revision equals the envelope's Project revision.           |
| `project.ProjectAccessChanged`                      | Project ID/revision and affected Parties, including removed Parties, for access-specific reactions.                                                            |
| `project.ProjectAuthorityChanged`                   | Independently revisioned source authority. Pending/failed invalidates; ready records one complete read and its absolute validity, including an unchanged read. |
| `project.ProjectSynchronisationCheckpointPublished` | Announces an owner-materialised inventory checkpoint and its exact record count and manifest hash. It is advisory rather than a queue barrier.                 |

The material and access events can share one Project revision; receipts must
distinguish event and reaction identity. They carry no source freshness assertion;
the separate authority event does. Consumers use the owner query during the
transition and reconcile gaps through its complete snapshot. Bootstrap must bind
queues before the owner read. Replay
re-emits the retained material snapshot with its original revision and event time;
it cannot renew source authority. `ProjectAccessObserved` remains the historical
provider observation contract from the source branch and is not published here.

`ProjectAuthorityChanged.authority_revision` orders authority transitions for one
Project. A ready fact carries the last complete acquisition, source sequence,
Project revision, snapshot fingerprint, `last_complete_read_at` and `valid_until`.
Pending and failed facts retain the previous complete evidence for diagnosis but
deny new access-sensitive decisions immediately. Consumers ignore older
revisions and treat equal revisions with different authority content as corrupt.
Redelivery retains every timestamp and cannot extend validity.

## Authority delivery and retention

Each successful complete read emits one compact `ProjectAuthorityChanged` renewal.
It carries the existing evidence fields and no membership list. Routine scheduling
adds no pending event. Admitted notices within the retry budget, first or changed
failures and material access changes keep their immediate durable publication.
Repeated identical failures retain their authority revision and original failure
time without another event. The 125-second default and 305-second maximum
revocation bounds remain unchanged.

Authority consumers persist the latest revision, a fingerprint of its canonical
authority content and the projected state atomically, then acknowledge delivery.
Older revisions are ignored; equal revisions require matching content. This
current record provides durable deduplication for the state-only renewal stream
without a permanent receipt row per message. Material access reactions, commands
and other external effects retain their own receipts and idempotency rules.

The <a href="/source/contracts/events/retention.json.txt">retention declaration</a> gives authority subscriber
queues a 305-second message TTL. Broker administration applies an exact queue
policy, including to existing queues, without deleting or redeclaring them with
new arguments. Quorum messages already queued retain the expiry assigned on
arrival; the policy bounds subsequent publications. Existing backlog is drained
by the consumer's subscription-first bootstrap and revision checks. Material,
access-change, retry and dead-letter queues keep their
existing policies. Mandatory publication and publisher confirms still apply.

Authority events are complete, independently revisioned evidence. After the
maximum validity window, an old renewal cannot authorise a decision. Consumers
must check `valid_until` on every decision, including when the queue is empty or
delivery is interrupted. Expiry of an old invalidation cannot restore access:
all ready evidence preceding it has also expired by then. A restarted, delayed
or newly activated consumer obtains the complete owner snapshot/checkpoint while
consuming and merges by revision before enabling access. Queue emptiness and a
broker acknowledgement never prove that its view is current.

The owner keeps successfully published authority outbox payloads and dispatches
for 24 hours, then deletes them in bounded batches. Unconfirmed deliveries and
live leases are preserved. Current authority and command replay records remain
available independently of this delivery buffer. Checkpoint retention and outbox
retention do not extend permission validity.

Public payloads contain no credentials, identity claims or proof documents.
Private observation facts remain under Beto's context-local proto and catalogue.
Event routing uses `integration.<event name>` on `pharos.events`. Execution Core,
Pipeline Coordination and Workload Portfolio each have separate durable queues,
retry/dead-letter paths and a caller credential that can query Project and Party
Governance. Their projection implementations remain consumer-owned.

## Initial load, checkpoints and repair

Bind and begin consuming every declared Project and Party queue before opening a
Project checkpoint. Read every Project page with the same checkpoint ID, then
collect the Party IDs in the returned memberships and query the Party checkpoint
at that handover. Keep consuming while both reads run. The retained `GetProjects`
directory contains provider numeric IDs and cannot initialise a canonical view.

Checkpoint pages are materialised from one repeatable-read owner transaction.
Concurrent owner commits therefore appear wholly before or after a checkpoint;
they never move records between its pages. Pages sort by canonical Project UUID.
An expired or unknown checkpoint must be restarted from the first page. The owner
commits the checkpoint rows and exact checkpoint-event bytes together, then a
separate durable relay publishes the marker.

The Project item hash is SHA-256 of deterministic `CanonicalProject` Protobuf
bytes. Source freshness is excluded because Waldur acquisition validity remains
Project-owned query evidence. The checkpoint manifest iterates sorted records and
hashes each Project ID, revision, lifecycle and item hash, with a zero byte after
each value. A consumer compares the complete ID/revision/item-hash set and the
manifest before recording the checkpoint sequence as applied. Counts are only a
diagnostic.

The marker can arrive before older facts because each event has its own queue,
broker reconnects redeliver independently and Project material/access events can
share a revision. Consumers keep separate receipts by event name and message ID,
apply state by aggregate revision, and treat equal-revision material and access
events as distinct work. A marker never proves completion on its own. State that
is newer than the checkpoint also prevents a direct manifest match; reconcile the
checkpoint into a staging comparison, retain newer local revisions, and repeat
with a later checkpoint. This rule gives the same result across reconnects and
multiple streams.

A daily comparison opens a new checkpoint, reconstructs the exact manifest from
the consumer view, and repairs missing, different and extra records from the
pages. Rebuilding after local data loss follows the same subscription-first
sequence. Consumer scheduling, staging and repair transactions belong to the
receiving context.

Authority bootstrap uses the same subscription-first rule. Apply each checkpoint
item's `source_status` by `authority_revision` while material state is staged,
continue consuming authority events, then require every retained Project to have
a current, non-expired ready authority revision before enabling local access
decisions. A restart repeats this merge. An incomplete rebuild, unknown gap,
pending/failed state or expired `valid_until` is unavailable and defers
access-sensitive work. Material agreement alone never marks authority healthy.

The current source policy scans every 30 seconds, with the scan interval required
to be no more than half the freshness budget. One acquisition has a 25-second
deadline inside a 30-second lease. Ready evidence is valid for two minutes by
default and configuration is capped at five minutes. Delivery lag is measured
from `changed_at`; consumer apply lag is measured from receipt to its committed
authority revision. Both share the remaining absolute validity window. If either
does not finish by `valid_until`, the consumer denies. With the accepted
five-second source clock skew, the default hard removal bound is 125 seconds from
a change immediately after the last complete read; the configured ceiling is
305 seconds. A delivered pending or failed event shortens that bound. The
24-hour checkpoint retention does not extend permission validity.

## Current decisions and accepted work

Every person-initiated read, output read, launch, manual retry or cancellation
uses that person's current explicit capability. Authorisation is checked before
receipt replay. Membership removal, Party disablement and binding retirement
remove those operations from that person.

For an ordinary departure, suspend the person's access to that Project
immediately. Personal reads, control, new launches and interactive Site access
to that Project stop; other authorised members continue to manage its accepted
work using their own permissions. Final withdrawal of that Project's native access
and binding waits until that work finishes, or an authorised cancellation and
its cleanup finish. The Site owner also preserves any account still needed by
other authorised Projects. Ordinary departure does not itself cancel accepted
work.

Project-scoped suspension needs Beto-owned commands and published integration
facts, with consumers applying the denial within the agreed revocation bound.
This owner workflow remains to be implemented. The current source-managed Project
aggregate restores only `active`; its lifecycle and Party-wide disablement are
separate from the required member suspension within one Project.

A successful launch or Pipeline-start decision supplies the Project revision,
management generation, source authority revision and validity used at acceptance.
The accepting context records that evidence with the exact work identity,
immutable Workload or definition revisions, resolved inputs, environment,
accepted deadline and initiating Party. This is continuation authority for the
accepted work. It is evidence rather than a bearer credential and cannot approve
a new or changed definition.

Metallo configures the maximum lifetime for accepting further execution under
that record; the initial deployment value is 14 days. Save an absolute deadline
at acceptance, shortened by an earlier requested or Site limit. Configuration
changes affect new acceptances only. A Pipeline and all its declared steps share
that original deadline; replay, renewal and automatic retries cannot extend it.

Expiry prevents new native dispatch, later Pipeline steps and automatic retries.
It does not itself cancel an already-running native job. Native wall-time limits,
Project closure and authorised cancellation retain their own rules. Observation,
stage-out of the running work, cancellation and cleanup keep narrowly scoped
operational authority until their obligations finish. Account retention remains
required until those obligations are settled; reaching the dispatch deadline is
not a release event. #766 owns configuration and enforcement, with #771 proving
these boundaries before activation.

Automatic retries of an accepted Run and steps declared by an accepted Pipeline
continue under that bounded record after the initiator leaves or is disabled.
Accepted-but-queued work follows its saved deadline. A manual retry, added step,
changed definition or changed immutable input is a new decision. Project closure
or a changed management generation prevents further dispatch and starts durable
cancellation and cleanup. An explicit security cancellation does the same for
queued and running work. Normal cancellation records the current requester, then
continues under its accepted cancellation authority if that requester later
loses access.

Continuation credentials and native authority are scoped to the accepted Run or
Attempt, exact repositories and Site bindings, and its execution and cleanup
deadline. They operate independently of the initiator's current membership,
interactive login and personal credentials. A Site may retain the exact native
account and binding recorded for that work under #765, fenced by its Project,
Site and account/binding generation and the accepted-work evidence and deadline.
This includes a per-person HPC account identified by `site_username`; the
platform authority to operate it remains scoped to the accepted Run and Project.
Retention grants no personal access, new launch or changed-work permission. The
Site owner must preserve that restricted operational authority until completion
or authorised cancellation and cleanup, then complete the deferred withdrawal
for that Project.

Source Control (#762), Site account delivery (#765), Execution Core (#766) and
input resolution (#767) must qualify this boundary before production workers are
enabled. Operator observation, security cancellation and cleanup use a separate
audited operator authority; member capabilities do not confer it.
Metallo's <a href="/source/services/metallo/docs/epics/instantiate-and-run/run-access.md.txt">Run access policy</a>
describes the Site isolation and operational checks required for activation.

Decision-time `CheckProjectCapability` calls remain required until each consumer
applies the authority stream, proves actual material, Party and binding rows
against owner checkpoints, handles retirement and disablement, fails closed on
restart and expiry, and passes the real Beto/PostgreSQL/RabbitMQ removal lane.
Separate queues provide no shared ordering barrier. Execution Core's worker and
Pipeline Coordination's coordinator currently have callers. `metallo-requests`
still lacks one and must refuse rather than use a stand-in. Workload Portfolio's
caller is in the `metallo` process.

The source protocol is adapted from ikons' implementation at
[`3636595ff`](https://github.com/pharos-ai-factory/pharos-platform/commit/3636595ff693befcdf0bb12ba6482be865a3e41b).
The [context guide](/reference/services/beto/contexts/project/README.md) describes
ownership, recovery and qualification. Frozen canonical fixtures in
`testdata/v1.json` and the retained directory fixtures independently check wire
compatibility through generated Go/Python bindings.

## Broker delivery

| Boundary               | Value                                                                                                                                   |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Request exchange       | `pharos.requests` (durable topic)                                                                                                       |
| Request routing key    | `request.project.query`                                                                                                                 |
| Owner queue            | `ref.project.queries` (durable quorum, 15-second TTL)                                                                                   |
| Quarantine             | `ref.project.queries.dead`                                                                                                              |
| Reply exchange and key | `pharos.replies`, `reply.project.<connection UUID>`                                                                                     |
| Caller queue           | `ref.api.project.replies.<connection UUID>` (exclusive classic, auto-delete, 30-second TTL)                                             |
| Request metadata       | Persistent, `application/x-protobuf`, type `query`, broker-validated `UserId`, `ReplyTo` set to the caller's instance reply routing key |
| Reply metadata         | Persistent, `application/x-protobuf`, type `reply`, app ID `project`                                                                    |
| Correlation            | AMQP message ID and correlation ID both equal `request_id`                                                                              |

The broker-validated publisher identity, configured caller policy and scoped
permissions establish the trusted caller boundary. `AppId` supplies no
authentication. Beto publishes mandatory replies with confirms and acknowledges the
request afterwards. A failed publication closes the consumer, leaving the request
unacknowledged for redelivery. Quorum delivery limits bound repeated failures.
Malformed bytes, unknown query variants, wrong owners/versions and mismatched
identities are transferred to quarantine with confirmation before acknowledgement.
Provider failures produce a typed `temporarily_unavailable` reply; callers can
issue a fresh read. Requests have no authoritative side effects or command receipts.

Replies preserve the legacy directory fields as typed Protobuf. The API's HTTP
adaptor returns a bare JSON array with numeric IDs and snake_case keys, mapping
absent descriptions/avatars to JSON null. Protobuf JSON rendering is a different
format, including its quoted int64 convention. The legacy endpoint also passes
through provider-specific fields beyond the stable directory view; these are
outside this typed initial contract.

Beto receives its own `PROJECT_DATABASE_URL_FILE`, `PROJECT_BROKER_URL_FILE` and
operational API key. Enrolled source workers receive separate restricted provider
bindings; administrator credentials remain outside the runtime.
`pnpm dev:up` and the context administration command provision the topology with
host-side administrator credentials. Beto runtime credentials cannot declare queues,
consume caller reply queues, or publish another context's replies. The API
translates `GET /api/v1/projects` into this query. It receives only
`API_BROKER_URL_FILE` for `pharos_api`, which may publish `request.project.query`
and create, bind and consume its connection reply queues. The generated broker
catalogue scopes queue authority to `ref.api.project.replies.<UUIDv4>` and reply
bindings to `reply.project.<UUIDv4>`. It grants no request-queue administration or
reply publication to the API.

The Argo dev-box overlay provisions the same catalogue in `pharos-broker-provision`,
using the administration image after RabbitMQ becomes ready and before application
startup. Its projected Secret contains the administrator password and declared
runtime bindings. Only that Job can reach RabbitMQ's management port through
the administration NetworkPolicies. Repeated runs preserve runtime passwords.
Host and Argo provisioning read the generated `runtime_bindings` for every declared
Beto owner and caller; Project contributes `PROJECT_BROKER_URL` and `API_BROKER_URL`.

Each API broker connection creates a fresh UUID and exclusive queues before it
publishes requests. Beto validates `ReplyTo` against the requested owner's route
and UUIDv4 format. Separate queues prevent overlapping Pods from consuming each
other's replies. Reconnecting removes the old queues and releases pending callers
as unavailable; callers repeat uncertain reads with fresh request IDs. An absent
instance queue is unroutable, even while another instance remains connected.

The retained shared reply queue remains provisioned for explicit administrative
cleanup, but authenticated runtime requests require instance-specific `ReplyTo`.
The API foundation on this branch already sends broker-validated `UserId` and
instance reply routes. Apply the scoped permissions and use this API version
before enabling the canonical Beto composition.
The [RabbitMQ queue lifecycle](https://www.rabbitmq.com/docs/queues) defines why
connection-owned queues are exclusive classic queues.

`python3 scripts/beto_broker_integration.py` creates a disposable RabbitMQ
container and runs the native Beto process with restricted credentials. It checks
concurrent API connections, reconnect, exclusive ownership, scoped permissions,
foreign-address quarantine and unroutable-reply recovery. `DOCKER_CONTEXT` selects
the local engine without changing the user's default context.

Beto retains private `GET /healthz` and bearer-authenticated `GET /diagnostics`.
Project queries are available through RabbitMQ only.

## Registration and generation

<a href="/source/contracts/project/messaging/broker.json.txt">`broker.json`</a> declares the query queue family and its API and
context-specific Metallo callers. `metallo-requests` uses a dedicated query-only
principal and reply family under the enrolled `execution_core` subject for its
co-hosted Run and Workload decisions. Its declaration sets `consume_events: false`,
so it cannot take the execution worker's view deliveries. The <a href="/source/contracts/beto/messaging/catalogue.json.txt">Beto request catalogue</a>
declares this Protobuf schema for the API. Run
`pnpm generate:beto` after changing it or this Protobuf schema; use
`pnpm check:contracts:beto` to detect generated drift. The generated broker
catalogue is shared by host provisioning and Beto's topology executable. A new
request family requires explicit transport support and authority tests before
registration. Query handlers must cover every declared query before startup.

The same compiled descriptors generate plain input/response models under Beto's
`contracts/requests/generated/model/project`, identifiers under `generated/names`,
and owner codecs and typed query registrations under
`contexts/project/adaptors/messaging/generated`. The application uses the plain
contracts; Protobuf runtime dependencies remain in the outer packages. Handwritten
read-model aliases preserve the existing application names and legacy JSON keys.

Response encoders require exactly one success or error alternative, copy optional
values and preserve absence. Signed and unsigned 64-bit values must fit the exact
JavaScript integer range; signed values range from `-9007199254740991` to
`9007199254740991`. Invalid output becomes a redacted retryable reply. Current
canonical requests require explicit scalar fields; the boundary enforces these
numeric limits for query inputs as well.

Generation rejects missing or foreign contract metadata, invalid reply associations,
recursive responses and unsupported field shapes. It supports scalar inputs and
scalar, optional, repeated and nested response fields. Maps, enums and user oneofs
need an explicit generator extension. Drift checks cover both generated models
and owner adaptors, pruning obsolete generated files while preserving handwritten
owner code. Historical request/reply fixtures verify wire compatibility separately.

## Browser member and invitation reads

The [HTTP integration guide](/reference/contracts/project/http_api/README.md) specifies nullable names,
independent revisions, completeness, errors and generated client usage.
`GetProjectMemberDetails` enriches only already qualified membership bindings through
Party Governance's narrow `GetBoundPartyAssertedName` owner query. It preserves
all existing Project and Party identity fields. `pending_member_count` describes
unresolved bindings and never represents invitations.

The established `GetProjectMembers` query and its `ProjectMembers`/`ProjectMember`
replies retain their exact wire fields and dependencies. Existing API instances
reject unknown reply fields, so names and completeness use the separate
`ProjectMemberDetailsCollection`/`ProjectMemberDetails` messages. Deploying the
new owner first therefore preserves the existing HTTP read. The new API uses
the details query and fails unavailable against an older owner.

`ListProjectInvitations` is a read facade over the existing isolated Waldur
invitation integration. Project owns its current administrator decision and
canonical Project scope; Party Governance retains invitation claim ownership.
The provider adaptor checks the actual Project/customer and every invitation's
scope after complete pagination. It returns at most 256 pending, unexpired
invitations, or fails the whole read. It introduces no invitation aggregate or
mutation. The returned Project revision versions permission evidence, while
`read_at` records successful read completion. Provider pagination has no atomic
collection revision.

<details class="page-source"><summary>Page source</summary>

<a href="/source/contracts/project/messaging/README.md.txt">Source snapshot</a> · Revision `f928834c8`.

</details>
