# Project application The canonical application owns complete source acquisition, qualified member resolution, one-root observation commands and current capability queries. The existing `GetProjects` mock keeps its legacy directory shape and behaviour. Canonical handlers use context-owned plain values; provider DTOs, wire codecs, storage snapshots and lifecycle wiring belong to outer adaptors and composition. ## Source acquisition and recovery `synchronisation.SourceRefresh` receives an enrolled provider/instance/scope and management generation. Notice receipt replay precedes provider I/O. For a new delivery, `ExternalProjectSource.ScopeOf` qualifies the Project's actual scope before the owner records an accepted or ignored receipt. Foreign scopes create no source reservation. Explicit source absence or access denial commits an `unavailable` receipt with a safe reason and the original delivery identity. Transient or ambiguous scope evidence remains retryable. Accepted notices only schedule durable complete reads. Their source/delivery identity and exact fingerprint are deduplicated by the owner intake port; quarantine must commit before malformed deliveries are acknowledged. A complete inventory is validated before any refresh is scheduled. An absent Project never implies canonical closure. The store reserves a random canonical Project UUID per exact provider tuple, then claims work with a token, generation, sequence and expected root revision. `Prepare` performs the source read and current Party-binding resolution outside the aggregate transaction. It preserves every source member, including unresolved references and unsupported roles. A wrong scope, partial read, excess membership, future timestamp or unavailable Party authority prevents an observation command. `Prepare` and `Record` are separate operations. Retain the exact prepared value when a commit response is uncertain; its retry uses the same command material. A new read requires a new fenced claim. The transaction checks receipt replay before its current lease admission, then invokes `ObserveExternalProjectCommandHandler.Execute`. It writes one root, receipt, outcome, private event intents and successful source evidence atomically. Allowed work states are pending acquisition, claimed acquisition, complete success, retry after a redacted source/Party/commit failure, and periodic recovery probes. After eight failures the counter saturates and complete-read probes continue every five minutes. Inventory and notices preserve the budget and deadline across restarts. A complete success restores normal acquisition and resets the budget. The isolated operator command can advance a probe with an expected generation and immutable receipt, retaining the failure when scheduling the read. Subsequent admitted notices can mark it pending; a complete read must succeed to restore ready authority. Failure never deletes membership or infers a lifecycle transition. Recovery belongs to the source worker and durable owner store. A late binding resolution cannot restore a source member that disappeared from the retained complete observation. ## Material state and freshness The domain snapshot excludes acquisition times and leases. Source evidence binds the exact source, scope, management generation, acquisition ID/order and complete canonical snapshot fingerprint to its committed Project revision. A successful unchanged read records new evidence without a new material revision or event. A replayed receipt or integration event cannot renew that evidence. `ProjectChanged` carries complete canonical material. Qualified membership rows remain per binding, so several valid rows may contribute roles to one Party. The public projection includes unresolved-member count and governance issues; it does not promote unresolved references to canonical Parties. `ProjectAccessChanged` includes removed Parties and uses a separate event receipt even when it shares the Project revision with `ProjectChanged`. `CheckProjectCapability` supports the explicit Project, Run, Workload and Pipeline capabilities in the published contract. Administrators have the complete matrix; developers have every capability except `project.access.manage`. The broad `project.read`, `project.execute` and `project.manage` names remain transition aliases for existing #794 callers. Each positive decision requires fresh source status, a current exact established binding at the retained revision and eligible Party authentication. It retains Project revision, Party revision/authVersion and source evidence in the result. The Project is read once more after Party queries; a changed revision, acquisition or pending notice requires a fresh decision. Source time is checked again to cover slow authority reads. Pending, failed and expired source authority returns `stale_authority`; unavailable or corrupt authority remains a retryable query failure. `ListMyProjects` reads candidates from Project's own membership snapshots for the authenticated Party and applies `CheckProjectCapability` with `project.view`. The request supplies pagination only. Denied candidates are excluded; stale or unavailable authority fails the whole page. Metadata must match the revision whose capability was checked. Final eligibility and expiry checks cover slow multi-Project reads. The read-only workflow creates no aggregates, facts or pending work. UUID-ordered pages represent current reads; changes before the cursor require restarting the listing. `ListPartyProjectAudiences` excludes permanently closed Projects before checking source freshness. Historical memberships remain in the owner record. A closure confirmed during capability evaluation also excludes that Project, preserving the browser's other audiences. Active Projects retain their capability, source authority and expiry checks. Candidate pagination advances across excluded Projects, so an empty page may still have a continuation cursor. Every page rechecks the caller's current Party authentication generation. Initial acquisition, a real change notice, a failed read and a successful read advance the separate authority revision and stage an exact public `ProjectAuthorityChanged` event. Routine refresh scheduling preserves existing evidence and its expiry. Only a successful complete read renews it. An unchanged read emits one compact authority renewal and preserves the material revision. The snapshot query carries the same authority revision for subscription-first bootstrap and repair. Consumers derive expiry from the owner-supplied absolute validity and cannot renew it through replay. `GetProjectIntegrationSnapshot` returns complete material plus evaluated source status, including stale evidence for reconciliation. `ResolveProjectProviderBinding` returns `unresolved` until a canonical snapshot exists. Missing canonical IDs return `not_found`. All three queries require explicit caller authorisation before reading owner data. Runtime composition supplies the two-minute default freshness budget; configured budgets must be positive and no more than five minutes. ## Reference and verification The source acquisition and recovery behaviour follows ikons' implementation commit `3636595ff693befcdf0bb12ba6482be865a3e41b`, reviewed at `origin/feat/metallo-waldur-membership-events` commit `39e5221cd8cbb086da3dde89cd33662fbda78fd5`. It retains complete paginated reads, stable double reads, opaque provider references, durable notice intake, fenced acquisition and unchanged-read suppression. Canonical Project ownership, Party binding qualification and separate freshness evidence are implemented in Beto. Focused domain/application tests cover exact canonical/source identity, immutable snapshot/event copies, semantic no-ops, member removals, unresolved late resolution, unsupported roles, complete bounds, corrupt restores, failed/stale acquisitions, current eligibility and source expiry during a query. Real PostgreSQL, broker, provider transport and deployment qualification belong to the owning adaptor and service lanes. ## Member names and invitation reads `GetProjectMembers` requires current `project.view` before and after the read. The established published query leaves name acquisition disabled and preserves its existing reply and dependencies. The separate `GetProjectMemberDetails` owner query enables name acquisition in the same authorised application read. Each resolved member binding obtains an optional asserted name through the published Party Governance query. The result preserves the Party projection's independent revision. Pending, superseded or ineligible identity attributes have no name/revision; a current nameless projection retains its revision. Owner outages fail the read. Missing names never remove membership rows. The response reports whether all observed bindings resolved separately from name availability. `ListProjectInvitations` uses the same current authority gate with `project.access.manage`. Its provider-independent `ProjectInvitations` port takes the exact source binding and customer retained by Project. The result is complete or unavailable, capped at 256, with pending/unexpired items sorted by expiry and opaque invitation reference. A second capability check fences revocation and Project/source changes. `read_at` comes from the injected clock after acquisition and the final authority check. No aggregate, event or pending workflow is created. The [HTTP guide](../../../../../contracts/project/http_api/README.md) is the client contract.