# Workload Portfolio Workload Portfolio is the Metallo context that owns Workloads. The Workload is its aggregate root; its immutable, numbered revisions are children, saved and changed together. A revision holds the execution specification and required inputs that Execution Core plans from. Execution Core and Pipeline Coordination reference exact Workload revisions. They read saved Workloads through service-level owner adaptors in [`adaptors/workload_evidence.py`](../../adaptors/workload_evidence.py) and check member access themselves. ## Registration The registry enrols `workload_portfolio` with the shared PostgreSQL bootstrap, which provisions its private database and restricted runtime role when explicitly run. The context uses the baseline schema without its own migrations. ## Domain records `domain.workload.Workload` keeps its Project, creating member, display name, creation time, version and optional retirement. Revisions are numbered from one without gaps. Each revision's specification is stamped with its Workload and Project identity. Batch revisions use `queued-batch`; services use `reconciled-service` with `serviceKind: application | huggingface-inference`. Execution Core validates the complete execution intent when it plans a Run. `to_mapping()` returns detached JSON-compatible data. `restore()` treats invalid saved data as `CorruptState`. ## Internal operations `application.handlers.WorkloadOperations` creates, revises, retires and reads one Workload through the shared `CommandPort` and `QueryPort` and an explicit `WorkloadAuthority`. `adaptors.postgres.operations(database, authority)` supplies production PostgreSQL adaptors. `saved_workloads(database)` supplies trusted owner reads for the cross-context adaptors. Running a repository action creates its Workload, so creating, revising, retiring and preparing Workloads need `workloads.manage`, and reads and the action catalogue need `workloads.view` (#755). Access is checked before each command and read. Denied access is rejected; unavailable or uncorrelated authority fails for retry. The permission provider is a test-only double until the real provider is connected. ## Repository actions `application.repository_actions.ActionWorkloads.prepare` validates one `actions.yaml` action and its parameter values, converts it in `domain.actions` and creates, reuses or revises that action's Workload. Each Project repository, branch and action has one Workload with a stable identity. The `RepositoryActions` port reads `actions.yaml` and resolves repository URLs. `metallo.adaptors.source_control_actions` implements it over Source Control's repository queries, with the commit the file was read at; `metallo-requests` composes it through `metallo.adaptors.source_control_owners` (#796), and tests use their own doubles. Invalid parameter values raise `ParameterErrors`, with one message per parameter. New action revisions also retain the effective named parameter values, including defaults, in optional `specification.actionParameters`. This display evidence is bounded to 64 names of at most 128 characters and values of at most 4,096 characters. Credential-like names, and definitions annotated with boolean `sensitive`, `secret` or `confidential`, retain a null value. These annotations control display evidence; they do not provide Runtime secret delivery. Execution commands keep their existing substitution behaviour. An empty map records a parameter-free action; an absent map is legacy unknown evidence and remains absent when old revisions are restored. Execution Core copies the accepted revision's evidence into its Run, so later action edits cannot alter an earlier Run's configuration. An already accepted Pipeline keeps its exact legacy revision on replay when all original execution material still matches. The new display field cannot force a different revision for that accepted work. A new request records its parameters in a new revision. Both `slurm-job` and `k8s-job` prepare `queued-batch` revisions. They select `slurm` and `k8s` respectively and share batch commands, parameter substitution, resources and timeout validation. Kubernetes batch actions use the existing Job Runtime. Their validation rejects service port and inference options, and reports `unsupported_action_feature` for a positive `options.gpus` or any `options.gpuClass`. An omitted GPU count or `gpus: 0` is valid. For Slurm jobs and Kubernetes services, `options.gpus` asks for accelerator devices and `options.gpuClass` names which one, in the Site's own vocabulary: the offering component (`gh200`, `gh200-mig-1g.18gb`, `l40`, `l40-shared`), never a Kubernetes resource name. The delivery's accelerator class map, not this context, turns a class into `nvidia.com/...`. An action that names no class keeps the legacy `gpu` class of one whole device. Whether the Site offers the requested class is decided by Execution Core's admission against the Site's applied limits, so a Workload can be written before its Site publishes the class. `application.action_catalogue.ActionCatalogue.list` describes the actions on `main` for a member, each with the reason the backend cannot run it, using the same checks as a launch. Its `RepositoryDirectory` port resolves Themelio's repository paths, and `adaptors.requests` answers the typed `list_actions` query ([runtime endpoints](../../docs/epics/instantiate-and-run/runtime-endpoints.md)). Service actions accept `options.exposure: project | public`. The default is `project`; batch actions reject this option. The choice becomes the immutable revision's `endpointPolicy.exposure` and is copied into the accepted Run. A public repository alone never publishes a running endpoint. The member needs the existing Workload management and Run launch permissions to launch that explicitly public action. Stop the Run to withdraw it; changing `actions.yaml` affects subsequent launches. ## Persistence `pharos.aggregates` holds one `kind = workload` snapshot with its revisions. `pharos.command_receipts` holds command fingerprints and recorded outcomes. Both are written in one transaction. Workloads are registered without a browser projection and publish no events yet. The context's first migration adds Workload Portfolio's copy of Beto's Project memberships, Party states and provider bindings (`pharos.project_views`, `project_members`, `party_views`, `party_bindings`, `project_access_invalidations`, `view_state`, `view_checkpoints`), applied from Beto's integration events with consumer receipts and loaded from Beto's checkpoints. The `metallo` process holds `WORKLOAD_PORTFOLIO_VIEWS_BROKER_URL` and runs its eight consumers and synchroniser. `PartyProjectAuthority` decides the Workload permission from it by Beto's rule once it is loaded (#757), and defers unless Beto's latest Project authority (`pharos.project_authority`, migration 0002) is ready, unexpired and describes the Project revision it holds (#798). Its caller, `metallo-requests`, reads this view over the context's database and confirms every allowing decision with Beto through its own query-only caller, `METALLO_REQUESTS_VIEWS_BROKER_URL` (#755). [Design](../../docs/epics/instantiate-and-run/party-project-views.md). [Design, remaining connections and verification](../../docs/epics/instantiate-and-run/workload-creation.md).