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

# Project context

The proposed <a href="/source/services/beto/docs/party-project/plan.md.txt">Party Governance and Project evolution plan</a>
defines the next persistent model, phased Waldur handover, owner contracts and
draft OpenAPI replacements for Themelio routes. The source-managed canonical
slice and retained directory have separate contracts.

## Canonical source-managed Project

The Project aggregate owns canonical identity, metadata and complete child
memberships. A source reservation allocates its UUID independently of the exact
provider/instance/external reference. The enrolled source scope and management
generation remain fixed for this iteration. Only complete qualified source reads
change its active state; source failures, inventory absence, not-found and
access-denied responses cannot close a Project. The provider is the source of
truth for the Project's existence: when a direct read of the enrolled Project
reports it removed (Waldur `is_removed`), the refresh worker closes it with the
operator close command. An open Project missing from a complete inventory is read
directly, so its removal is observed.

Source roles become `project-administrator` or `developer` through the Project
adaptor. Unsupported roles remain observations and grant no capability. Member
references resolve through Party Governance's published binding port; unresolved
references remain in the complete observation. An absent qualified administrator
is a governance issue. The slice does not invent an administrator or implement
native management writes.

`ObserveExternalProject` runs as a plain application command inside one owner
transaction. Material changes raise `ProjectChanged` and, where effective access
changes, `ProjectAccessChanged` with affected Parties, including removals.
Successful unchanged reads advance separate source-status evidence while
preserving the material Project revision and emitting no new domain facts.
Recorded receipt replay also preserves the original acquisition time.

The canonical service queries are `ResolveProjectProviderBinding`,
`GetProjectIntegrationSnapshot`, `ListProjectIntegrationSnapshots` and
`CheckProjectCapability`. Point snapshots are complete and bounded to 256 source
members; overflow requires recovery and never silently truncates access. The
inventory materialises immutable, scoped pages of canonical snapshots at one
checkpoint. A capability decision requires fresh complete source
evidence, a currently established exact Party binding at its retained revision,
and current Party authentication eligibility. The default source freshness budget
is two minutes, with a five-minute configuration ceiling. Runtime qualification
determines operational availability and revocation bounds.

