myxce/.kiro/specs/project-config/brief.md
Rongsong Shen f3d4dba970
docs(spec): projects own shares of their parent's resources; project-guarantee becomes project-borrowing
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>
2026-10-13 06:38:39 +00:00

8 KiB

Brief: project-config

(/kiro-discovery, 2026-10-09, Path E "configuration layering". Reference: IBM Spectrum LSF 10.1 Administering LSF pp. 107 (default project), 446-453 (resource allocation limits, PROJECTS / PER_PROJECT); Using LSF on UNIX p21 (bsub -P).)

Problem

Projects are meant to control resource ownership: who may charge work to a project and how much of the cluster a project may use. Today a project is only a free-text label the user types; anyone can name any project, project limits cannot be configured end to end, and nothing shows usage per project.

Current State

  • Submit data carries project_name (JobSubmitData, controller.capnp:582 → job_submit_data_t, types.h:356-368); xcecli jobs submit -P (xcecli/cmd/jobs/submit.go:60); the apiserver passes it through (backend/convert.go:307-317) without any check.
  • The scheduler maps an empty name to "default" (sched_get_project, scheduler/src/core.c:617-623). SchedProject (capnp:1260) has name and priority but no config source.
  • Limits already accept project scopes (LimitConfig, limits.yaml; CONSUMER_PROJECT, lib/xcelimit.c:23-26, vres key /p=<project>), but the scheduler's project-group manager pgmgr is created empty (core.c:185) and never loaded (scheduler.c:1144-1145 loads only ugmgr/hgmgr), so project limits cannot be configured to work.
  • No project fairshare, accounting view or access check exists.

Desired Outcome (v1 = owned shares; user decisions 2026-10-09, 2026-10-12)

  • Project definitions in the cluster configuration: path name, description, members (users and user groups), share and member_quota; plus a cluster default project. (No priority: queues own ordering.) Schema, YAML, JSON schema, xceadmin mapping, live-upgrade replication.
  • Project selection at submit: -P > the user's default project (if defined) > the cluster default > "default" (LSF p107 order, without environment variables).
  • Access check at submit (user decisions 2026-10-12): an RBAC permission project:submit, implied by membership of the project and grantable by roles (checked in the apiserver only: production submits always go through it, 2026-10-12), with a clear error; cluster_admin keeps its existing bypass.
  • Project admins: a project's admins get every job permission and project:view over its subtree, implied by config like membership. Only the cluster admin edits projects in this stage (config push).
  • Owned shares (user decisions 2026-10-12, replacing "PROJECTS / PER_PROJECT limits in limits.yaml"): written on the project itself, rolling up the tree, with pend reasons naming the project; see job-config-layering.md "Resource control".
  • Visibility: project shown in job details and listings; per-project usage against its limits (REST, xcecli, REPL info limit).
  • Merge input: the project is one level for job-config-merge (default project; any project-level defaults decided in requirements).

Approach

Follow the queue/usergroup config pattern (config-design.md): projects are config entities pushed to controller and scheduler (Conf4Sched), the scheduler fills pgmgr from them, and the existing limit machinery does the rest. Projects already reach the scheduler on each job, so the WAL needs no new job field.

Owned shares compile to limits (user decisions 2026-10-12). The project tree is the only place concurrent use is controlled: a project owns a share of its parent's resources and, in v1, holds at most that share (no borrowing; project-borrowing). The limit plugin stays as the internal mechanism and admins no longer write limits. Semantics: job-config-layering.md "Shares".

projects:
  - path: eng
    members: [eng-ug]
    share: { slots: 400, gpu: 16, mem: 2T }   # carved from the cluster
    member_quota: { slots: 64 }      # per user, over eng's whole subtree
    admins: [eng-leads]              # job control over eng's subtree
  - path: eng/hpc
    members: [alice, bob]
    share: { slots: 60% }            # 60% of eng's 400 slots
  - path: eng/ai
    members: [carol]
    share: { slots: 40%, gpu: 16 }
  - path: eng/tools                  # no share: draws from eng's unassigned part
    members: [eng-ug]
  • On each config push, and when the cluster's capacity changes, the scheduler generates per project P and resource r:
    • a share limit proj:<P>:<r> (project consumer {P} matching P and its descendants) with P's effective share. A nested percentage is the product of the percentages down from the root (an absolute amount once an ancestor is absolute), so it compiles to the plugin's existing percent form, which since bug-1834 is a fraction of the static total capacity (limit.c update_limit_res_value);
    • an unassigned-part limit projpool:<P>:<r> (consumer {P and its descendants}, excluding the subtrees of P's children with a share of r, using the consumer's existing exclude list) with P's share minus its children's shares; the root gets one when any top-level share of r exists;
    • for member_quota, projm:<P>:<r> (same consumer as the share limit plus users all per user, vres key limit=projm:...:<r>/u=<user>, lib/xcelimit.c:402). New in the plugin: descendant matching on the project consumer.
  • The path contains /, which is also the vres key's field separator; the trace splitter (sched_trace_capture.c:40, sti_copy_scope) would render p=eng/hpc as p=eng,hpc. Escape the project field in the key or use another internal separator (design).
  • Project-scoped entries in limits.yaml are rejected by xceadmin with a pointer to project shares; the rest of limits.yaml is retired by limits-to-owners.

Scope

  • In: project config model and pipeline (share, member_quota); default project resolution; submit access check; pgmgr loading; shares compiled to limits and enforced end to end; display; tests (unit, sim, live).
  • Out: borrowing idle shares (max), protecting owners from borrowers and reclaim by preemption (project-borrowing); fairshare; accounting history/billing.

Boundary Candidates

  • Config model and pipeline.
  • Submit resolution and access check (apiserver, controller).
  • Scheduler project groups and limits.
  • Display surfaces.

Out of Boundary

  • Scheduling-order policy (queues), application settings (profiles).

Upstream / Downstream

  • Upstream: config pipeline (admin-config-tool, config-upgrade-snapshot), peer-authentication-hardening (authenticated apiserver connections), limit plugin (plugins/limit.c, lib/xcelimit.c), RBAC (rbac_check.go).
  • Downstream: job-config-merge (project level), project-borrowing, limits-to-owners, lsf-config-migration, rbac-persistence (if access uses RBAC scopes).

Existing Spec Touchpoints

  • Extends: none.
  • Adjacent: scheduler-sim-diagnostics (info limit), resource-distribution-mapping, rbac-persistence.

Design questions (RBAC, 2026-10-12)

  • Resolved 2026-10-12 (user): production submits always go through the apiserver; harness and direct client-library submits are test tools. The apiserver resolves the project and checks project:submit (membership and role grants); the controller records the project and authorization basis it is given and never re-checks RBAC. A direct submit is recorded as given with basis direct in test mode (Req 3.13); in production mode peer-authentication-hardening rejects it.
  • The job's authorization basis (Req 3.11) is a new job field: WAL fact and scheduler snapshot must carry it (field completeness, persistence.md).

Constraints

  • Middleware chain unchanged; additive Cap'n Proto; old jobs keep their recorded project name even if the project is later removed from config (requirements decide what a pending job with a removed project does).
  • Project paths are identities: a job keeps the path it was submitted under; usage of a running job is never moved by a tree change.