sched_queue_is_ready() in core.c was a `return 1` stub, so the picker
(builtin_fcfs and the legacy fcfs) took jobs from every queue and admin
queue inactivation never reached dispatch.
It now returns false for a queue with QUEUE_STAT_INACT_ADMIN or
QUEUE_STAT_INACT_WINDOW set, so an inactive queue's jobs stay pending.
QUEUE_STAT_CLOSED is deliberately not a gate (LSF semantics: closing only
blocks new submissions; already-pending jobs still dispatch). The status
reaches the core through the same queue add/update paths in both drivers,
so live and sim behave identically. test_queue_admin_control.c covers
open, closed and inactive queues on both fixture shapes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
27 cross-spec conflicts among the 30 pending specs (5 high), code gaps
against the five product goals and the no-shared-storage use case (silent
failures such as unenforced queue/host close and REST submit dropping job
limits, cwd, env and file transfers; inter-component and apiserver
security; file staging; monitoring and accounting; scheduling policy;
operations), and a tiered priority list (spec hygiene, P0 defects, P1
foundation specs, P2 features, P3 initiatives).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
xce-node-manager: a "Cross-Spec Interactions" section (pending a
requirements revision; approved criteria unchanged) with six points: a
supervision/probe module shared with service-supervision, node-manager
components vs service jobs, a lease-based provisioner role, role
declaration and worker stop on decommission for provisioned hosts, a
host-loss grace period longer than worker restart plus rejoin, and a future
service proxy under the reverse-proxy role. design.md Open Questions points
to it.
The service-supervision, host-failure-job-recovery, elastic-hosts,
resource-provisioner and service-endpoint briefs cross-reference the
matching points; the roadmap lists the xce-node-manager revision.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
New briefs: provisioning-demand (the scheduler knows a provisionable
catalog, tries host templates as virtual hosts and license headroom after
real resources fail, finalizes demand and idle supply at session end;
simulator support is a hard requirement), resource-provisioner (new
daemon, several instances owning providers via controller leases,
self-describing providers, automatic policy, durable request/instance
state; src/autoscaler is prior art only), elastic-hosts (host templates,
expected hosts, drain and decommission), elastic-shared-resources
(borrowed license tokens change the shared total).
Roadmap: the new specs, plus updates to resource-distribution-mapping
(shared pools) and peer-authentication-hardening (provisioner role).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
New briefs: host-failure-job-recovery (jobs on a lost host are requeued or
end EXIT after a grace period; stale copies fenced on rejoin; manual
requeue), service-supervision (profile kind service: restart policy and
liveness/readiness probes run by each replica's own worker, in-place
restart), service-replica-set (controller-held service object owning
independent replica jobs; event-driven replacement, scaling and rolling
update; no head agent, no promotion), service-endpoint (per-replica ports,
endpoints of ready replicas), job-resize (backlog, elastic jobs).
application-profile brief gains the service section; roadmap lists the new
specs, the existing-spec updates and the backlog item.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bug-1934: every job-permission check read a single scope and compared it
only with "self", so a job:view or job:control grant scoped to self_ug acted
like "all" (list, get by ID, control), and self + self_ug collapsed to self.
userJobScope() now collects all of a user's grants of a job permission
before deduplication and adds them up (all; self; self_ug = the user and
everyone sharing a user group). The job list appends a submit_user filter
for narrower scopes, get-by-ID keeps covered rows in a new slice (removing
from the ranged slice skipped rows) and answers 403, both control paths
and job output check the owner against the scope. User groups now reach
the apiserver: the config push converts cluster_config_t.ugroups and
OnUserGroupsUpdated() replaces the new store (nested groups, all,
exclusions). The default basic_user now also sees its user-group mates'
jobs, as its configuration says.
bug-1935: project_name and license_project are accepted job filters, so
xcecli jobs list -P works.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
job-config-merge: spec initialized from its discovery brief.
project-config: requirements generated (9 requirements): nested projects
named by path, membership-based submit check with no inheritance and
overlapping membership, group-default then cluster-default project, open
mode until projects are defined, pending jobs held with a reason after
project changes, roll-up quotas, project visibility, a project scope for
job permissions covering the subtree and a project:view permission,
compatibility.
Roadmap: project-delegated-membership backlog item (runtime membership
edits by project managers, deferred).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The limit plugin called build_limit() with the host-group and
project-group managers swapped, so a limit's hosts: members were looked up
among the project groups (a host-group name became a literal host name and
the limit never applied) and its projects: members among the host groups.
Pass them in the order build_limit() declares.
New test test_limit_scopes_resolve_against_own_group_kind; the test's
main() now initializes the interned-string table that group names need.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
New steering job-config-layering.md: queue = scheduling policy, application
profile = what the application needs and how it runs (resreq, task range,
execution settings, pre/post-exec, limits), project = resource ownership.
Only two interactions: the job overrides a profile attribute marked
default (fixed: not at all; select: only ANDed terms), and queue ceilings
or project quotas check effective values (reject if never satisfiable,
pend if full now). Replaces the simplified job > app > queue rules.
Queue pre_exec/post_exec are deprecated with queue res_req: the profile's
pre/post-exec fills the worker's existing outer layer, the job's own stays
the inner layer, allowed or forbidden by the profile.
job-config-merge and application-profile briefs and the roadmap follow;
CLAUDE.md lists the new steering file.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
New briefs: job-config-merge (one live merge of job, application profile,
queue policy and project into effective values; simplified rules: job
overrides profile, select ANDed, queue hard/task limits reject; queue is
policy only, res_req deprecated), project-config (project quotas v1) and
project-guarantee (backlog).
application-profile: the profile carries the job's resreq (incl. span) and
task limits; "frozen at submit" replaced by the live merge.
parjob-placement: span constraints live in the fixed-N planner and the
range search bisects over the planner's allowed counts; LSF 10.1 block,
ptile, stripe and HOSTLIMIT_PER_JOB findings recorded.
Roadmap: new specs, existing-spec updates, lsf-config-migration backlog.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The parjob plan generator split only maxTasks, so a job submitted with a
task range (minTasks < maxTasks) pended until all maxTasks tasks fit,
even when minTasks did (a tasks 2..5 job pended on a host with 2 free
slots).
The runner now handles a task range in sched_alloc_range_(): it probes
minTasks first and pends after that one search when it does not fit;
otherwise it places maxTasks when that fits, or binary-searches for the
largest count that fits and places it. Fitting is monotone in the count
for balanced plans, so the search is exact for today's plan shapes.
ctx->ntasks carries the count; parjob's job-level pass scales per-task
shared wants and global limits to it, and the generator gets the request
with its task count fixed to it. Fixed counts keep the old path.
The decision trace shows each count tried (task count N (minimum) /
(maximum), placing N tasks) and releases a fitting probe's hosts.
Test fixtures that used minTasks = 1 as filler for an N-task job are now
fixed-count.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
parjob-placement now covers full LSF-style parallel placement: task
range (-n min,max), capacity-driven partitioning, span[ptile=n],
span[hosts=1], span[block=n] and a host-count limit. bug-1931 points to
it for the full task-range behaviour.
pend-reason-push-dedup: skip pushing a job's pend reason to the
controller when it equals the last one the controller accepted.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The session-level unsatisfiable-resreq cache stored only the exhausted
attempt's main reason (an int). The allocators record shortfalls as
per-host/per-resource entries and leave main_reason at 0, so every cache
hit in the live scheduler pended its job with an empty reason. The
simulator registered no cache at all (its table lived on xce_scheduler_t),
so it searched every identical pending job again and its traces differed
from production.
- The cache table moves to xce_sched_core_t; cache_lookup/insert/clear and
the hook adapters take the core, with no cast to a driver struct.
sched_init() builds it, cache_clear() rebuilds it per session,
xce_sched_core_fini() destroys it.
- A cache entry is a deep copy of the attempt's full xce_job_preason_t
plus the job that proved the request unsatisfiable (core->alloc_jobid,
set by sched_allocator()). A hit moves a copy into ctx->reason; a miss
leaves ctx->reason untouched.
- main.c passes the same three adapters to the simulator through new
sched_sim_args_t fields; sched_sim_driver_start() sets them on the sim
core (plain field writes: white-box tests include sim.c alone).
- Decision trace: a hit renders "no plan generated: skipped, same request
as <job> proved unsatisfiable earlier this session" (new
XCE_TRACE_NOPLAN_KNOWN_UNSAT via sched_trace_known_unsat()); the outcome
shows the restored reasons.
- sim_verify keeps its main-only PEND compare (old recordings carry
main-only hits); comments and the scheduler-sim-live-allocation design
note updated.
Tests: full-reason assertions in the cache dispatch tests (RED before:
"a cache hit must restore the original attempt's FULL pend reason"),
test_alloc_cache.c ported to the new API (deep copy, clear frees, -EEXIST
frees), a sim-registered clear test, a renderer test, and a sim e2e
scenario with two identical unsatisfiable jobs (204, 205) where 205 is
skipped with 204's full reasons (RED before: no skip line).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- bug-1931: the parjob allocator ignores minTasks (a tasks 2..5 job pends
although 2 slots are free).
- bug-1932: the unsatisfiable-request cache drops pend reasons on a hit
and is unused in the simulator.
- .kiro/specs/parjob-capacity-partitioning: initialized spec with the
candidate heuristics for capacity-driven plan generation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The three live E2E tests (test_e2e_live_push, test_e2e_live_ha_push x2)
skipped with 'no xce_controller binary found': they run from the build
dir's repo mirror, where controller discovery only finds a sibling
xce.build/build.release/build.dbg controller dir that happens to exist
when pytest starts, and nothing ordered the controller link before the
xceadmin target. The xceadmin custom_target now depends on the
controller and passes its path, which meson-build.sh exports as
XCE_CONTROLLER_BIN (a caller's value wins; left unset in the Nix
package sandbox so nix build keeps skipping them). xceadmin: 373 passed,
none skipped.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The scheduler appended one NO_RESOURCE/MATCH_FAIL entry per host per
placement plan, so a pending parallel job carried plans x hosts copies
(~93k entries in the 1000-host bounds test). libxce gains
add_job_preason_once() with a per-context hash index keyed on
(host, resource, code) that keeps first-occurrence order; the
scheduler's three builders use it. add_job_preason() is unchanged for
other callers. The parjob equivalence test now expects each short host
once; the bounds test runs in ~2 s instead of ~19 s.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
repl_u64_str_eq() compared the packed jobid's raw decimal string with
the filter text, so a breakpoint on the dashed jobid the REPL prints
never fired. The filter is now parsed once when the breakpoint is set
(repl_parse_jobsel_arg, moved to repl_fmt.c) and matched with
repl_jobsel_matches: dashed ids, X[k], X[a-b] and raw decimal ids all
work, a bare array-aligned id also matches its elements, and an
unparseable value is rejected instead of creating a dead breakpoint.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bug-1928: repl_count_jobs() and repl_info_jobs() never destroyed their
job-table iterators (16 B per continue round); both now do, and the
baseline and trace wiring tests run with leak detection on.
bug-1929: continue-mode round summaries listed only pending jobs, so a
dispatch-only round printed an empty jobids= and rounds dispatching
different jobs collapsed together; rounds now record and list every
affected job (scheduler-sim-repl-verbosity Req 2.1, 3.2). The baseline
transcript changes only in its two jobids= lines.
bug-1930: trace on job now accepts the raw decimal jobid form that info
trace and info job accept, through a shared parser.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Feature validation round 1: tests for trace off keeping recall (1.5),
no logging from REPL trace sources (8.7), reconstructed records in the
trace file (8.1) and first in continue mode (7.3), output after trace
off matching an untraced run (10.4), and tracing enabled only from the
REPL (11.1). design.md now reflects the REPL-side file split, the added
API, the dropped file tag and the test files; duplicate trace
declarations removed from repl_internal.h.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 6.4: a 1000-host, 64-task parallel job traced at full level stores
exactly the line bound with exact omitted counts and counters; 10,000
traced decisions keep the trace store within 64 MiB without evicting the
newest. The scheduler guide lists the REPL trace files and test rules
and carries measured file sizes. Full all-components ASAN build passes.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 6.3: the contention and extra replays run with tracing off, all
jobs at summary and full level, and a job subset, in step and continue
mode; decisions, full pend reasons, --sim-output recordings, snapshots,
--sim-verify results and per-step accounting dumps are identical.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 6.2: injects plan-claims growth, snapshot, finalize, host-scan,
per-host uncommitted lookup, allocation-function and unknown-jobid
faults and asserts each traced outcome. Also fixes two deferred OOM
paths: a failed limit row keeps the host's verdict bits, and an array
listing that cannot be built is reported instead of silently truncated.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The pend outcome printed 'main reason: code 0' for every pend the
allocator builds, because 0 (libxce's 'no main reason set') fell through
to the unknown-code fallback. Found by the 6.1 end-to-end review.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 6.1: drives the real simulator REPL with tracing on for all and
selected jobs at both levels, asserting every Requirement 2-6 section,
ordering in step and continue mode and at breakpoints, info trace at a
breakpoint, after finish, for arrays and after a units change, and a
byte-identical trace file copy.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
repl_cmd_quit() (quit and EOF) released the trace settings while the
picker and allocator threads could still run; at a mid-session
breakpoint the allocator reads core->trace_ctl unlocked on every job
pop, a possible use-after-free just before exit. quit now cancels and
joins both threads first, as sched_sim_driver_run() does on return.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 5.3: session hooks around each sim session; trace-only decisions
are handled first and skip verification, recording, echo and
breakpoints; traced decisions are emitted after the echo gate and before
the breakpoint check; reconstructed dispatches get reconstructed
records; unclaimed traces are freed at both decision free sites.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>