A project's `share` (absolute or a percentage of its parent's) is carved from the parent; siblings never add up to more than the parent. v1 is strict: a project holds at most its share. A project's own jobs and its children without a share draw from its unassigned part. Borrowing idle shares above a project's own (`max`) and reclaim are future work, in the renamed project-borrowing brief. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
27 KiB
Requirements Document
Project Description (Input)
Discovery brief: .kiro/specs/project-config/brief.md. Governing rules: .kiro/steering/job-config-layering.md (2026-10-09): the project owns resource ownership (membership, owned shares, later borrowing, accounting) and never owns how a job runs or is ordered.
Who has the problem: cluster admins who need to control which users may charge work to a project and how much of the cluster each project may use; users who need to know which projects they can use and why a job is held by a project share.
Current situation:
- A project is a free-text label on the job (
-P). Anyone can name any project. The apiserver only checks its length, and the scheduler maps an empty name to"default". - No project is defined anywhere in cluster config.
- Project-scoped limits exist in
limits.yaml, but the scheduler's project-group table is never loaded. The host-group and project-group managers were passed tobuild_limitswapped (bug-1933, fixed in6ecd418d). xcecli jobs list -Pis rejected by the apiserver ("unsupported filter").- Nothing shows a project's usage.
What should change (v1 = owned shares): nested project definitions in cluster config, each owning a share of its parent's resources; default-project resolution; a project:submit check at submit (implied by membership); project shares enforced end to end and rolling up the tree; project visibility. Borrowing is project-borrowing.
Decisions (2026-10-10):
- Access is by project membership (2026-10-12: membership implies the RBAC permission
project:submit; roles may also grant it). - Open mode applies until any project is defined.
- Default project: group default first, then the cluster default.
- A pending job held by a project change pends with a reason.
- Projects nest, named by path (e.g.
eng/hpc/cfd). - A job may be charged to any project in the tree.
- Membership is not inherited between parent and child, and a user may be a member of any number of projects.
- Usage rolls up: a job counts against its project and every ancestor.
- RBAC interaction:
- a new
projectscope for job permissions, so a project manager can view and control the jobs of a project and its whole subtree; - a
project:viewpermission. - Runtime (delegated) membership edits are left for later; membership changes only through cluster config.
- a new
Decisions (2026-10-12, resource control under the project tree; job-config-layering.md "Resource control"):
- The project tree is the only place concurrent use is capped. Quotas are written on the project (
quota,member_quota), not inlimits.yaml; the limit plugin stays as the internal mechanism, and the scheduler compiles them into limits. (quotabecamesharethe same day, below.) limits.yamlis removed; this feature rejects its project-scoped entries,limits-to-ownersretires the rest.member_quotacaps each user's jobs in the project's whole subtree.- (Superseded the same day by "owned shares", below:
quotabecameshare.) - The full path is the project's identity and the only form a submit accepts; there is no last-segment shorthand.
- Members are users and user groups (with
alland excludes); a sub-project is never a member.
Decisions (2026-10-12, owned shares; job-config-layering.md "Shares"):
- A project owns a
shareof its parent's resources, per resource, as an absolute amount or a percentage of the parent's effective share; the root's share is the cluster's capacity. Siblings' shares never add up to more than their parent's. - v1 is strict: a project holds at most its share. Borrowing idle shares (a
maxabove the share) and reclaiming them is future work (project-borrowing). - A project's own jobs and its children without a share draw from its unassigned part (its share minus its children's shares), first come, first served.
- A project may declare a share for a resource only if its parent has one for that resource, or it is top-level.
- A resource no project declares a share for is not controlled (today's behaviour).
Decisions (2026-10-12, project RBAC):
- Submitting under a project is an RBAC permission,
project:submit(scope typeproject), checked likequeue:submit. - Membership implies the grant: being a member of P gives an implicit
project:submiton P only (membership is not inherited). Roles may also grantproject:submitexplicitly (e.g. to service accounts); aprojectscope covers the named projects and their subtrees, as for everyprojectscope. - Project admins: a project's
admins(users and user groups) get an implicit grant of every job permission andproject:viewover the project's whole subtree. Being an admin does not implyproject:submit. - In production every job submission goes through the apiserver (REST:
xcecli, UIs); the harness and direct client-library submits are test tools. The apiserver is therefore the one place that resolves the project and checksproject:submit; the controller records what the apiserver decided and does not re-check RBAC. - In this stage project definitions, members and admins change only through cluster config, so only the cluster administrator edits them; project admins manage jobs, not the project.
Introduction
This feature makes projects first-class cluster configuration entities that control resource ownership.
- Definitions: admins define a tree of projects named by path, each with its own members and owned shares, and say which user groups default to which project.
- Submit: the system resolves each job's project, admits only users holding
project:submiton it (every member does), and records the project on the job. - Shares: each project owns part of its parent's resources and holds at most that part with its own and its descendants' jobs; a held job's reason names the project.
- Visibility: users and admins can see projects and their usage against their shares.
- RBAC: submitting under a project is the
project:submitpermission, implied by membership; a project's admins get job permissions over its subtree; roles can grant the same permissions explicitly. - Compatibility: a cluster that defines no projects keeps today's behaviour.
Boundary Context
- In scope:
- nested project definitions (path name, description, members, admins, default-for groups,
share,member_quota) and a cluster default project; - default-project resolution, the
project:submitcheck at submit, and an "open" mode when no projects are defined; - pending-job behaviour when a project or membership changes;
- owned shares (absolute or percentage), the unassigned part, and
member_quota, compiled into the scheduler's limits, applying end to end, with project-naming pend reasons; - rejecting project-scoped entries in
limits.yaml; - overlapping membership (a user in several projects);
- project listing (as a tree) and detail with usage against shares;
- RBAC: a
project:submitpermission implied by membership; project admins with implied job permissions over the subtree; aprojectscope for job permissions (covering the project's subtree) and aproject:viewpermission; - job filtering by project.
- nested project definitions (path name, description, members, admins, default-for groups,
- Out of scope:
- borrowing idle shares above a project's own (
max), protecting owners from borrowers, and reclaim by preemption (project-borrowing); - fairshare;
- any project influence on job ordering or priority (queues own ordering);
- accounting history and billing of finished jobs;
- runtime edits of a project's members, admins or shares by project admins (
project-delegated-membership; in this stage only the cluster administrator changes them, through cluster config); - per-user default projects in
users.yaml; - membership inherited between parent and child projects;
- moving or renaming a project while keeping its jobs' recorded name;
- wildcard or pattern matching of member names;
- retiring the rest of
limits.yaml(user, queue and host scopes) and moving per-host slots to host config (limits-to-owners); - aggregate caps on queues (dropped by decision 2026-10-12, not deferred).
- borrowing idle shares above a project's own (
- Adjacent expectations:
- Project shares are written on the project (decision 2026-10-12, reversing "
limits.yamlstays the place where project quotas are written"). The scheduler's limit plugin enforces them as generated limits; admins never write those limits. peer-authentication-hardeningmakes the controller accept user requests in production mode only from an authenticated apiserver connection; Requirement 3.12 relies on it, and until it lands the controller's trust in the apiserver's authorization is a trusted-network assumption.- Shares count what the scheduler allocated or reserved, not measured use; a job without a memory reservation can exceed its project's memory share at run time (accepted; profile default reservations close the gap).
- User groups come from the existing user-group configuration.
- Grants made through the role API are subject to the existing
rbac-persistencegap (not durable across apiserver restart) until that spec lands. job-config-mergetreats project shares as ceilings: they hold or admit a job but never change its values.- The defect that resolved limit host and project scopes against each other's group tables (it also broke host-group limits) was fixed separately as bug-1933 (
6ecd418d); Requirement 6.8 keeps it as a requirement.
- Project shares are written on the project (decision 2026-10-12, reversing "
Requirements
Requirement 1: Project definitions
Objective: As a cluster admin, I want to define projects and their members in the cluster configuration, so that project ownership is explicit and versioned with the rest of the cluster config.
Acceptance Criteria
- The configuration tool shall accept project definitions consisting of a path name, an optional description, members (user names, user-group names, or
all), optional excluded members, optional admins (user names or user-group names), an optional list of user groups for which the project is the default, an optionalshare(a map from resource name to an absolute amount or a percentage of the parent's share) and an optionalmember_quota(a map from resource name to an absolute amount). - The configuration tool shall treat a path name of one segment (e.g.
eng) as a top-level project and a path of several/-separated segments (e.g.eng/hpc/cfd) as a child of the project named by the path without its last segment. - The configuration tool shall accept an optional cluster default project naming one defined project.
- If a project definition repeats a path name, uses a reserved name, has an empty or malformed path segment, names a parent project that is not defined, names an undefined user group (in members, excluded members or admins), lists a default-for user group that is not covered by the project's members, has a
shareormember_quotaentry naming an undeclared resource or giving a negative or non-numeric amount, has a percentage inmember_quotaor a percentage above 100% inshare, declares asharefor a resource its parent (when it has one) declares no share for, gives siblings shares of one resource in different forms (absolute and percentage), or gives siblings shares that add up to more than 100% or, when all are absolute and the parent's is absolute, to more than the parent's share, then the configuration tool shall reject the configuration with an error identifying the project and the offending field. - If the cluster default project names an undefined project, or a project whose members do not include every user, then the configuration tool shall reject the configuration with an error naming the cluster default project.
- When a configuration with changed project definitions is applied by live configuration upgrade, the Cluster shall apply the new definitions to all subsequent submissions and scheduling decisions without a restart.
- The Cluster shall keep project definitions, including their shares, across controller failover and restart identically to other cluster configuration.
- If the limit configuration contains a limit scoped to projects (listed or per project), then the configuration tool shall reject the configuration with an error naming the limit and stating that project shares are written on the project.
Requirement 2: Project resolution at submit
Objective: As a user, I want my job to get the right project when I do not name one, so that I do not have to pass -P for my usual project.
Acceptance Criteria
- When a job is submitted with a project path name, the Cluster shall use that project for the job, whether it is a top-level, inner or leaf project.
- When a job is submitted without a project name and at least one defined project lists a user group the submitter belongs to as default-for, the Cluster shall use the first such project in configuration order.
- When a job is submitted without a project name and no default-for rule matches the submitter, the Cluster shall use the cluster default project if one is configured, and otherwise the project
default. - The Cluster shall record the resolved project name on the job at submit, show it in the job's details, and keep it unchanged across later configuration changes, failover and restart.
- The Cluster shall apply the same project resolution to every submission through the REST API (
xcecliand any other client of the API) and to every element of a job array. - The Cluster shall identify a project at submit only by its full path name; a name that is only the last segment of a defined project's path (e.g.
cfdforeng/hpc/cfd) is treated as the top-level project of that name.
Requirement 3: Submit authorization
Objective: As a cluster admin, I want only users allowed on a project to submit work under it, so that projects control who may use their resources.
Acceptance Criteria
- While at least one project is defined, when a job is submitted under a project, the Cluster shall accept the job's project if the submitter holds
project:submiton it: implicitly as a member of that project, or through a role grant whose scope covers it. - If, while at least one project is defined, a job is submitted under a project on which the submitter does not hold
project:submit, then the Cluster shall reject the submission with an error naming the project and stating that the user may not submit under it. - If, while at least one project is defined, a job is submitted under a project name that is not defined, then the Cluster shall reject the submission with an error naming the unknown project.
- Where the submitter has the cluster administrator role, the Cluster shall accept any defined project without a further check.
- The Cluster shall treat a user as a member of a project if the user is listed directly in that project's own members, belongs to a user group listed there, or those members include
all, and the user is not excluded there. - The Cluster shall not infer membership of a project from membership of its parent, its children or any other project; the implicit
project:submitof a member covers that project only. - The Cluster shall treat a role grant of
project:submitwith scope typeallas covering every project, and with scope typeprojectas covering the named projects and their descendants. - Where a user may submit under several projects, the Cluster shall accept the user's jobs under any of those projects.
- The Cluster shall reject an unauthorized or unknown-project submission before the job is assigned a job ID or recorded.
- The Cluster shall check
project:submitin addition to, not instead of, the existing queue permission. - The Cluster shall record on the job whether its project was authorized by membership, by a role grant or by the cluster administrator role.
- The apiserver shall be the only component that resolves the project and checks
project:submit; the controller shall record the project and authorization basis it receives with the submission without checking them again. - While the controller runs in test mode, when a job is submitted directly to the controller, bypassing the apiserver (harness and client-library test paths), the Cluster shall record the project name as given (an empty name as
default) with the authorization basisdirect, without project resolution or authorization; Requirement 5 still applies to the pending job, except the membership hold of 5.2. (In production mode such a submit is rejected bypeer-authentication-hardening.)
Requirement 4: Open mode when no projects are defined
Objective: As an operator of an existing cluster, I want clusters without project definitions to keep working as today, so that upgrading does not break existing submissions.
Acceptance Criteria
- While no project is defined in the cluster configuration, the Cluster shall accept any project name at submit without a
project:submitcheck. - While no project is defined in the cluster configuration, when a job is submitted without a project name, the Cluster shall record the project
default. - When the first project definitions are applied to a cluster that had none, the Cluster shall apply Requirement 5 to pending jobs whose recorded project is undefined or does not include their owner.
- While no project is defined, the Cluster shall not check
project:submit.
Requirement 5: Pending jobs after project changes
Objective: As a cluster admin, I want project changes to take effect on work that has not started, without disturbing running work, so that removing a project or a member is effective and reversible.
Acceptance Criteria
- If, while projects are defined, a pending job's recorded project is no longer defined, then the Scheduler shall keep the job pending with a reason naming the project and stating that it is not defined.
- If a pending job's project was authorized by membership and its owner is no longer a member of the job's recorded project, then the Scheduler shall keep the job pending with a reason naming the project and stating that the owner is not a member. A job authorized by a role grant or the cluster administrator role is not held when roles change.
- When a later configuration change makes a held job's project defined again, or restores its owner's membership, the Scheduler shall consider the job for scheduling again without user action.
- While a job is running, the Cluster shall not stop, requeue or alter the job because of a change to its project's definition or membership.
- The Cluster shall allow an admin to kill or modify a job held by Requirement 5 using the existing job controls.
Requirement 6: Owned shares
Objective: As a cluster admin, I want each project to own a share of its parent's resources and hold no more than it, so that a project can always get what it owns and no project, or member of one, takes more than its part.
Acceptance Criteria
- The Scheduler shall compute each project's effective share of a resource as its absolute amount, or as its percentage of its parent's effective share; a top-level project's percentage is of the cluster's capacity of that resource (the static total over the cluster's hosts), so percentage shares change when hosts are added or removed.
- The Scheduler shall count against a project's share of a resource the amount of that resource allocated to every running job whose recorded project is that project or one of its descendants.
- The Scheduler shall treat a project's unassigned part of a resource as its effective share minus the effective shares of its children that declare a share of that resource (the cluster's capacity minus the top-level shares at the root), and shall count against it the jobs of the project itself and of its descendants that are not under a child with a share of that resource.
- The Scheduler shall count against a project's
member_quotafor a resource, separately for each user, the amount of that resource allocated to that user's running jobs whose recorded project is that project or one of its descendants, whether or not the user is a direct member of that project. - The Scheduler shall dispatch a job only if, for every resource it is allocated, the job fits the share of every project on its path that declares one, the unassigned part it draws from, and the
member_quotaof its recorded project and of every ancestor. - The Scheduler shall not let a project hold more than its effective share by using capacity owned by another project (no borrowing in this feature).
- While no project declares a share of a resource, the Scheduler shall not restrict that resource by project.
- The Scheduler shall count a job only against its recorded project and that project's ancestors, never against other projects its owner belongs to.
- If dispatching a job would exceed a share, an unassigned part or a member quota, then the Scheduler shall keep the job pending with a reason naming the project, the resource, which of the three is full, and its current usage and amount; the reason shall not name an internal limit.
- When usage falls below a share, unassigned part or member quota, the Scheduler shall consider jobs held by it for scheduling again.
- When a configuration change or a change in the cluster's hosts changes an effective share, the Scheduler shall apply the new amounts to all subsequent scheduling decisions without a restart, keep counting running jobs against their recorded project and its ancestors, and never stop or alter a running job because a share is now below its usage.
- Where absolute shares of top-level projects add up to more than the cluster's current capacity of a resource, the Cluster shall report it in project diagnostics and in a configuration-tool warning; the Scheduler shall still apply each share as written.
- The Scheduler shall resolve each generated limit's project and user scopes against project and user names only, so that a project name never matches a host group or user group (the scope separation fixed by bug-1933).
- The live Scheduler and the scheduler simulator shall make identical share decisions for the same input.
- The Scheduler shall show a project path containing
/unchanged in pend reasons, decision traces and diagnostics.
Requirement 7: Project visibility
Objective: As a user or admin, I want to see projects, their members and their usage against shares, so that I know which projects I can use and why a job is held.
Acceptance Criteria
- When a user lists projects through the REST API or
xcecli, the Cluster shall return every project the user may submit under (as a member or through a role grant) or administers, with its full path name and description, shown in tree order, and mark the project that would be the user's default, so that a submit client can offer them for choosing the project to charge. - Where the requester has the cluster administrator role, or a
project:viewpermission covering a project, the Cluster shall include that project in the list with its members, excluded members and default-for user groups. - When a user requests a project's details, the Cluster shall show, for the project and for each of its ancestors, the effective share (and the form it was written in), the unassigned part, and every
member_quotaentry, each with the project it is set on, the resource, the amount and the current usage counted against it (including descendants' jobs; formember_quota, the requesting user's own usage, and every user's usage to an admin or a holder ofproject:viewcovering the project). - If a user requests the details of a project they are neither a member nor an admin of, and they have neither the cluster administrator role nor a
project:viewpermission covering the project, then the Cluster shall reject the request without revealing the project's members or usage. - When a user lists jobs filtered by project through the REST API or
xcecli, the Cluster shall return only jobs with that recorded project, or, when the user asks to include sub-projects, with that project or any descendant, subject to the user's existing job-visibility permissions. - While no project is defined, the Cluster shall report that projects are not configured when projects are listed.
- The scheduler's diagnostic interface shall show, for a named project, the shares, unassigned parts and member quotas that apply to it (its own and its ancestors'), their effective amounts and their current usage.
Requirement 8: Project-scoped access control
Objective: As a cluster admin, I want project admins and roles to hold rights over the jobs and details of a project and its sub-projects, so that project leads can manage their project's work without being cluster administrators.
Acceptance Criteria
- The Cluster shall accept roles that grant a job permission (view, kill, modify or any other existing job permission) with the scope type
projectand a list of project path names as scope values. - The Cluster shall treat a
project-scoped grant on a project as covering that project and every descendant project. - When a user performs a job operation, the Cluster shall allow it if any of the user's grants for that operation covers the job (scope
all,selffor the user's own jobs, orprojectcovering the job's recorded project), and shall reject it with a permission error otherwise. - When a user lists jobs, the Cluster shall return exactly the jobs covered by the user's view grants.
- The Cluster shall accept roles that grant
project:viewwith the scope typeallorproject; aprojectscope covers the named projects and their descendants. - If, while projects are defined, a role is created or updated with a
projectscope value that names an undefined project, then the Cluster shall reject the change with an error naming the unknown project. - While a
projectscope value names a project that is no longer defined, the Cluster shall treat that value as covering no project. - The Cluster shall leave the behaviour of existing roles and grants unchanged; the default RBAC configuration shall grant no
project-scoped permission and no explicitproject:submitto any role other than the cluster administrator. - The Cluster shall accept roles that grant
project:submitwith the scope typeallorproject. - The Cluster shall treat each admin of a project (a listed user, or a member of a listed user group) as holding every job permission and
project:viewwith scopeprojecton that project, which covers its subtree, without any role assignment. - The Cluster shall not give a project admin
project:submiton the project, nor any right to change a project's definition, members, admins or shares, by being its admin. - When a configuration change adds or removes a project admin, the Cluster shall apply the change to all subsequent requests without a restart.
- When a user lists their projects, the Cluster shall mark the projects the user administers.
Requirement 9: Compatibility and durability
Objective: As an operator, I want projects added without data migration or downtime, so that existing jobs and records keep working.
Acceptance Criteria
- When a job recorded before this feature is recovered, the Cluster shall keep its recorded project name, treating an empty name as
default. - The Cluster shall accept a cluster configuration without any project definitions and behave as Requirement 4 describes.
- When a configuration tool of an earlier version pushes a configuration without project definitions, the Cluster shall treat it as defining no projects.
- While no project declares a share or member quota, the Scheduler shall dispatch jobs exactly as it would without this feature's share checks.