The [published capability matrix](/reference/contracts/project/messaging/README.md#canonical-owner-boundary)
separates Project information, Run state, Run outputs, launch, manual retry,
cancellation, Workload and Pipeline operations, and access administration. The
three broader `project.*` capabilities remain transition aliases for #794 only.

Project authority has its own revision stream. An initial reservation or a real
source notice within the retry budget publishes pending invalidation; routine scans preserve the last
complete evidence until it expires. A first or changed complete-read failure publishes failed
authority. Repeated identical failures back off, then probe every five minutes
after eight attempts. A successful complete read restores ready authority
automatically; operator recovery can bring the next probe forward.
Each successful complete read publishes one ready renewal with its
original observation time and absolute validity. An unchanged read advances this authority revision
without advancing the Project aggregate. The transition and exact public event
bytes commit together. Replay retains the old authority timestamps. Confirmed
deliveries are cleaned after 24 hours. The authority queues retain messages for
305 seconds; consumer restart and repair use the published current snapshot.
See the [authority delivery contract](/reference/contracts/project/messaging/README.md#authority-delivery-and-retention).

Consumers merge this stream with material, Party and binding views and fail
closed while rebuilding or after expiry. The owner query remains the
decision-time gate until consumers qualify equivalent local checks; separate
event queues do not form an ordering barrier. The contract guide records the
measurable bounds, transition callers and accepted-work continuation policy.
The operator composition `services/project-operations` accepts `close`,
`security-cancel` and `advance-management`. Closure is permanent. Security
cancellation and management handover advance the management generation, fencing
previously accepted work while leaving current membership intact. The aggregate,
command receipt, material events, authority invalidation and source-lease fence
commit together. A source refresh preserves closure and uses the new generation;
it cannot reopen the Project or restore an older generation. An operator decision
preserves the last complete-read timestamp and queues a fresh acquisition within
the existing retry budget. Exhausted sources retain their periodic recovery probes.
Project-scoped member suspension also needs Beto-owned commands and integration
facts. The [accepted-work policy](/reference/contracts/project/messaging/README.md#current-decisions-and-accepted-work)
requires immediate suspension of personal Project access and Site account
delivery (#765) to retain the exact native account and binding needed to finish
accepted work or authorised cancellation and cleanup. This owner workflow remains pending implementation.

See the <a href="/source/services/beto/contexts/project/application/README.md.txt">application workflow</a> for owner ports, recovery,
query evidence and source attribution. Metallo owns its local projections and
consumer receipts. Native Project writes, fulfilment and browser subscriptions
remain separate work.
Source Control also owns its GitLab group/member effects and durable projections.
Its owner-query adaptor and scopes remain disabled pending qualification.

## Runtime and enrolment

Beto opens Project's isolated PostgreSQL database alongside Party Governance.
Canonical broker queries authenticate the publisher and require explicit
`PROJECT_QUERY_SCOPES`: a JSON array of `subject`, `provider`, `instance` and
`scope`. It defaults to an empty array. Execution Core, Pipeline Coordination and
Workload Portfolio have separate broker principals, queues and exact Project
query scopes. The Waldur registration supplies those scopes to Beto's source
Secret; provider credentials remain confined to Beto.
For Waldur, `scope` uses the customer's lower-case UUID without hyphens, matching
the source adaptor's normalised enrolment and complete-read evidence.

The source worker is disabled by default. `PROJECT_WALDUR_ENABLED=true` requires
the complete installation/customer scope, read-only API token, STOMP credentials
and verified CA bindings. Configuration lives in
<a href="/source/services/beto/services/beto/di/project_configuration.go.txt">the composition root</a>.
Runtime defaults are a 30-second inventory interval, 30-second fenced database
lease and 25-second acquisition deadline. Permission checks fail closed when
source evidence is pending, failed or older than the configured freshness budget.
With the accepted five-second source clock skew, the default absolute revocation
ceiling is 125 seconds after a change immediately following a complete read; the
maximum configured ceiling is 305 seconds. Delivery and consumer progress share
the remaining validity window and are observable from the event's transition
time and the consumer's committed apply time. Live qualification must establish
normal availability and lag below those ceilings.

In the dev-box deployment, registration targets namespace `pharos`, pod label
`app: beto` and Secret `beto-project-source`. It supplies restricted reader and
STOMP credentials, and writes the enrolled scope alone to Secret
`metallo-project-source`, which Metallo's views load for (#780). Project consumes
STOMP 1.2 over verified TLS on port 61614.
Only committed notice/quarantine receipts permit ACK. REST acquisition and
periodic inventory use separate Project ports. No Project/Party provider
credentials are installed in Metallo. Production execution workers remain
paused while their consumer implementation is completed separately.

Project resolves bindings and checks eligibility through Party Governance's
published query contracts. The composition root supplies the in-process,
scope-authorised query handlers; Project's adaptor imports only those published
contracts. Cross-context writes, implementation imports and foreign SQL are
absent. New bindings are picked up on a subsequent complete source acquisition.

The retained login, AUP and provider writers stay authoritative. The Waldur
8.1.3 infrastructure adaptation requires live login/AUP and registration
qualification before deployment. See the <a href="/source/services/beto/docs/party-project/waldur-migration.md.txt">migration provenance and gates</a>.

## Retained static directory

`GetProjects` is a read-only query returning the complete static directory.
There is no aggregate, write command, database, migration, event or browser
projection associated with this directory operation.

The application query receives `ports.ProjectReadRepository`. Its Go read model
preserves the legacy Themelio `GET /api/models/projects` fields: numeric `id`,
`name`, `path`, `full_path`, nullable `description` and `avatar_url`,
`visibility`, `web_url`, `created_at` and `updated_at`. The numeric ID remains an
opaque legacy provider binding. It grants no canonical Pharos identity.

The mock returns two deterministic example projects and allocates fresh data for
each read. All trusted service callers receive the same list. It performs no
network access. The legacy endpoint filtered GitLab subgroups using the caller's
token and excluded groups marked for deletion. A future real adaptor needs an
explicit user-scope design, complete pagination, deletion filtering and provider
error mapping before replacing the mock.

The web application uses `ListMyProjects` through `GET /api/v1/me/projects`.
Project derives membership from the authenticated Party actor and checks current
`project.view` authority for every result. It returns paginated canonical UUIDs,
revisions, names, organisations and lifecycle. The retained directory remains
available for historical consumers and fixtures. See the
[member listing contract](/reference/contracts/project/messaging/README.md#canonical-owner-boundary).

The <a href="/source/services/beto/services/beto/di/project.go.txt">composition root</a> registers `GetProjects`
through its <a href="/source/services/beto/contexts/project/adaptors/messaging/generated/queryregistration/queries.go.txt">generated response-bound registration</a>.
The schema generates the plain input/result models and
<a href="/source/services/beto/contexts/project/adaptors/messaging/generated/codec/responses.go.txt">owner codecs</a>.
The service's shared
<a href="/source/services/beto/adaptors/messaging/requests/registry.go.txt">request registry</a> handles dispatch,
envelope validation, complete handler registration and reply encoding. Provider failures become redacted
`temporarily_unavailable` replies; invalid envelopes go to quarantine.
<a href="/source/services/beto/services/beto/di/project.go.txt">Beto's composition module</a> binds the mock and registry through Fx and runs the
RabbitMQ consumer; DI and wire dependencies remain outside application packages.

The registry dispatches plain application values through `QueryBusPort`. The
application handler selects the complete `Projects` response; its published read
model aliases the generated plain Project. The checked encoder preserves nullable
fields and rejects integers outside the JavaScript-safe range. Encoding failures
produce redacted `temporarily_unavailable` replies. Generated identifiers preserve
the existing wire names. Registration rejects missing mappings, invalid or foreign
identifiers, duplicate handlers and input/reply type mismatches before workers start.

See [the wire contract](/reference/contracts/project/messaging/README.md) and
[decision 0046](/reference/docs/decisions/0046-service-communication-and-composition.md).

## Verification

The query tests cover legacy fields, empty results, independent mock data,
malformed and foreign contracts, and redacted provider failures. Registry tests
cover duplicate and mismatched registrations, independent query dispatch and
cancellation. Composition tests cover graph resolution, partial-start cleanup
and worker draining. The RabbitMQ
lane checks reply correlation, quarantine, restricted permissions and redelivery
after an unroutable reply, then repeats the query against the built service image.

Recorded on 13 October 2026 for the slice based on `fb798afca`:

| Lane                               | Result              | Evidence and scope                                                                                                                                                                                                                         |
| ---------------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Beto query and DI                  | Passed              | Go race tests, vet and builds; legacy field shape, independent mock values, typed wire validation, redacted failures, resolved Fx graph, partial-start cleanup, worker draining and operational-only HTTP.                                 |
| Real integration                   | Passed              | `pnpm test:integration`: PostgreSQL and RabbitMQ regression lanes, 135 Python infrastructure tests, all six images and Chromium session/realtime regression. Beto's typed query round trip also runs against its built image.              |
| Project broker                     | Passed              | Restricted owner and caller roles, idempotent topology bootstrap, repeatable reads, malformed-request quarantine, denied topology/foreign reply access and redelivery after an unroutable reply.                                           |
| Full host verification             | Platform limitation | `pnpm verify` stops at three existing Metallo launcher tests on macOS: Bash 3.2 empty-array handling and `/var` versus `/private/var` path resolution. All four launcher tests pass unchanged in the pinned Linux Playwright container.    |
| Remaining verification steps       | Passed separately   | All three Go modules' race tests/vet/build, frozen Python/Ruff/Mypy and 954 unit tests, Web type-check/build, Kubernetes renders, credential checks and all Ansible syntax lanes.                                                          |
| Registration and development setup | Passed              | Architecture/security negative fixtures, generated contracts, large-file review, storage-free context registration, and focused dev-box credential/source/cluster tests. Adding the Project broker binding preserves existing credentials. |
| Live environments and UAT          | Not run             | Minikube/k3s rollout, actual NetworkPolicy enforcement, dev-box takeover, operator UAT, remote CI, real Themelio/provider integration and production deployment remain separate checks.                                                    |

## Reference improvements — 14 October 2026

Beto adopts the reference's service/context adaptor organisation, selective
contract generation, explicit generated output locations and pinned Go static
checks. See the [Beto adaptor map](/reference/services/beto/adaptors/README.md) for navigation and
commands. The legacy project fields, static results and wire identities remain
unchanged. At that stage Project had no persistence authority or browser subscription.

The <a href="/source/contracts/project/messaging/broker.json.txt">broker manifest</a> declares
its query boundary. It generates exact resource and topic permissions for Project
and a separate topology role. Beto runtime users cannot configure resources, bind
queues or bypass routing through the default exchange. API callers have the
scoped temporary-reply-queue capability in the [transport contract](/reference/contracts/project/messaging/README.md). The topology role can
redeclare registered resources and bind the registered query/reply keys. RabbitMQ
requires queue read and delivery-exchange write for dead-letter declarations;
that limited capability remains confined to administration.

Host provisioning preserves the Project runtime password and returns a fresh
`TOPOLOGY_BROKER_URL` for the current administration pass. It is never mounted in
Beto's workload. The standalone topology executable requires the
`pharos_beto_topology` identity. Kubernetes provisioning restarts Beto after applying permissions to replace cached
authorisation; retired queues retain their evidence
until explicit administrative cleanup.

Verification for this update on 14 October 2026:

| Lane                            | Result                                                                                                                                                                                                                          |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Beto static and native tests    | Passed: formatting, generated drift, architecture/large-file review, vet, pinned Staticcheck/errcheck and race tests.                                                                                                           |
| Contract generation and tooling | Passed: complete regeneration, selective Beto generation/checks, 65 tooling tests and 8 development-source tests.                                                                                                               |
| Real infrastructure             | Passed: `pnpm test:integration`, including PostgreSQL, RabbitMQ permission refusals, repeatable scoped topology declaration, reply recovery, 135 Python infrastructure tests, all images, built-image GetProjects and Chromium. |
| Full host verification          | `pnpm verify` reaches the same three existing macOS launcher failures recorded above. All four launcher tests pass unchanged in the pinned Linux Playwright container with its required socket mount.                           |
| Remaining platform checks       | Passed separately: all Go modules' race tests/vet/build, frozen Python/Ruff/Mypy and 954 tests, Web types/build, both Kubernetes renders, workload credential/configuration checks and Ansible syntax lanes.                    |
| Live environments and UAT       | Not run: Minikube/k3s rollout, live NetworkPolicy enforcement, dev-box takeover, operator UAT, real provider integration, remote CI and production deployment.                                                                  |

## Reference alignment — 15 October 2026

Reviewed the reference through `3c8b080`. Project now uses schema-derived query/reply
identifiers and direct composition with owner mappings and `QueryBusPort`. Shared
broker consumers reject envelope visibility that disagrees with their subscription.
The complete static listing and published Protobuf bytes retain their contract.
The reference's durable command reactions and explicit realtime publications
remain future work for a write-capable context. See
[decision 0046](/reference/docs/decisions/0046-service-communication-and-composition.md).

| Lane                      | Result                                                                                                                                                                                                                                                                  |
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Full verification         | Passed: `pnpm verify`, including shared API/Beto static checks, 70 tooling tests, 207 development-tool tests, all Go race/vet/build checks, 954 Python tests, Web types/build, Kubernetes renders and Ansible syntax. The earlier macOS launcher failures are resolved. |
| Real infrastructure       | Passed: `pnpm test:integration`, including PostgreSQL, RabbitMQ, 135 Python infrastructure tests, all six service images, the built-image Project query and Chromium session/realtime checks.                                                                           |
| Generation and boundaries | Passed: full contract regeneration, selective Beto drift checks, generated-name and composition negative fixtures, owner/type/callback validation and subscription visibility tests. Container-based `gofmt` also preserves standard input.                             |
| Live environments and UAT | Not run: Minikube/k3s rollout, live NetworkPolicy enforcement, dev-box takeover, operator UAT, real provider integration, remote CI and production deployment.                                                                                                          |

## Generated query contracts — 15 October 2026

Reviewed the subsequent reference commits `33d464a` and `a9d7bf6`. The Project
schema now owns the plain input/result types, response association, checked codecs
and typed registration. The existing application read-model name aliases its
generated resource type. See the [contract guide](/reference/contracts/project/messaging/README.md)
for generation, supported field shapes and numeric limits.

`pnpm verify` and `pnpm test:integration` both passed after this update. This
includes all Go static/race/build checks, architecture and generation fixtures,
Python and Web checks, real PostgreSQL/RabbitMQ, all service images, the packaged
Project query and Chromium sessions/realtime. Schema-evolution tests compile and
exercise generated Go encoders with optional, nested and repeated fields.
Historical fixtures preserve exact request/reply bytes; legacy JSON keys and null
values also pass. Generation prunes retired models/adaptors and preserves
handwritten owner files.

Both lanes passed again after integrating API endpoint commit `2796ab049`. The
API generates the schema metadata within its own module; concurrent broker queries
and the built API's `GET /api/v1/projects` round trip through Beto also passed.

Live UAT, Minikube/k3s rollout, NetworkPolicy enforcement, dev-box takeover, real
provider integration, remote CI and production deployment remain unrun.

## Main integration — 16 October 2026

Merged `main` through `78d20394c`. The Argo administration image now provisions
Project's broker credentials and topology before API and Beto start. Project
remains storage-free alongside Metallo Site Runtime's private SQLite journals.
API retains the native listener settings introduced on `main`.

`pnpm verify`, full contract regeneration and architecture checks passed on the
merged tree. This includes API/Beto static checks and native race tests, the
executable listener checks, 104 tooling tests, 344 development-tool tests and
1,221 Metallo tests, Web types/build, Kubernetes/Argo renders and Ansible syntax.
The development-tool suite skipped five launcher tests because the expected
Docker socket was absent, and one Linux process-identity test on macOS. All 11
focused broker provisioning tests passed after adding coverage for mounted
credentials and secret-free output from the administration entry point.

The merged tree has not rerun the real PostgreSQL/RabbitMQ, built-image, Chromium
or edge lanes while container testing is paused. Native dev-box takeover is
pending release of another checkout's workstation connection. Live k3s rollout,
NetworkPolicy enforcement, UAT, real provider integration and production
deployment remain unverified for this merge.

## Project operator commands

Run `services/project-operations` from a controlled maintenance environment with
`PROJECT_OPERATIONS_DATABASE_URL` set to the restricted Project owner database
credential. This executable is the independent management authority; its commands
have no member HTTP or RabbitMQ registration and its credential is absent from
Runtime containers. The canonical operator ID is audit attribution. Enrol the
same provider, instance, scope, initial generation and source freshness as Beto.

The request is an immutable JSON file, reused verbatim after an uncertain result:

```json
{
  "ID": "10000000-0000-4000-8000-000000000001",
  "ProjectID": "10000000-0000-4000-8000-000000000002",
  "OperatorID": "10000000-0000-4000-8000-000000000003",
  "ExpectedVersion": 7,
  "Action": "security-cancel",
  "Reason": "Compromised execution",
  "At": "2026-10-29T10:00:00Z"
}
```

Invoke the executable with `-request command.json -provider waldur -instance INSTANCE
-scope SCOPE -initial-generation 1 -source-freshness 2m`. A stale revision refuses
without changing Project state. Replaying a successful command returns its receipt
without advancing the generation or renewing authority. Reusing its ID with changed
material is an idempotency conflict. Existing outbox publishers deliver the facts;
Execution Core and Pipeline Coordination save policy cancellation on observing a
permanent fence, then recover native stop and cleanup independently of membership.

For an exhausted source, use the same executable with `-recover-source`. The
request contains `ID`, `ProjectID`, `OperatorID`, `ExpectedGeneration`, `Reason`
and `At`; omit `Action` and `ExpectedVersion`. Read `generation`,
`failure_attempts`, `last_failure`, `last_attempt_at` and `exhausted_at` from the
scoped `pharos.project_sources` row before preparing it. Resolve the provider
problem first. The response is `resumed`, `not_found`, `not_exhausted` or
`conflict`, with the recorded source generation. Resume resets the eight-failure
budget and makes work due ahead of the next periodic probe, retaining the failure and memberships. A later notice
can mark authority pending; a complete read must succeed to restore ready
authority. Recovery works before a Project aggregate exists. Reuse the
exact request file after an uncertain result; an old receipt cannot resume a
later exhaustion.
`GetProjectMembers` serves the API’s Project member view after a current
`project.view` decision. It returns the complete bounded membership as canonical
Party IDs and roles, the Project revision and a count of unresolved bindings.
It validates exact Party binding evidence and rechecks caller authority and the
Project/source revisions before returning. It exposes no provider IDs or guessed
profile names. `repositories.delete` grants Project administrators the authority
checked by Source Control’s durable deletion workflow; developers remain denied.

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

<a href="/source/services/beto/contexts/project/README.md.txt">Source snapshot</a> · Revision `f928834c8`.

</details>
