Repository navigation
CI compile-jobs from the model (slice A): derive cargo build parallelism, fail-closed serial, live nextest-gate consumer - #5546
Conversation
# Conflicts: # dsl/std/realization_width.dag
…ed fleet catalog (uncapped → emit-time-constant) quick-ant host-measured the CI fleet UNCAPPED (memory.max=max at service/slice/root, MemoryMax=infinity), so the binding compile budget is physical RAM, not a cgroup cap, and the derive can run from committed facts rather than a live read. - operator_fleet: operator_fleet_host_memory_bytes() reuses compute_host_ram_bytes_total (single authority for fleet RAM, §3); operator_fleet_kernel_pid_max for the (non-binding, uncapped) pids term. - rust_gates_ci: rust_gate_build_jobs_args() now keys ci_compile_jobs on the committed fleet budget instead of the runtime-0 seam. Still fail-closed serial (--build-jobs 1) while the committed measurement is CompileJobUnmeasured (Refuse short-circuits before the budget); auto-widens when the fleet measurement flips to Measured. Witnesses + compile-clean gate green by execution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…review: drop the Int unit-waist) claude-opus-4-7 REQUEST_CHANGES (#5546): ci_compile_jobs's `memory_budget_bytes: Int` was a flat-scalar unit field — parallel-representation debt. ByteSize is already the carrier upstream (compute_host_ram_bytes_total) and downstream (process_memory_aware_spawn_width(memory_budget: ByteSize)); the Int waist only unwrapped to re-wrap via byte_size(). Per DESIGN §3 single-authority / consume-never-fork (the standing unit-modeling hard-block), thread ByteSize through: - ci_compile_jobs / ci_compile_jobs_for: memory_budget_bytes: Int -> memory_budget: ByteSize; pass directly to process_memory_aware_spawn_width (no byte_size re-wrap; byte_size import dropped). - rust_gates_ci: rust_gate_build_jobs_args passes compute_host_ram_bytes_total(host: srv1_host) (ByteSize) directly. - operator_fleet: deleted operator_fleet_host_memory_bytes() (existed only to unwrap) + its imports. - witnesses: memory_budget call sites pass byte_size(...) ByteSize. Witness aggregate + compile-clean gate green by execution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed in a56ee73 — valid finding, this was the unit-modeling hard-block (flat-scalar Threaded
— sent from sleek-cat-446 |
…ew: no guessed pid_max default) claude-opus-4-7 APPROVE-with-note (#5546): operator_fleet_kernel_pid_max = 4194304 was a guessed host kernel fact authored as a flat scalar with no measurement-vs-default disposition — the pids term silently guessed while the memory term refuses to guess via CompileJobUnmeasured (a §5 fail-closed asymmetry). Fix: make the pids budget fail-closed-symmetric by sourcing it from the measurement. - ci_compile_measurement: CompileJobMeasured gains pids_budget: Int (alongside per_rustc_peak, procs_per_job). Unmeasured → the whole derive refuses; no standalone pids guess. - ci_compile_jobs / ci_compile_jobs_for: drop the separate pids_budget param; the Measured arm destructures it from the measurement. - operator_fleet: deleted operator_fleet_kernel_pid_max (the guessed constant) + the Int import. - rust_gates_ci: ci_compile_jobs(memory_budget, force_serial) — pids no longer a call-site arg. - witnesses: measured_with_pids(p) builds the sample; pids-binding witness sources pids from the measurement (24 → 8); memory/force-serial/unreadable witnesses unchanged in outcome. host_ram stays a committed fleet fact (a known hardware spec, not measured); the fleet pid ceiling is sourced from quick-ant's measured pids.max in slice B. Witnesses + direct compile (0 diagnostics) green by execution. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Addressed in You were right that Fix: — sent from sleek-cat-446 |
…ak (per-job placement divisor) (#5574) * WIP: ci is slow investiation * docs/plans: ci-merge-freshness decision record (stale-green root of the 3× fleet-red) Pins the 3× fleet-red to stale-green (PR validated against a pre-gate base, merged without re-validating current main) via the #5429 timeline receipts; ranks the merge-policy fixes (merge-queue >> require-up-to-date under the approval outage); scopes neat-ibex's reverse-staleness lens as complementary, not the 3×-red killer. Decision record for the operator's merge-policy call. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs/plans: scoped edge-(b) brief — rust-test↔consumed-.dag provenance (the §1 coverage keystone) Scoping artifact for the operator greenlight call. One declared fact (rust-test→ consumed-.dag closure on the existing NodeArtifactProvenance carrier) read in two directions: FIRE-when-consumed (coverage wall, fail-closed) and SKIP-when-unaffected (affordability selector) — DESIGN §4 one grammar both directions. Shared testgen-reflection blocker; first vertical slice; honest multi-day estimate. Build HELD for operator nod; decoupled from #5427. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs/plans: edge-(b) brief — fold in the coverage-completeness asymmetry (closure ⊇ reads) bright-stag's load-bearing sharpening: coverage (fire-on-change) is fail-OPEN to under-declaration (declaration drift re-opens the .dag→rust hole), while affordability (skip) is fail-safe to over-declaration. So (1) the completeness lens must check closure ⊇ actual-.dag-reads (CORRECTNESS), not mere presence — presence is the §5 faked-cache-key trap; (2) structural closure discovery IS the soundness, not optional polish — a hand-authored closure is the §3/§5 fork that silently re-opens the hole; (3) slice-1 must state whether its closure is structurally derived (proves the wall) or hand-listed (proves only the wiring). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ROADMAP: anchor the edge-(b) keystone brief from the rust-gate-coverage item (doc reachability) The new docs/plans/edge-b-rust-dag-provenance-brief.md was an orphan doc → the floor witness doc_graph_has_no_orphan_docs (dsl/test/claim/doc_reachability_witness_test.dag) RED on #5526. Fix per the rule (every docs/**/*.md reachable from a ROADMAP/DESIGN root): add a terse pointer on the existing §1 rust-gate-coverage line, its correct semantic home — edge-(b) is the .dag→rust coverage wall that #5427 (the .rs-hole-closer) does not close. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ROADMAP: trim edge-(b) line 36 to short summary + status (bright-stag refinement) Per the operator short-lines rule (bright-stag enforces): move the mechanism density (rust-test↔consumed-.dag closure, fail-closed both directions) into the brief; keep the ROADMAP line a short scannable summary + pointer + explicit no-overclaim status ('SCOPED / pending operator greenlight'). Build is NOT greenlit; the line now says so. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * WIP: ci is slow investiation * fmt: rustfmt the cgroup-peak measurement additions (claim_executor) The hand-written binding_cap_cgroup_dir / cgroup_peak_pids_at_binding_ancestor / sccache_server_cgroup_rel helpers were not rustfmt-clean; this only reflows them. No logic change. Fixes the rust_tests fmt failure on the held draft #5564. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * CI measurement: rust_tests-job leaf cgroup peak + --measure-cgroup-peak (per-job placement divisor) Second half of the whole-tree CI memory measurement (companion to #5564's ci-job emit): a claim_executor --measure-cgroup-peak standalone mode + a rust_tests-job ci.yml step that calls it, so the rust_tests job (the binding ~16-23 GiB per-job constraint, measured live on srv1) emits its own cgroup peak. Corrects #5564's cap-ancestor read for the real fleet. Live srv1 inspection (operator-granted) shows the runner units run MemoryMax=infinity (UNCAPPED), so binding_cap_cgroup_dir returns None and the cap-ancestor read emits "unavailable". The measurement now reads memory.peak at the LEAF runner cgroup (the ephemeral per-job cgroup, always present) and reports capped-vs-uncapped + host MemTotal. One walk, all reads (single authority): the emit line carries memory.peak (usage) + memory.max (budget, =uncapped on the fleet) + host_ram + pids.current/max + the sccache server cgroup classified descendant-vs-sibling (the "accounted exactly once" decision) — consumed by the compile-jobs divisor (#5546) and the placement model (#5559). Stacked on #5564 (reuses its binding_cap_cgroup_dir / sccache scan). ci.yml regenerated via main_wet; drift gate green; fmt + clippy -D warnings + release build clean; --measure-cgroup-peak verified by execution. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Review fix (#5574): path-component prefix for sccache descendant check claude-opus-4-7 flagged that sccache_under_leaf used a bare string prefix, so a sibling like <leaf>-other.service would misclassify as a descendant (under-counts host_fixed_overhead — the fail-OPEN direction). Compare on path components: leaf itself, or a strict <leaf>/ prefix. Comment updated to match. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansrls@gunb.ai> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…Appropriation/LineItem/zero-based; recursive conservation + admission construction) (#5582) * budget-tree carrier: hierarchical memory budget, two-verdict (static conservation WALL + runtime reconcile HANDLER) Foundational §1 carrier for ROADMAP 1-budget-tree (operator: "model the whole machine as a memory budget tree; each level inherits a budget from its parent as a transaction"). Zero consumers yet — routed for review before any consumer edit. product.budget_tree models a node's allocated budget (capacity_intent) and its children's claims, with TWO DISTINCT regimes (never conflated — else a runtime ratchet masquerades as a compile wall): REGIME 1 node_conserves : Bool — STATIC conservation over AUTHORED budgets. Sum(children claims) <= parent budget. Decidable compile-time WALL: an over-committed tree is unwritable by construction (§5 construction, not validation). This is the stern-otter co-residence OOM made unwritable. REGIME 2 reconcile : Reconciliation — RUNTIME intent x MEASURED-actual. A fail-closed HANDLER (Realization), NOT a wall. Admit in QoS order (Guaranteed > Burstable > BestEffort); classify: AllSatisfied actual covers all claims Evicted best-effort/burstable shed to fit actual GuaranteedShortfall typed LOUD error — guaranteed set exceeds actual (genuinely under-provisioned; never a silent OOM, which matters most on the UNCAPPED fleet where the physical OOM-killer would otherwise pick random victims) Levels (L0 host / L1 concurrent runs / L2 within-run rustc+spawn-width) are BudgetNode INSTANCES; spawn-width #5444, placement R #5559, compile-jobs N #5546 become consumer leaves that IMPORT their parent allocation (divide-once), not parallel facts that re-divide host_ram. Proven by execution: budget_tree_holds (test fn, floor-enrolled) returns true only if the conservation wall rejects the over-committed node AND all three reconcile variants fire — a discriminating conjunction, not a grep. Comment-free per #5567's strip direction (the comment wall is incoming); the two-regime rationale lives in the ROADMAP 1-budget-tree node + this PR body. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * review fixes (opus-4-7 #5582): dissolve priority_eq to canonical ==, add ByteSize algebra to std.measure Finding 1 (predicate dissolution / §4 ops-from-inhabitance): deleted priority_eq (Bool helper minted per-coproduct with a `_ => false` wildcard) — claims_of_priority now routes through canonical `==` (Value::eq, the single CanonKey authority; same form as extdeps oci linux.dag namespace equality). Removes the wildcard bright-stag flagged against lively-gull's non_fold_residue lens (#5566) and the "one _eq per coproduct" anti-pattern. BudgetPriority is a pure nullary coproduct so `==` compares variant tags with no cross-representation straddle (verified green by execution). Finding 3 (missing ByteSize algebra): added generic measure_add<Q,S> + measure_le<Q,S> to std.measure (the canonical home all reviewers named). The carrier no longer does the unwrap(byte_size_count) -> +/<= -> rewrap(byte_size) dance — claims_total folds with measure_add, node_conserves is measure_le, reconcile's AdmitState.used is ByteSize. Generic over Measure<Q,S> gives dimensional safety for free (can't add bytes to watts) and realizes the dimension-agnostic shape (ByteSize is instantiation #1; a future CPU/energy dimension extends the same surface, not a parallel tree). Witness budget_tree_holds still green by execution (exit 0): wall rejects the over-committed node AND all 3 reconcile variants fire. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ground budget tree in real accounting (extdeps); add tree recursion + admission-as-construction (opus-4-7 round 2) Operator: ground budget_tree in the real budgeting/accounting framework (start from en.wikipedia.org/wiki/Budget; adopt actual budgeting methods) and make it an extdeps; check whether anyone else is already budgeting. NEW extdeps/accounting/budget.dag — the single §3 authority for the budgeting framework, anchored to en.wikipedia.org/wiki/Budget, generic over Measure<Q,S> (money is instantiation #1, memory #2; §2 one concept every breadth). Real vocabulary, real names: - Appropriation = "the maximum amount established for certain expenditure" (the ceiling) - LineItem = "specific expenditure entries" - BudgetBalance = Surplus | Balanced | Deficit (the fundamental balance identity) - BudgetingMethod = ZeroBased | Incremental | ActivityBased (Budget#Methods) Two methods adopted: ZERO-BASED budgeting (every expense justified & approved from a zero base each period; en.wikipedia.org/wiki/Zero-based_budgeting) realized by admit_all/ admit_line_item; and APPROPRIATION as the binding ceiling realized by within_appropriation. budget_tree.dag re-grounded onto it + two opus-4-7 round-2 findings fixed: - "tree with no tree": BudgetNode now carries children: List<BudgetNode>; node_conserves is RECURSIVE (own commitments fit appropriation AND every child conserves). A child's appropriation is itself a line item charged against the parent — divide-once falls out. - "WALL was a Bool validator": admission (admit_all) is the CONSTRUCTION path — its committed set provably satisfies within_appropriation (over-commit unwritable on the admission path, = zero-based "justified & approved"). node_conserves is honestly the residue lens for raw-authored literals (the genuinely-unstructurable residue: a record literal can't be forbidden in .dag), NOT relabeled a wall. Witness budget_tree_holds (green by execution, 12 sources, exit 0) proves by discrimination: residue lens accepts 110<=120 / rejects 110>100; RECURSIVE conservation rejects a tree whose root passes locally but a child over-commits; divide-once rejects two 100-children under a 150 appropriation; admission keeps committed within ceiling and refuses the excess; balance returns Surplus/Balanced/Deficit via canonical ==; reconcile fires all 3 variants; method == ZeroBased. Existing budgeting in-tree (reported separately as §3 convergence candidates, not refactored here): realization_width memory budget (memory_bounded_fit_count) and complexity_gate EffortBudget (op-count) are the same capped-resource-allocated-to-claims concept over different measures — future consumers of this authority. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Brian Searls <briansrls@gunb.ai> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
What & why
The rust-test floor node was the CI tentpole. I measured the lever (the original deliverable): the win is
cargo nextest run(process-per-test) — 1571s libtest → 341s nextest, 4.6× — NOT floor-node sharding (one 128-core runner; nextest already saturates ~46 cores). That swap landed in #5427.nextest exposed the next tentpole: the single-threaded compile clamped by
CARGO_BUILD_JOBS=1(set when sccache is active; its trigger is a sccache spawn/pids EAGAIN, not memory). This PR derives the cargo job count from the model instead of the hand-set clamp — the operator's "ban the hand-tuned flag; derive from the dag; compilation fails if there isn't enough information."This is slice A: the reusable mechanism + its first live consumer (the modeled nextest gate). It is additive and behavior-preserving (fail-closed serial) and mergeable on its own.
This PR (slice A — green by execution)
std/realization_width.dag:process_bounded_job_count+process_memory_aware_spawn_width— the 3-termmin(cores, ⌊0.8·mem÷per_rustc_peak⌋, ⌊pids÷procs_per_job⌋), a literal extension of the floor'smemory_aware_spawn_widthmin-fold (same 0.8 safety + fail-closed-on-zero), adding the pids/process term. + 4 discriminating witnesses.gunbc/ci_compile_measurement.dag:CompileJobMeasurement = CompileJobMeasured{per_rustc_peak, procs_per_job} | CompileJobUnmeasured. Committed state isCompileJobUnmeasured→ fail-closed by construction (an explicit variant, not a 0/None sentinel).gunbc/ci_compile_jobs.dag: the derive →DerivedCompileJobs = CompileJobs{count} | CompileJobsRefuse{reason}. Refuses on Unmeasured rather than guessing.compile_jobs_count_or_serialmaps Refuse → serial 1.force_serialmodels the EAGAIN cold-retry precedence.extdeps/rust/cargo_build.dag: typedbuild_jobs_argsslot onBuild+Nextest(argv splices it;[]default = backward-compat); spelling helperscargo_build_jobs_flag/cargo_nextest_build_jobs_flag— extdeps owns the-j/--build-jobsspelling (§3); the count is workflow policy.gunbc/operator_fleet.dag:operator_fleet_host_memory_bytes()reuses the single-authoritycompute_host_ram_bytes_total;operator_fleet_kernel_pid_max.tools/rust_gates_ci.dag:run_gatesderives the nextest gate's build parallelism viaci_compile_jobskeyed on the committed fleet budget → currently--build-jobs 1(fail-closed serial whileCompileJobUnmeasured; Refuse short-circuits before the budget), passed to the livecargo.Build.Nextestgate. Auto-widens when the committed measurement flips.Verified: witness aggregates green-by-execution; whole-tree
dsl_compile_clean_gate= ExitSuccess. No ci.yml drift (the rust gate runs at CI runtime via gunbc, not emitted).Follow-on (slice B — separate PR)
The actual ~16m displacement is the pre-gunbc bootstrap build step (
cargo build … --binsin load-bearingci_spec.dag), which can't run the.dagderive in-process. Since the fleet is uncapped (host-measured:memory.max=maxeverywhere), B bakes an emit-time constant-j Ninto ci.yml, removes theCARGO_BUILD_JOBSenv clamp, and re-derives-j 1on EAGAIN retry. B carries a §5 budget fix: the emit-timeNmust divide host RAM by the co-residence count (host RAM is shared once across placement × within-build), not claim the full 128 GiB per build. B is sequenced after quick-ant's cgroup-measurement step + the co-residence-authority lock, and the committed measurement must not flip toMeasureduntil B's divisor lands.