diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85aa13c9389..8e8522713f2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -265,68 +265,3 @@ jobs: git fetch --no-tags origin main 2>/dev/null || true "$ROOT/target/release/claim_executor" --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --plan-entry src/v2/workflow/ci_floor_plan.dag --plan-function gunbc_ci_regen_floor_batches --notice-title "self-host fixed-point (regen + staleness) — required; folded into ci job" timeout-minutes: 270 - emit_determinism: - runs-on: [self-hosted, linux, arm64] - needs: [ci] - timeout-minutes: 50 - steps: - - name: Checkout - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - name: Isolate toolchain dirs - run: | - rm -rf "$RUNNER_TEMP/rustup" "$RUNNER_TEMP/cargo" - echo "HOME=$RUNNER_TEMP" >> "$GITHUB_ENV" - echo "CARGO_HOME=$RUNNER_TEMP/cargo" >> "$GITHUB_ENV" - echo "RUSTUP_HOME=$RUNNER_TEMP/rustup" >> "$GITHUB_ENV" - echo "MAKEFLAGS=" >> "$GITHUB_ENV" - echo "CARGO_BUILD_JOBS=4" >> "$GITHUB_ENV" - if sccache --show-stats >/dev/null 2>&1; then - echo "RUSTC_WRAPPER=sccache" >> "$GITHUB_ENV" - echo "CARGO_INCREMENTAL=0" >> "$GITHUB_ENV" - fi - - name: Setup Rust - uses: actions-rust-lang/setup-rust-toolchain@v1.16.0 - with: - components: rustfmt - cache: false - rustflags: '' - env: - HOME: ${{ runner.temp }} - - name: Pin rustup default (isolated RUSTUP_HOME has no default toolchain) - run: | - rustup default "$(rustup show active-toolchain | awk '{print $1; exit}')" - if [ -x "$CARGO_HOME/bin/cargo" ]; then CARGO_BIN="$CARGO_HOME/bin/cargo"; else CARGO_BIN="$(command -v cargo || true)"; fi - if [ -z "$CARGO_BIN" ]; then echo "::error::no cargo binary: neither the isolated $CARGO_HOME/bin/cargo shim nor PATH carries one"; exit 1; fi - echo "CARGO_BIN=$CARGO_BIN" >> "$GITHUB_ENV" - - name: Cache Cargo - uses: actions/cache@v5 - with: - path: | - ${{ runner.temp }}/cargo/registry/index/ - ${{ runner.temp }}/cargo/registry/cache/ - ${{ runner.temp }}/cargo/git/db/ - target/ - key: cargo-ci-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }} - restore-keys: | - cargo-ci-${{ runner.os }}-${{ runner.arch }}- - - name: Download release-bins artifact - uses: actions/download-artifact@v4 - with: - name: release-bins - timeout-minutes: 10 - - name: Unpack + verify release bins (claim_executor --verify-build-artifacts; fail-closed) - run: | - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - mkdir -p "$ROOT/target/release" - tar -xzf "$ROOT/release-bins.tgz" -C "$ROOT/target/release" - rm -f "$ROOT/release-bins.tgz" - "$ROOT/target/release/claim_executor" --verify-build-artifacts "$ROOT/target/release/claim_executor" "$ROOT/target/release/gunbc" "$ROOT/target/release/floor_skip_discovery_witness" "$ROOT/target/release/discover_source_root_ingest" "$ROOT/target/release/claim_batch" "$ROOT/target/release/regen_stage0" "$ROOT/target/release/interp_recorded_fixture_witness" "$ROOT/target/release/v1_src_dag_parse" "$ROOT/target/release/auth_declared_but_unwired_witness" "$ROOT/target/release/bootstrap_witness" "$ROOT/target/release/dag_collect_fingerprint_witness" "$ROOT/target/release/diagnostics_witness" "$ROOT/target/release/effects_rest_transport_witness" "$ROOT/target/release/infer_semantics_witness" "$ROOT/target/release/parse_witness" - timeout-minutes: 5 - - name: Emit determinism gate (two full-corpus emits, diff -r; serial job per operator 2026-07-11) - run: | - ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd) - git fetch --no-tags origin main 2>/dev/null || true - "$ROOT/target/release/claim_executor" --source-root "$ROOT/dag" --source-root "$ROOT/src/v2" --plan-entry src/v2/workflow/ci_floor_plan.dag --plan-function gunbc_ci_emit_determinism_batches --notice-title "emit determinism (two full-corpus emits, diff -r byte-identity) — serial job, off the floor path" - timeout-minutes: 30 diff --git a/dag/gunbc/ci_gate.dag b/dag/gunbc/ci_gate.dag index 4bc8ab349c5..4bc5112f265 100644 --- a/dag/gunbc/ci_gate.dag +++ b/dag/gunbc/ci_gate.dag @@ -10,4 +10,3 @@ type Gate | RegenVerifyGate | SelfHostReadsRealBytesGate | SelfHostStalenessGate - | EmitDeterminismGate diff --git a/dag/gunbc/ci_materialization.dag b/dag/gunbc/ci_materialization.dag index ac0fe61a72a..d252c47e337 100644 --- a/dag/gunbc/ci_materialization.dag +++ b/dag/gunbc/ci_materialization.dag @@ -134,7 +134,7 @@ fn workflow_run_steps_count(w: Workflow) -> Int { w.jobs |> fold(init: 0, f: (acc, j) => acc + job_run_steps_count(job: j)) } -data ci_cost_floor_exempt_step_names_note: String = "Cost-floor roster BY STEP NAME, resolved to script identities against the live workflow at gate time (never a stale copied string): the two prelude snippets run in every job by construction — plural sites, workflow-isolation LCA — but each is a sub-second environment write whose store-tier discharge would cost more than the recompute it saves. The acceptance is TYPED (AcceptedBelowCostFloor), so the roster is countable and reviewable; an entry here is a claim that the script is below the cost floor, not an escape from the ladder. Third entry (2026-07-11 job split): the unpack+verify step runs once per artifact-consuming job (ci, emit_determinism) — plural by construction, but NOT duplicate computation: it is the per-consumer materialization arm of the release-bins artifact store (the transfer/decode cost of the store tier itself), and no store tier can discharge an untar whose input arrives per-job — the recompute IS the minimal realization. Dissolve-on: an extdeps-cited actions-artifact CacheProvider row (C0 discipline, same shape as ci_sccache_provider_note) modeling the upload as the covering provider at the workflow LCA, at which point the unpack demands discharge through it and this roster entry retires." +data ci_cost_floor_exempt_step_names_note: String = "Cost-floor roster BY STEP NAME, resolved to script identities against the live workflow at gate time (never a stale copied string): the two prelude snippets run in every job by construction — plural sites, workflow-isolation LCA — but each is a sub-second environment write whose store-tier discharge would cost more than the recompute it saves. The acceptance is TYPED (AcceptedBelowCostFloor), so the roster is countable and reviewable; an entry here is a claim that the script is below the cost floor, not an escape from the ladder. Third entry (unpack+verify release bins): after Phase D (2026-07-14) removed the emit_determinism job it runs in the single artifact-consuming ci job — single-site now (pre-Phase-D it ran once per consuming job, ci and emit_determinism); the exempt-roster check precedes the plurality check in group_verdict, so it keeps its AcceptedBelowCostFloor verdict rather than collapsing to a bare AcceptedSingleRecompute: it is the per-consumer materialization arm of the release-bins artifact store (the transfer/decode cost of the store tier itself), and no store tier can discharge an untar whose input arrives per-job — the recompute IS the minimal realization. Dissolve-on: an extdeps-cited actions-artifact CacheProvider row (C0 discipline, same shape as ci_sccache_provider_note) modeling the upload as the covering provider at the workflow LCA, at which point the unpack demands discharge through it and this roster entry retires." data ci_cost_floor_exempt_step_names: List = [ "Isolate toolchain dirs", diff --git a/dag/gunbc/ci_spec.dag b/dag/gunbc/ci_spec.dag index d57b023de4c..4f77c394434 100644 --- a/dag/gunbc/ci_spec.dag +++ b/dag/gunbc/ci_spec.dag @@ -5,7 +5,7 @@ import gunbc.ci_gate { EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, SourceRootIngestGate, RegenVerifyGate, - SelfHostReadsRealBytesGate, SelfHostStalenessGate, EmitDeterminismGate + SelfHostReadsRealBytesGate, SelfHostStalenessGate } import gunbc.ci_layer_roots { witness_layer_roots, @@ -16,7 +16,6 @@ import gunbc.commit_workflow { commit_gate_roster, project_ci_floor_gates, project_ci_regen_job_gates, - project_ci_emit_determinism_job_gates, project_ci_floor_witness_entries } import std.realization_schedule { ScheduleWitnessEntry } @@ -88,8 +87,6 @@ data gunbc_ci_floor_gates: List = project_ci_floor_gates(roster: commit_ga data gunbc_ci_regen_floor_gates: List = project_ci_regen_job_gates(roster: commit_gate_roster) -data gunbc_ci_emit_determinism_floor_gates: List = project_ci_emit_determinism_job_gates(roster: commit_gate_roster) - data gunbc_ci_floor_witness_entries: List = project_ci_floor_witness_entries(roster: commit_gate_roster) data gunbc_ci_gates: List = gunbc_ci_floor_gates @@ -135,19 +132,6 @@ data gunbc_ci_regen_spec: CiSpec = { deploy_stages: [], } -data gunbc_ci_emit_determinism_spec: CiSpec = { - gates: gunbc_ci_emit_determinism_floor_gates, - witness_entries: [], - discovery_scan_dirs: [], - diff_policy: { - base: ci_merge_base_ref, - head: "HEAD", - mode: DiffMergeBase - }, - notice_title: "emit determinism (two full-corpus emits, diff -r byte-identity) — serial job, off the floor path", - deploy_stages: [], -} - fn ci_spec_with_witness_entries(spec: CiSpec, entries: List) -> CiSpec { CiSpec { gates: spec.gates, @@ -287,7 +271,7 @@ data ci_floor_required_artifact_names: List = [ "parse_witness" ] -data ci_floor_required_artifact_names_note: String = "This list is the single cross-job handoff manifest (operator job-split ruling 2026-07-11): ci_release_build_line derives its --bin flags from it (build/manifest skew is unwritable), the build job verifies these bins post-build, packs exactly these into the release-bins artifact, and every downstream job (ci floor, emit_determinism) unpacks and re-verifies the same list before running. Membership = the floor-invoked bins (claim_executor, gunbc, floor_skip_discovery_witness) plus the FULL bin_name universe of tools.host_prelude transports (witness_bin / ensure_bin_built_and_verified) that corpus witnesses can address — pre-split those fell back to in-job cargo builds against the build step's warm target/ (cheap); post-split the floor job has no build cache, so a bin missing here would silently re-absorb the ~33min monolith cold compile the job split exists to bound. The intentionally-absent negative control (no_such_witness_bin_zzz) is excluded. The build-if-absent stale-binary class (#6352) is out of scope: a same-run artifact is fresh by construction." +data ci_floor_required_artifact_names_note: String = "This list is the single cross-job handoff manifest (operator job-split ruling 2026-07-11): ci_release_build_line derives its --bin flags from it (build/manifest skew is unwritable), the build job verifies these bins post-build, packs exactly these into the release-bins artifact, and the downstream ci floor job unpacks and re-verifies the same list before running (pre-Phase-D the emit_determinism job did too; removed 2026-07-14). Membership = the floor-invoked bins (claim_executor, gunbc, floor_skip_discovery_witness) plus the FULL bin_name universe of tools.host_prelude transports (witness_bin / ensure_bin_built_and_verified) that corpus witnesses can address — pre-split those fell back to in-job cargo builds against the build step's warm target/ (cheap); post-split the floor job has no build cache, so a bin missing here would silently re-absorb the ~33min monolith cold compile the job split exists to bound. The intentionally-absent negative control (no_such_witness_bin_zzz) is excluded. The build-if-absent stale-binary class (#6352) is out of scope: a same-run artifact is fresh by construction." fn artifact_shell_path(name: String) -> String { concat(concat("\"$ROOT/target/release/", name), "\"") @@ -493,7 +477,6 @@ fn git_fetch_script(policy: DiffPolicy) -> String { data floor_plan_entry: String = "src/v2/workflow/ci_floor_plan.dag" data floor_plan_function: String = "gunbc_ci_floor_batches" data regen_floor_plan_function: String = "gunbc_ci_regen_floor_batches" -data emit_determinism_floor_plan_function: String = "gunbc_ci_emit_determinism_batches" fn scheduler_invoke_with(spec: CiSpec, plan_function: String) -> String { concat( @@ -549,20 +532,6 @@ fn gunbc_ci_regen_floor_run(spec: CiSpec) -> String { gunbc_ci_regen_floor_only_script(spec: spec) } -fn gunbc_ci_emit_determinism_floor_only_script(spec: CiSpec) -> String { - concat( - concat("ROOT=", ci_repo_root_shell(), "\n"), - concat( - concat(git_fetch_script(policy: spec.diff_policy), "\n"), - concat(scheduler_invoke_with(spec: spec, plan_function: emit_determinism_floor_plan_function), "\n") - ) - ) -} - -fn gunbc_ci_emit_determinism_floor_run(spec: CiSpec) -> String { - gunbc_ci_emit_determinism_floor_only_script(spec: spec) -} - fn gunbc_ci_workflow_run(spec: CiSpec) -> String { gunbc_ci_floor_only_script(spec: spec) } diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 0bc2c863506..b911ded0801 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -7,7 +7,6 @@ import gunbc.ci_workflow_expressions { import gunbc.ci_spec { CiSpec, gunbc_ci_spec, gunbc_ci_floor_run, gunbc_ci_regen_spec, gunbc_ci_regen_floor_run, - gunbc_ci_emit_determinism_spec, gunbc_ci_emit_determinism_floor_run, gunbc_ci_deploy_stages, DeployStage, gunbc_ci_deploy_run_script, ci_release_build_script, ci_release_bins_pack_script, ci_release_bins_unpack_verify_script, @@ -276,20 +275,6 @@ fn ci_release_bins_unpack_verify_step() -> Step { } } -fn ci_emit_determinism_step() -> Step { - RunStep { - name: Present { value: "Emit determinism gate (two full-corpus emits, diff -r; serial job per operator 2026-07-11)" }, - id: none, - run: gunbc_ci_emit_determinism_floor_run(spec: gunbc_ci_emit_determinism_spec), - shell: none, - env: none, - working_directory: none, - if_condition: none, - continue_on_error: none, - timeout_minutes: Present { value: gunbc_ci_emit_determinism_step_timeout_minutes } - } -} - fn ci_floor_peak_pre_step() -> Step { RunStep { name: Present { value: "Floor cgroup peak pre-read (calibration; reset if permitted)" }, @@ -431,14 +416,10 @@ data gunbc_ci_aux_step_timeout_minutes: Int = 5 data gunbc_ci_artifact_transfer_step_timeout_minutes: Int = 10 -data gunbc_ci_artifact_transfer_step_note: String = "Cross-job handoff of the release bins (operator job-split ruling 2026-07-11): the build job packs ci_floor_required_artifact_names into one tgz (tar preserves the executable bits a raw artifact zip would drop) and uploads it; the ci floor and emit_determinism jobs download, unpack into target/release, and re-verify via claim_executor --verify-build-artifacts before any step consumes a bin (fail-closed: a missing or truncated artifact reds the job at the verify, never as a mid-floor bin fallback). 10m is the transfer envelope for a ~200MB archive under fleet pressure; upload uses if-no-files-found: error so an empty pack is loud." +data gunbc_ci_artifact_transfer_step_note: String = "Cross-job handoff of the release bins (operator job-split ruling 2026-07-11): the build job packs ci_floor_required_artifact_names into one tgz (tar preserves the executable bits a raw artifact zip would drop) and uploads it; the ci floor job downloads, unpacks into target/release, and re-verifies via claim_executor --verify-build-artifacts before any step consumes a bin (fail-closed: a missing or truncated artifact reds the job at the verify, never as a mid-floor bin fallback). 10m is the transfer envelope for a ~200MB archive under fleet pressure; upload uses if-no-files-found: error so an empty pack is loud." data gunbc_ci_selection_control_step_timeout_minutes: Int = 15 -data gunbc_ci_emit_determinism_step_timeout_minutes: Int = 30 - -data gunbc_ci_emit_determinism_step_note: String = "The emit-determinism oracle (two full-corpus 'gunbc compile --target rust' runs + diff -r byte-identity, tools.emit_determinism_transport ReplicatedOracle runs: 2) measured 12m25s inside the floor step (run 29125380927 gantt 22:17:36->22:30:01) — ~75% of the floor's wall. Moved to its own serially-chained job (operator ruling 2026-07-11) so the required floor path stops paying it per-merge; 30m = 2x measured + fleet-pressure headroom. Proactive replacement (post-hoc x2 oracle -> constructive determinism) is NOT reachable today: the nondeterminism class this guards (HashMap iteration order leaking into emitted Rust) lives in the v1 seed's host Rust, outside v2.std.determinism's modeled scope — it dissolves with the seed at the §7 self-host frontier, not by a .dag mechanism reaching into hand-Rust." - data gunbc_ci_selection_control_step_note: String = "The affected-set selection control suite as a per-PR gate (operator fork (c), 2026-07-10 — the acceptance's 'green AND wired' half): floor_skip_discovery_witness runs the hermetic skip/refuse/divergence controls, including predict_only_red_predicted_unaffected_is_divergence — the discriminating RED for the falsifier's divergence counting — and the empty-diff-skips pin (ruling 2026-07-05: a failure arm refuses, never widens). It rides the per-PR ci job, not only the nightly falsifier cadence, so a PR that reintroduces a widen (the cli_run.rs entry_file_touched class) reds BEFORE merge, not the morning after. Hermetic: both diff channels are injected (floor_diff_observe.dag floor_name_status_injection_encoding_note), so the suite measures fixtures, never this checkout's real diff. Budget: 80s measured local (2026-07-10, full suite, release build); 15m is the fleet-pressure envelope, a named term in the ci backstop sum." data gunbc_ci_prelude_allowance_minutes: Int = 5 @@ -455,12 +436,8 @@ fn gunbc_ci_job_backstop_timeout_minutes() -> Int { gunbc_ci_artifact_transfer_step_timeout_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_floor_step_timeout_minutes + gunbc_ci_floor_step_timeout_minutes + gunbc_ci_selection_control_step_timeout_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_prelude_allowance_minutes } -fn gunbc_ci_emit_determinism_job_backstop_timeout_minutes() -> Int { - gunbc_ci_artifact_transfer_step_timeout_minutes + gunbc_ci_aux_step_timeout_minutes + gunbc_ci_emit_determinism_step_timeout_minutes + gunbc_ci_prelude_allowance_minutes -} - data gunbc_ci_job_timeout_policy_disposition: Disposition = Terminal { - reason: "gunbc CI budget semantics (operator ruling 2026-07-06): floor step gunbc_ci_floor_step_timeout_minutes enforced at STEP level (RED-by-failure); ci job backstop gunbc_ci_job_backstop_timeout_minutes() = release build + floor + aux + prelude (100m at floor=45m after #6422 witness-cone receipt; was 85m at floor=30m after #6361 Lever A dissolve-on; was 65m at floor=10m) so cold release build is not killed by a job cap shorter than gunbc_ci_release_build_step_timeout_minutes (receipt run 28819559597 @ 201371c: 10m job kill aborted cargo mid-build). The accumulator-copy roster gate is OFFLINE, not CI-enrolled (operator ruling 2026-07-10): its three witness entries re-parse rostered files under the interpreter (~15m serial measured; receipt: run 29066265723 hit the then-30m step cap with the gate enrolled on a 27m40s baseline floor) — re-enroll when parse/resolve memoization or batch-1 shared-parse reuse shrinks the gate to seconds. The job cap must cover step budgets so gates reach RED-by-step-failure, not job-cancelled silent-pass (#6323 starvation-kill scoped to ci/floor only on main; the rust_tests job and its warm/gate step-sum backstop were removed 2026-07-11 to free runner capacity). dissolve-on: compile-clean scoping/memoization shrinks batch-1; floor step raised when batch-1 completes inside measured envelope (receipt run 28902523405 @ e1a27e4286). #6422 receipt run 29065683045 @ a2bcd6f668: batches 1-3 ~20m (dag_collect_fingerprint_witness_keystone_holds 488s), batch 4 rust_monolith_gate still running at 30m step kill — raised floor to 45m (= rust gate step budget). 2026-07-10 step-budget completeness (review catch, #6438): the resolve-receipt gate step had NO step cap — a hang there could only die by job-cancel, the same starvation-kill class — so it now carries the aux cap (the script is a sub-second receipt read) and the ci backstop counts the aux steps exactly. Every ci-job step now has a declared step budget and the backstop is the exact step-sum + prelude. 2026-07-10 merge of #6441 into #6438: the incoming materialization receipt gate step landed uncapped (timeout none) — same class — so the merge resolution capped it aux too; the ci backstop counts FIVE aux terms: peak pre-read, peak post-read, resolve-receipt gate, materialization receipt gate, merge-admission gate. 2026-07-10 selection-control wiring (operator fork (c)): the affected-set selection control step (floor_skip_discovery_witness, see gunbc_ci_selection_control_step_note) joins the ci job with its OWN named budget gunbc_ci_selection_control_step_timeout_minutes (15m; 80s measured local) — a named term in the backstop sum, not a sixth aux term — so the backstop stays the exact step-sum + prelude. 2026-07-11 serial job split (operator ruling): the single ci job became three serially-chained jobs — build (release build + pack/upload of the ci_floor_required_artifact_names manifest; backstop gunbc_ci_build_job_backstop_timeout_minutes()), ci (downloads + verifies the release-bins artifact, then floor/receipts/selection/admission/regen; backstop gunbc_ci_job_backstop_timeout_minutes() with the release-build term replaced by artifact-transfer + unpack terms), and emit_determinism (needs ci; the x2 full-corpus emit oracle extracted from the floor batches via the GithubActionsEmitDeterminismJob surface; backstop gunbc_ci_emit_determinism_job_backstop_timeout_minutes()). Rationale: a cold monolith build (~33-34min measured, runs 29125380927/29126347183 — sccache healthy at 96% Rust hits, zero read errors; the wall is one true-miss compile of the 161k-line v1-compiler crate, and per-host sccache caches mean a cross-host repeat pays it again) consumed the shared job budget so every .rs-touching merge ran at the ceiling by construction; each unit now carries its own bounded, monitored budget and a timeout is attributable at job grain. The chain is strictly serial (build -> ci -> emit_determinism), so the concurrent runner-slot footprint stays one job per run. Honesty row: emit_determinism lands NON-required on the gunbc main ruleset (the same status ci_regen had before #6470 folded it in; only the ci job is protection-required) — the oracle still runs and reds loudly every merge, but enforcing it is an operator ruleset action; its per-merge-required return trigger is that ruleset flip, and its deeper dissolve-on is the §7 self-host frontier retiring the v1 seed emitter whose host-Rust nondeterminism class it guards." + reason: "gunbc CI budget semantics (operator ruling 2026-07-06): floor step gunbc_ci_floor_step_timeout_minutes enforced at STEP level (RED-by-failure); ci job backstop gunbc_ci_job_backstop_timeout_minutes() = release build + floor + aux + prelude (100m at floor=45m after #6422 witness-cone receipt; was 85m at floor=30m after #6361 Lever A dissolve-on; was 65m at floor=10m) so cold release build is not killed by a job cap shorter than gunbc_ci_release_build_step_timeout_minutes (receipt run 28819559597 @ 201371c: 10m job kill aborted cargo mid-build). The accumulator-copy roster gate is OFFLINE, not CI-enrolled (operator ruling 2026-07-10): its three witness entries re-parse rostered files under the interpreter (~15m serial measured; receipt: run 29066265723 hit the then-30m step cap with the gate enrolled on a 27m40s baseline floor) — re-enroll when parse/resolve memoization or batch-1 shared-parse reuse shrinks the gate to seconds. The job cap must cover step budgets so gates reach RED-by-step-failure, not job-cancelled silent-pass (#6323 starvation-kill scoped to ci/floor only on main; the rust_tests job and its warm/gate step-sum backstop were removed 2026-07-11 to free runner capacity). dissolve-on: compile-clean scoping/memoization shrinks batch-1; floor step raised when batch-1 completes inside measured envelope (receipt run 28902523405 @ e1a27e4286). #6422 receipt run 29065683045 @ a2bcd6f668: batches 1-3 ~20m (dag_collect_fingerprint_witness_keystone_holds 488s), batch 4 rust_monolith_gate still running at 30m step kill — raised floor to 45m (= rust gate step budget). 2026-07-10 step-budget completeness (review catch, #6438): the resolve-receipt gate step had NO step cap — a hang there could only die by job-cancel, the same starvation-kill class — so it now carries the aux cap (the script is a sub-second receipt read) and the ci backstop counts the aux steps exactly. Every ci-job step now has a declared step budget and the backstop is the exact step-sum + prelude. 2026-07-10 merge of #6441 into #6438: the incoming materialization receipt gate step landed uncapped (timeout none) — same class — so the merge resolution capped it aux too; the ci backstop counts FIVE aux terms: peak pre-read, peak post-read, resolve-receipt gate, materialization receipt gate, merge-admission gate. 2026-07-10 selection-control wiring (operator fork (c)): the affected-set selection control step (floor_skip_discovery_witness, see gunbc_ci_selection_control_step_note) joins the ci job with its OWN named budget gunbc_ci_selection_control_step_timeout_minutes (15m; 80s measured local) — a named term in the backstop sum, not a sixth aux term — so the backstop stays the exact step-sum + prelude. 2026-07-11 serial job split (operator ruling): the single ci job became three serially-chained jobs — build (release build + pack/upload of the ci_floor_required_artifact_names manifest; backstop gunbc_ci_build_job_backstop_timeout_minutes()), ci (downloads + verifies the release-bins artifact, then floor/receipts/selection/admission/regen; backstop gunbc_ci_job_backstop_timeout_minutes() with the release-build term replaced by artifact-transfer + unpack terms), and emit_determinism (needs ci; the x2 full-corpus emit oracle extracted from the floor batches via the GithubActionsEmitDeterminismJob surface; backstop gunbc_ci_emit_determinism_job_backstop_timeout_minutes()). Rationale: a cold monolith build (~33-34min measured, runs 29125380927/29126347183 — sccache healthy at 96% Rust hits, zero read errors; the wall is one true-miss compile of the 161k-line v1-compiler crate, and per-host sccache caches mean a cross-host repeat pays it again) consumed the shared job budget so every .rs-touching merge ran at the ceiling by construction; each unit now carries its own bounded, monitored budget and a timeout is attributable at job grain. The chain is strictly serial (build -> ci -> emit_determinism), so the concurrent runner-slot footprint stays one job per run. Honesty row: emit_determinism lands NON-required on the gunbc main ruleset (the same status ci_regen had before #6470 folded it in; only the ci job is protection-required) — the oracle still runs and reds loudly every merge, but enforcing it is an operator ruleset action; its per-merge-required return trigger is that ruleset flip, and its deeper dissolve-on is the §7 self-host frontier retiring the v1 seed emitter whose host-Rust nondeterminism class it guards. SUPERSEDED 2026-07-14 (this PR, Phase D): the emit_determinism job, its x2 sampling oracle (tools.emit_determinism_gate/_transport, both deleted), and the GithubActionsEmitDeterminismJob surface were REMOVED — so ci_build_job -> ci_job is now the whole serial chain and this note's emit_determinism terms are historical. Determinism became a construction gate (§5 construction-over-validation): v2.lens.determinism.determinism_compile_gate enrolled in v2.compiler.compile always_required_root_lenses (mirrors accumulator_copy #6527) reds any bare map_keys reach into the v2 compile door, and C.2 (#6576) made the v1 seed emitter map_keys-free by construction so its host-Rust class needs no x2 sample. Operator ruling 2026-07-14 (b): the dying §7 seed carries no active guard; the construction guarantee replaces the sampling oracle." } fn ci_deploy_job(stage: DeployStage) -> Job { @@ -518,23 +495,6 @@ fn ci_job() -> Job { } } -fn ci_emit_determinism_job() -> Job { - Job { - id: "emit_determinism", - name: none, - runner: gunbc_ci_selected_runner_spec(), - steps: concat(ci_prelude_steps(), [ci_release_bins_download_step(), ci_release_bins_unpack_verify_step(), ci_emit_determinism_step()]), - needs: ["ci"], - env: none, - outputs: none, - if_condition: none, - strategy: none, - timeout_minutes: Present { value: gunbc_ci_emit_determinism_job_backstop_timeout_minutes() }, - continue_on_error: none, - concurrency: none - } -} - fn ci_regen_floor_step() -> Step { RunStep { name: Present { value: "gunbc ci regen (self-host fixed-point — required; folded into ci job 2026-07-11)" }, @@ -582,5 +542,5 @@ data ci_workflow: Workflow = { id_token: none } }, - jobs: concat([ci_build_job(), ci_job(), ci_emit_determinism_job()], ci_deploy_jobs()) + jobs: concat([ci_build_job(), ci_job()], ci_deploy_jobs()) } diff --git a/dag/gunbc/commit_workflow.dag b/dag/gunbc/commit_workflow.dag index eb593385926..92e9eaa6bf6 100644 --- a/dag/gunbc/commit_workflow.dag +++ b/dag/gunbc/commit_workflow.dag @@ -5,7 +5,7 @@ import gunbc.ci_gate { EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, SourceRootIngestGate, RegenVerifyGate, - SelfHostReadsRealBytesGate, SelfHostStalenessGate, EmitDeterminismGate + SelfHostReadsRealBytesGate, SelfHostStalenessGate } import gunbc.generated_artifact { GeneratedArtifact, CiYamlArtifact, GithooksPrePushArtifact, artifact_path @@ -28,7 +28,6 @@ type LocalTidyCheck type CommitWorkflowSurface = GithubActionsCiJob | GithubActionsCiRegenJob - | GithubActionsEmitDeterminismJob | GitPrePushHook type CommitCheckKind @@ -92,10 +91,6 @@ data commit_gate_roster: List = [ check: CommitSpecGate { gate: SelfHostStalenessGate }, surfaces: [GithubActionsCiRegenJob] }, - CommitCheckEnrollment { - check: CommitSpecGate { gate: EmitDeterminismGate }, - surfaces: [GithubActionsEmitDeterminismJob] - }, CommitCheckEnrollment { check: CommitWitnessClaim { entry: "dag/test/claim/github_merge_state_witness_test.dag", @@ -398,8 +393,6 @@ fn commit_workflow_surface_eq(a: CommitWorkflowSurface, b: CommitWorkflowSurface match b { GithubActionsCiJob => true _ => false } GithubActionsCiRegenJob => match b { GithubActionsCiRegenJob => true _ => false } - GithubActionsEmitDeterminismJob => - match b { GithubActionsEmitDeterminismJob => true _ => false } GitPrePushHook => match b { GitPrePushHook => true _ => false } } @@ -424,7 +417,6 @@ fn gate_eq(a: Gate, b: Gate) -> Bool { RegenVerifyGate => match b { RegenVerifyGate => true _ => false } SelfHostReadsRealBytesGate => match b { SelfHostReadsRealBytesGate => true _ => false } SelfHostStalenessGate => match b { SelfHostStalenessGate => true _ => false } - EmitDeterminismGate => match b { EmitDeterminismGate => true _ => false } } } @@ -450,12 +442,6 @@ fn project_ci_regen_job_gates(roster: List) -> List ) } -fn project_ci_emit_determinism_job_gates(roster: List) -> List { - gates_from_enrollments( - rows: filter(roster, e => enrollment_has_surface(e: e, surface: GithubActionsEmitDeterminismJob)) - ) -} - data ci_witness_optin_inversion: Disposition = Terminal { reason: "Opt-in inversion (operator, 2026-07-04) DISSOLVE FIRED at the discovery flip (operator acceptance 2026-07-09 on #6403; re-landed 2026-07-10, see gunbc.ci_spec ci_spec_discovery_flip_note): the corpus re-enrolled through CiSpec.discovery_scan_dirs with SelectionApplied, so per-PR CI runs tree-wide discovery shrunk to the diff's affected set. The scaffold's dissolve-on (affected-set selection + floor memoization make per-PR selection-by-affectedness affordable) is satisfied at discovery width=1 under union-resolve S1. project_ci_floor_witness_entries is Terminal, not interim: CommitWitnessClaim rows remain live as the explicit-entry roster (execution-kind rows with their own resource profile, and any row that must run as declared) - only the roster's EXCLUSIVITY as witness source dissolved." } @@ -613,7 +599,6 @@ fn realization_artifact_path(surface: CommitWorkflowSurface) -> String { match surface { GithubActionsCiJob => artifact_path(a: CiYamlArtifact) GithubActionsCiRegenJob => artifact_path(a: CiYamlArtifact) - GithubActionsEmitDeterminismJob => artifact_path(a: CiYamlArtifact) GitPrePushHook => artifact_path(a: GithooksPrePushArtifact) } } @@ -622,7 +607,6 @@ fn realization_handler_for_surface(surface: CommitWorkflowSurface) -> CommitWork match surface { GithubActionsCiJob => CiYamlHandler GithubActionsCiRegenJob => CiYamlHandler - GithubActionsEmitDeterminismJob => CiYamlHandler GitPrePushHook => GitPrePushScriptHandler } } @@ -631,7 +615,6 @@ fn realization_artifact_for_surface(surface: CommitWorkflowSurface) -> Generated match surface { GithubActionsCiJob => CiYamlArtifact GithubActionsCiRegenJob => CiYamlArtifact - GithubActionsEmitDeterminismJob => CiYamlArtifact GitPrePushHook => GithooksPrePushArtifact } } diff --git a/dag/gunbc/floor_materialization.dag b/dag/gunbc/floor_materialization.dag index 0fd080cad66..f49a233184d 100644 --- a/dag/gunbc/floor_materialization.dag +++ b/dag/gunbc/floor_materialization.dag @@ -6,7 +6,7 @@ import gunbc.ci_gate { Gate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, SourceRootIngestGate, RegenVerifyGate, - SelfHostReadsRealBytesGate, SelfHostStalenessGate, EmitDeterminismGate + SelfHostReadsRealBytesGate, SelfHostStalenessGate } import std.materialization_ladder { Frame, SharedStateFrame, @@ -53,7 +53,6 @@ fn gate_consumer_site(g: Gate) -> String { RegenVerifyGate => "gate:RegenVerifyGate" SelfHostReadsRealBytesGate => "gate:SelfHostReadsRealBytesGate" SelfHostStalenessGate => "gate:SelfHostStalenessGate" - EmitDeterminismGate => "gate:EmitDeterminismGate" } } diff --git a/dag/gunbc/merge_admission.dag b/dag/gunbc/merge_admission.dag index 39d8c490615..b1ba55d36c6 100644 --- a/dag/gunbc/merge_admission.dag +++ b/dag/gunbc/merge_admission.dag @@ -20,7 +20,7 @@ import gunbc.ci_gate { EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, SourceRootIngestGate, RegenVerifyGate, - SelfHostReadsRealBytesGate, SelfHostStalenessGate, EmitDeterminismGate, + SelfHostReadsRealBytesGate, SelfHostStalenessGate, } import std.content_hash { content_hash_atom, content_hash_combine, content_hash_tagged } import std.disposition { Disposition, Scaffold, SingleAuthority } @@ -80,7 +80,6 @@ fn gate_content_hash(g: Gate) -> ContentHash { RegenVerifyGate => content_hash_atom(value: "RegenVerifyGate") SelfHostReadsRealBytesGate => content_hash_atom(value: "SelfHostReadsRealBytesGate") SelfHostStalenessGate => content_hash_atom(value: "SelfHostStalenessGate") - EmitDeterminismGate => content_hash_atom(value: "EmitDeterminismGate") } } diff --git a/dag/test/claim/ci_materialization_witness_test.dag b/dag/test/claim/ci_materialization_witness_test.dag index 422527c32fe..853730ef7ec 100644 --- a/dag/test/claim/ci_materialization_witness_test.dag +++ b/dag/test/claim/ci_materialization_witness_test.dag @@ -52,9 +52,9 @@ test fn ci_catalog_projection_has_no_refusals() -> Bool { } test fn ci_uses_steps_are_counted_not_silent() -> Bool { - ci_uses_steps_skipped_count() == 12 + ci_uses_steps_skipped_count() == 8 } test fn ci_run_step_natures_are_claims_counted_not_silent() -> Bool { - ci_run_step_natures_claimed_not_derived_count() == 19 + ci_run_step_natures_claimed_not_derived_count() == 15 } diff --git a/dag/test/claim/computation_demand_duplication_witness_test.dag b/dag/test/claim/computation_demand_duplication_witness_test.dag index 37219cc9a1b..fa58bdcb2a4 100644 --- a/dag/test/claim/computation_demand_duplication_witness_test.dag +++ b/dag/test/claim/computation_demand_duplication_witness_test.dag @@ -1,13 +1,11 @@ module test.claim.computation_demand_duplication_witness -import tools.emit_determinism_transport { - ed_typed_compile_invocation, - emit_determinism_replication_intent -} import v2.lens.duplicate_computation { ComputationDemandFact, ReplicationIntent, + ReplicatedOracle, SingleRun, + TypedArgvInvocation, UnguardedDemand, duplicate_computation_clean_holds, duplicate_computation_violation_count, @@ -18,20 +16,28 @@ import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly -fn emit_determinism_typed_demand_facts(replication: ReplicationIntent) -> List { +fn synthetic_replicated_invocation(run_site: String, out_dir: String) -> TypedArgvInvocation { + TypedArgvInvocation { + site: run_site, + tool: "gunbc", + args: ["compile", "--source-root", "dag", "--dependency-pool-index", "primary-precedence", "--output-dir", out_dir, "--target", "rust"] + } +} + +fn replicated_pair_demand_facts(replication: ReplicationIntent) -> List { concat( typed_argv_computation_demands( - invocation: ed_typed_compile_invocation( - run_site: "tools.emit_determinism_transport.run_emit_corpus_x2_diff_zero#first", - out_dir: "/tmp/emit-determinism-out-first" + invocation: synthetic_replicated_invocation( + run_site: "test.claim.computation_demand_duplication_witness#first", + out_dir: "/tmp/replicated-oracle-out-first" ), guard: UnguardedDemand, replication: replication ), typed_argv_computation_demands( - invocation: ed_typed_compile_invocation( - run_site: "tools.emit_determinism_transport.run_emit_corpus_x2_diff_zero#second", - out_dir: "/tmp/emit-determinism-out-second" + invocation: synthetic_replicated_invocation( + run_site: "test.claim.computation_demand_duplication_witness#second", + out_dir: "/tmp/replicated-oracle-out-second" ), guard: UnguardedDemand, replication: replication @@ -39,16 +45,16 @@ fn emit_determinism_typed_demand_facts(replication: ReplicationIntent) -> List Bool { +test fn replicated_oracle_pair_is_declared_and_admissible() -> Bool { return duplicate_computation_clean_holds( - facts: emit_determinism_typed_demand_facts(replication: emit_determinism_replication_intent) + facts: replicated_pair_demand_facts(replication: ReplicatedOracle { runs: 2 }) ) } -test fn emit_determinism_pair_without_declaration_reds() -> Bool { +test fn replicated_pair_without_declaration_reds() -> Bool { return duplicate_computation_violation_count( - facts: emit_determinism_typed_demand_facts(replication: SingleRun) + facts: replicated_pair_demand_facts(replication: SingleRun) ) == 1 } -data typed_projection_subject_note: String = "The live subject is the typed-argv projection: both facts are projected from ed_typed_compile_invocation, the same construction whose args the run path passes to gunbc.WitnessBin.Run, and the two distinct --output-dir values above also prove the placement-flag stripping keeps the pair's computation_key equal. The lens's ShellProgram walker keeps its synthetic lens-unit subjects in v2.test.duplicate_computation and the not-yet-migrated bash-program transports as its domain; it dissolves with the sidecar." +data typed_projection_subject_note: String = "The live subject is the typed-argv projection: both facts are projected from synthetic_replicated_invocation (same tool+args except the --output-dir placement flag), and the two distinct --output-dir values prove the placement-flag stripping keeps the pair's computation_key equal — a declared ReplicatedOracle (runs 2) pair is admissible, SingleRun reds. Formerly keyed on tools.emit_determinism_transport.ed_typed_compile_invocation (the x2 full-corpus emit oracle); re-homed to a self-contained synthetic fixture when the x2 emit-determinism gate and transport were deleted (Phase D, 2026-07-14 — determinism became a construction gate, v2.lens.determinism enrolled in always_required_root_lenses). The lens's ShellProgram walker keeps its synthetic lens-unit subjects in v2.test.duplicate_computation and the not-yet-migrated bash-program transports as its domain; it dissolves with the sidecar." diff --git a/dag/tools/ci_gates.dag b/dag/tools/ci_gates.dag index fb87a53e375..ee05b7693d9 100644 --- a/dag/tools/ci_gates.dag +++ b/dag/tools/ci_gates.dag @@ -6,7 +6,7 @@ import gunbc.ci_spec { EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, SourceRootIngestGate, RegenVerifyGate, - SelfHostReadsRealBytesGate, SelfHostStalenessGate, EmitDeterminismGate + SelfHostReadsRealBytesGate, SelfHostStalenessGate } import tools.emit_host_gate { run_emit_host_ci_gate } import tools.layering_imports_gate { run_layering_imports_ci_gate } @@ -14,7 +14,6 @@ import tools.extdeps_external_authority_gate { run_extdeps_external_authority_ci import tools.dag_compile_clean_gate { run_dag_compile_clean_gate } import tools.generated_artifact_gate { run_generated_artifact_drift_gate } import tools.regen_verify_gate { run_regen_verify_gate } -import tools.emit_determinism_gate { run_emit_determinism_gate } fn exit_ok(e: ProcessExit) -> Bool { match e { ExitSuccess => true _ => false } @@ -37,7 +36,6 @@ fn run_spec_gate(g: Gate) -> ProcessExit { SelfHostStalenessGate => exit_failure( reason: "SelfHostStalenessGate: run via ci_floor_plan (self_host_realized_comparison_claims.staleness_gate_holds)" ) - EmitDeterminismGate => run_emit_determinism_gate() } } diff --git a/dag/tools/emit_determinism_gate.dag b/dag/tools/emit_determinism_gate.dag deleted file mode 100644 index 52af9610854..00000000000 --- a/dag/tools/emit_determinism_gate.dag +++ /dev/null @@ -1,30 +0,0 @@ -module tools.emit_determinism_gate - -import std.process { ProcessExit, ExitSuccess, exit_failure } -import tools.emit_determinism_transport { run_diff_recursive_discrimination_receipt, run_emit_corpus_x2_diff_zero } - -func run_emit_determinism_gate_body() -> ProcessExit { - let oracle_discriminates = run_diff_recursive_discrimination_receipt() - return if !oracle_discriminates { - exit_failure( - reason: "emit-determinism gate refused: shell.Diff.Recursive discrimination receipt failed - the tree-compare oracle did not distinguish differing trees from identical ones, so a determinism verdict from it would be fabricated" - ) - } else { - let det = run_emit_corpus_x2_diff_zero() - if det { - ExitSuccess - } else { - exit_failure( - reason: "emit-determinism gate failed: two sequential 'gunbc compile dag --target rust' runs produced non-identical output trees" - ) - } - } -} - -func run_emit_determinism_gate() -> ProcessExit { - run_emit_determinism_gate_body() -} - -func main() -> ProcessExit { - run_emit_determinism_gate_body() -} diff --git a/dag/tools/emit_determinism_transport.dag b/dag/tools/emit_determinism_transport.dag deleted file mode 100644 index 431be6fd7cf..00000000000 --- a/dag/tools/emit_determinism_transport.dag +++ /dev/null @@ -1,99 +0,0 @@ -module tools.emit_determinism_transport - -import extdeps.shell -import extdeps.diffutils -import extdeps.filesystem.filesystem_io -import extdeps.git -import extdeps.gunbc -import std.logic { Bool } -import tools.host_prelude { witness_bin, witness_bin_ensure_built_typed, witness_bin_release_path } -import v2.lens.duplicate_computation { ReplicationIntent, ReplicatedOracle, TypedArgvInvocation } - -data ed_typed_transport_doc: String = "Emit-determinism transport, typed. The receipt is a composition of typed service operations (shell.Mktemp.Dir, gunbc.WitnessBin.Run twice on the gunbc binary, shell.Diff.Recursive on the two output trees, shell.Remove.RecursiveForce) - no bash is serialized or executed. The former ShellProgram builder and its RealizationDispatch scaffold row dissolved with the flip. The two compile runs are one declared ReplicatedOracle pair: the duplicate_computation lens projects both invocations from ed_typed_compile_invocation - the same construction the run path executes - so the declared-pair witness and the run path cannot drift." - -data emit_determinism_corpus_roots: List = ["dag", "src/v2"] - -data emit_determinism_replication_intent: ReplicationIntent = ReplicatedOracle { runs: 2 } - -data ed_gunbc_bin_name: String = "gunbc" - -fn ed_source_root_args() -> List { - fold(emit_determinism_corpus_roots, init: [], f: (acc, r) => concat(acc, ["--source-root", r])) -} - -fn ed_compile_args(out_dir: String) -> List { - concat( - concat(["compile"], ed_source_root_args()), - [ - "--dependency-pool-index", "primary-precedence", - "--output-dir", out_dir, - "--target", "rust" - ] - ) -} - -fn ed_typed_compile_invocation(run_site: String, out_dir: String) -> TypedArgvInvocation { - TypedArgvInvocation { - site: run_site, - tool: ed_gunbc_bin_name, - args: ed_compile_args(out_dir: out_dir) - } -} - -fn ed_gunbc_ready(root: String) -> Bool { - witness_bin_ensure_built_typed(root: root, bin: witness_bin(var_name: "GUNBC", bin_name: ed_gunbc_bin_name)) -} - -fn ed_run_compile(root: String, invocation: TypedArgvInvocation) -> Bool { - let run = gunbc.WitnessBin.Run( - workdir: root, - bin_path: witness_bin_release_path(root: root, bin_name: ed_gunbc_bin_name), - args: invocation.args - ) - run.success -} - -fn run_diff_recursive_discrimination_receipt() -> Bool { - let a = shell.Mktemp.Dir() - let b = shell.Mktemp.Dir() - if a.success && b.success { - let wrote_a = Filesystem.Write(path: concat(a.path, "/probe.txt"), content: "left") - let wrote_b = Filesystem.Write(path: concat(b.path, "/probe.txt"), content: "right") - let differing = shell.Diff.Recursive(left: a.path, right: b.path) - let equal = shell.Diff.Recursive(left: a.path, right: a.path) - let cleaned_a = shell.Remove.RecursiveForce(path: a.path) - let cleaned_b = shell.Remove.RecursiveForce(path: b.path) - wrote_a.success && wrote_b.success - && (differing.identical == false) && (differing.exit_code == 1) - && equal.identical - && cleaned_a.success && cleaned_b.success - } else { - false - } -} - -fn run_emit_corpus_x2_diff_zero() -> Bool { - let root = git.Core.Toplevel() - if ed_gunbc_ready(root: root.path) { - let out1 = shell.Mktemp.Dir() - let out2 = shell.Mktemp.Dir() - if out1.success && out2.success { - let first = ed_run_compile( - root: root.path, - invocation: ed_typed_compile_invocation(run_site: "tools.emit_determinism_transport.run_emit_corpus_x2_diff_zero#first", out_dir: out1.path) - ) - let second = ed_run_compile( - root: root.path, - invocation: ed_typed_compile_invocation(run_site: "tools.emit_determinism_transport.run_emit_corpus_x2_diff_zero#second", out_dir: out2.path) - ) - let compared = shell.Diff.Recursive(left: out1.path, right: out2.path) - let cleaned1 = shell.Remove.RecursiveForce(path: out1.path) - let cleaned2 = shell.Remove.RecursiveForce(path: out2.path) - first && second && compared.identical && cleaned1.success && cleaned2.success - } else { - false - } - } else { - false - } -} diff --git a/dag/tools/floor_effect_gate_witness.dag b/dag/tools/floor_effect_gate_witness.dag index 4f46ac17592..4059c7e14de 100644 --- a/dag/tools/floor_effect_gate_witness.dag +++ b/dag/tools/floor_effect_gate_witness.dag @@ -3,7 +3,7 @@ module tools.floor_effect_gate_witness import gunbc.ci_spec { EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, - RegenVerifyGate, EmitDeterminismGate + RegenVerifyGate } import tools.ci_gates { exit_ok, run_spec_gate } @@ -30,7 +30,3 @@ func generated_artifact_drift_gate_passes() -> Bool { func regen_verify_gate_passes() -> Bool { exit_ok(e: run_spec_gate(g: RegenVerifyGate)) } - -func emit_determinism_gate_passes() -> Bool { - exit_ok(e: run_spec_gate(g: EmitDeterminismGate)) -} diff --git a/dag/tools/host_prelude.dag b/dag/tools/host_prelude.dag index e82a82111b1..82ea55f642f 100644 --- a/dag/tools/host_prelude.dag +++ b/dag/tools/host_prelude.dag @@ -99,7 +99,7 @@ fn toolchain_provision_shell_exec(script: String) -> Bool { result.success } -data witness_invocation_doc: String = "Shared witness-binary invocation. A dag/tools transport states WHICH binaries must exist (WitnessBin: prereqs + the one to invoke) and its argv Strings; this module realizes that intent as TYPED service operations (shell.Test.IsExecutable, cargo.Build.Build, gunbc.WitnessBin.Run at the git.Core.Toplevel root) - no bash is serialized or executed on the run path, per the shell-emission-model rule that runtime-present sites are argv invocations wearing bash syntax and need no shell. The former ShellProgram builders (witness_bin_program, gunbc_claims_program) dissolved with the oracle flip to emitter-independent goldens in v2.test.claim.bash_program_fold; ensure_gunbc_built remains as a scaffold for emit_determinism_transport (lane C)." +data witness_invocation_doc: String = "Shared witness-binary invocation. A dag/tools transport states WHICH binaries must exist (WitnessBin: prereqs + the one to invoke) and its argv Strings; this module realizes that intent as TYPED service operations (shell.Test.IsExecutable, cargo.Build.Build, gunbc.WitnessBin.Run at the git.Core.Toplevel root) - no bash is serialized or executed on the run path, per the shell-emission-model rule that runtime-present sites are argv invocations wearing bash syntax and need no shell. The former ShellProgram builders (witness_bin_program, gunbc_claims_program) dissolved with the oracle flip to emitter-independent goldens in v2.test.claim.bash_program_fold; ensure_gunbc_built and the ensure_bin_built_and_verified shell-stmt chain are the legacy pre-flip bin-build scaffold, now uncalled after Phase D (2026-07-14) removed emit_determinism_transport (their last consumer) — dissolve-on: delete the uncalled chain in a follow-up hygiene sweep; toolchain_provision_shell_exec is the only surviving consumer of this module's shell path." type WitnessBin { var_name: String diff --git a/src/v2/compiler/00_compile.dag b/src/v2/compiler/00_compile.dag index b2a5fec1171..382a5a0c4ed 100644 --- a/src/v2/compiler/00_compile.dag +++ b/src/v2/compiler/00_compile.dag @@ -27,6 +27,7 @@ import extdeps.communication.medium { Lossless, Medium } import v2.std.compilers.target_model { TargetModel } import v2.extdeps.languages.dag { dag_language_model } import v2.lens.complexity_accumulator_copy.compile_gate { accumulator_copy_compile_gate } +import v2.lens.determinism { determinism_compile_gate } import v2.lens.fact_density { fact_density_hollow_alias_gate } import v2.lens.unit_modeling { unit_modeling_gate } import std.algebra { Cons, Empty, FreeMonoid } @@ -122,14 +123,20 @@ data required_lens_grain_note: String = "Two required-roster grains, one door (v data accumulator_copy_lens: CompileLens = CompileLens { gate: accumulator_copy_root_gate } +data determinism_lens: CompileLens = CompileLens { gate: determinism_root_gate } + fn always_required_root_lenses() -> List { - [accumulator_copy_lens] + [accumulator_copy_lens, determinism_lens] } fn accumulator_copy_root_gate(tree: InferredTree) -> Outcome> { accumulator_copy_compile_gate(node: tree.root) } +fn determinism_root_gate(tree: InferredTree) -> Outcome> { + determinism_compile_gate(node: tree.root) +} + fn fact_density_hollow_alias_compile_gate(tree: InferredTree) -> Outcome> { fact_density_hollow_alias_gate(node: tree.root) } diff --git a/src/v2/lens/determinism.dag b/src/v2/lens/determinism.dag index fb9f908114c..bf139e4019f 100644 --- a/src/v2/lens/determinism.dag +++ b/src/v2/lens/determinism.dag @@ -4,9 +4,10 @@ import v2.lens.common.construction_justification { ConstructionJustification, Wa import std.disposition { SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.determinism { DeterminismAxis, Deterministic, NonDeterministic, determinism_compose } +import std.algebra { Cons, Empty } import v2.std.determinism { DeterminismFact } import v2.std.collection { Absent, List, Optional, Present, list_at_optional, optional_absent, optional_present } -import v2.std.diagnostic { Diagnostic, ExternalContractUnknown, Unavailable, node_locus } +import v2.std.diagnostic { Diagnostic, ExternalContractUnknown, NonEmptyDiagnostics, Outcome, Rejected, Unavailable, node_locus, outcome_accepted } import v2.std.logic { Bool } import v2.std.node { Atom, Edge, Node, NodeFold, Symbol, TypeNode, fold_node } import v2.std.node_query { node_is_call, node_positional_child_targets } @@ -106,6 +107,35 @@ fn determinism_contract_witness(signature: Symbol, n: Node) -> Witness List { + match local_determinism_axis(n: n) { + NonDeterministic { source: _ } => [determinism_leak_diagnostic(n: n)] + Deterministic => [] + } +} + +fn determinism_findings(root: Node) -> List { + fold_node( + n: root, + algebra: NodeFold { + init: fn(n0) { local_determinism_findings(n: n0) }, + step: fn(acc, _edge, child) { concat(acc, child) } + } + ) +} + +fn determinism_compile_gate(node: Node) -> Outcome> { + match determinism_findings(root: node) { + Cons { head: first, tail: rest } => + Rejected { + diagnostics: NonEmptyDiagnostics { head: first, tail: rest } + } + Empty => outcome_accepted(value: Holds { value: node }) + } +} + data construction_justification: ConstructionJustification = ConstructionJustification { class: WallAfterGrounding { dissolves_to: SingleAuthority } } diff --git a/src/v2/lens/determinism/reach_witness_test.dag b/src/v2/lens/determinism/reach_witness_test.dag index d421399855a..fefde1146d0 100644 --- a/src/v2/lens/determinism/reach_witness_test.dag +++ b/src/v2/lens/determinism/reach_witness_test.dag @@ -1,8 +1,9 @@ module v2.test.lens_determinism.reach_witness -import v2.lens.determinism { determinism_axis_for_node, determinism_contract_witness } +import v2.lens.determinism { determinism_axis_for_node, determinism_contract_witness, determinism_compile_gate } import std.determinism { DeterminismAxis, Deterministic, NonDeterministic } import v2.std.determinism { DeterminismFact } +import v2.std.diagnostic { Accepted, Outcome, Rejected } import v2.std.node { Atom, ComputationNode, @@ -96,3 +97,33 @@ test fn determinism_flags_nested_map_keys_in_subtree() -> Bool { test fn determinism_axis_map_keys_is_nondeterministic() -> Bool { axis_is_nondeterministic(a: determinism_axis_for_node(n: map_keys_call_fixture())) } + +fn gate_rejects(o: Outcome>) -> Bool { + match o { + Rejected { diagnostics: _ } => true + Accepted { value: _, diagnostics: _ } => false + } +} + +fn gate_accepts(o: Outcome>) -> Bool { + match o { + Accepted { value: _, diagnostics: _ } => true + Rejected { diagnostics: _ } => false + } +} + +test fn determinism_gate_rejects_map_keys_reach() -> Bool { + gate_rejects(o: determinism_compile_gate(node: map_keys_call_fixture())) +} + +test fn determinism_gate_accepts_clean_tree() -> Bool { + gate_accepts(o: determinism_compile_gate(node: clean_call_fixture())) +} + +test fn determinism_gate_accepts_sorted_map_keys_sibling() -> Bool { + gate_accepts(o: determinism_compile_gate(node: sorted_map_keys_call_fixture())) +} + +test fn determinism_gate_rejects_nested_map_keys_in_subtree() -> Bool { + gate_rejects(o: determinism_compile_gate(node: nested_map_keys_fixture())) +} diff --git a/src/v2/test/claim/affected_set_universe_test.dag b/src/v2/test/claim/affected_set_universe_test.dag index c6e528a65fa..4de50dae334 100644 --- a/src/v2/test/claim/affected_set_universe_test.dag +++ b/src/v2/test/claim/affected_set_universe_test.dag @@ -14,7 +14,7 @@ import v2.lens.affected_set { repo_process_universe, repo_process_universe_from } -import gunbc.ci_spec { Gate, gunbc_ci_gates, gunbc_ci_regen_floor_gates, gunbc_ci_emit_determinism_floor_gates } +import gunbc.ci_spec { Gate, gunbc_ci_gates, gunbc_ci_regen_floor_gates } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly @@ -38,7 +38,7 @@ fn identity_in(ps: List, sym: Symbol) -> Bool { test fn affected_set_universe_gate_processes_match_declared_gates() -> Bool { bag_eq( xs: process_identities(ps: ci_gate_processes()), - ys: gate_discriminants(gates: concat(concat(gunbc_ci_gates, gunbc_ci_regen_floor_gates), gunbc_ci_emit_determinism_floor_gates)), + ys: gate_discriminants(gates: concat(gunbc_ci_gates, gunbc_ci_regen_floor_gates)), eq: symbol_eq ) } diff --git a/src/v2/test/claim/ci_floor_plan_witness_test.dag b/src/v2/test/claim/ci_floor_plan_witness_test.dag index f86fc97897d..69508e71db7 100644 --- a/src/v2/test/claim/ci_floor_plan_witness_test.dag +++ b/src/v2/test/claim/ci_floor_plan_witness_test.dag @@ -25,7 +25,7 @@ import v2.workflow.ci_floor_plan { witness_regen_spec_carries_no_witness_corpus, } import gunbc.ci_layer_roots { witness_layer_roots, witness_discovery_scan_dirs } -import gunbc.ci_spec { gunbc_ci_spec, ci_spec_with_witness_entries, ci_spec_with_discovery_scan_dirs, CiSpec, EmitHostGate, LayeringImportsGate, EmitDeterminismGate } +import gunbc.ci_spec { gunbc_ci_spec, ci_spec_with_witness_entries, ci_spec_with_discovery_scan_dirs, CiSpec, EmitHostGate, LayeringImportsGate, RegenVerifyGate } import std.realization_schedule { SelectionOff, SelectionApplied, SelectionPredictOnly, NodeFrontierSelection, runnable_forbids_corpus_co_residence, runnable_heavy_whole_tree_resolve, @@ -82,11 +82,11 @@ data corpus_only_spec: CiSpec = ci_spec_with_witness_entries( entries: [synthetic_corpus_row] ) -data heavy_pair_spec_note: String = "The 2026-07-11 serial job split moved EmitDeterminismGate off the ci-floor surface (GithubActionsEmitDeterminismJob), leaving DagCompileCleanGate the floor's only heavy-whole-tree-resolve runnable — so the production spec can no longer exhibit the two-heavies-co-batch violation the serialize-heavy red control discriminates on. heavy_pair_spec restores the discriminating pair synthetically (optin_spec + EmitDeterminismGate as a second heavy row): the property under witness is the SCHEDULER's serialize-heavy behavior, which must stay pinned green-and-red regardless of which gates the production roster currently routes through the floor." +data heavy_pair_spec_note: String = "Phase D (2026-07-14) DELETED EmitDeterminismGate along with its x2 sampling oracle (tools.emit_determinism_gate/_transport) and the GithubActionsEmitDeterminismJob surface. DagCompileCleanGate remains the production floor heavy-whole-tree-resolve runnable, so the production spec cannot exhibit the two-heavies-co-batch violation the serialize-heavy red control discriminates on. heavy_pair_spec restores the discriminating pair synthetically (optin_spec + RegenVerifyGate as a second heavy row — heavy_whole_tree_resolve, host-compiler-spawning, off the ci-floor surface: the faithful analog of the removed EmitDeterminismGate, preserving the exact heavy count the deleted gate contributed): the property under witness is the scheduler serialize-heavy behavior, which must stay pinned green-and-red regardless of which gates the production roster currently routes through the floor." fn ci_spec_with_extra_heavy_gate(spec: CiSpec) -> CiSpec { CiSpec { - gates: concat(spec.gates, [EmitDeterminismGate]), + gates: concat(spec.gates, [RegenVerifyGate]), witness_entries: spec.witness_entries, discovery_scan_dirs: spec.discovery_scan_dirs, diff_policy: spec.diff_policy, diff --git a/src/v2/test/claim/ci_spec_witness_test.dag b/src/v2/test/claim/ci_spec_witness_test.dag index cd7ba6c2296..70813661e7b 100644 --- a/src/v2/test/claim/ci_spec_witness_test.dag +++ b/src/v2/test/claim/ci_spec_witness_test.dag @@ -11,7 +11,6 @@ import gunbc.ci_spec { CiSpec, DiffPolicy, DiffMergeBase, Gate, gunbc_ci_spec, gunbc_ci_gates, gunbc_ci_floor_gates, gunbc_ci_regen_floor_gates, - gunbc_ci_emit_determinism_floor_gates, ci_release_features, scheduler_invoke, gunbc_ci_run_script, gunbc_ci_workflow_run, gunbc_ci_floor_only_script, git_fetch_script, ci_release_build_script, ci_floor_build_verify_script, @@ -88,7 +87,7 @@ fn witness_gate_roster_matches_coproduct_arms() -> Bool { match coproduct_nullary_inhabitants(type_name: reflect_gate_type) { Accepted { value: inhabitants, diagnostics: _ } => bag_eq( - xs: gate_arm_symbols(gates: concat(concat(gunbc_ci_gates, gunbc_ci_regen_floor_gates), gunbc_ci_emit_determinism_floor_gates)), + xs: gate_arm_symbols(gates: concat(gunbc_ci_gates, gunbc_ci_regen_floor_gates)), ys: arm_keys, eq: symbol_eq ) && bag_eq( diff --git a/src/v2/workflow/ci_floor_plan.dag b/src/v2/workflow/ci_floor_plan.dag index 10fe18eeba9..45bfda548e5 100644 --- a/src/v2/workflow/ci_floor_plan.dag +++ b/src/v2/workflow/ci_floor_plan.dag @@ -3,14 +3,14 @@ module v2.workflow.ci_floor_plan import std.execution_mode { Hermetic, Wet } import gunbc.ci_layer_roots { witness_layer_roots, witness_exclusion_substrings, witness_discovery_scan_dirs, bin_witness_wet_entries } import gunbc.ci_spec { - gunbc_ci_spec, gunbc_ci_regen_spec, gunbc_ci_emit_determinism_spec, CiSpec, + gunbc_ci_spec, gunbc_ci_regen_spec, CiSpec, ci_spec_with_discovery_scan_dirs } import gunbc.ci_gate { Gate, EmitHostGate, LayeringImportsGate, ExtdepsExternalAuthorityGate, DagCompileCleanGate, GeneratedArtifactDriftGate, SourceRootIngestGate, RegenVerifyGate, - SelfHostReadsRealBytesGate, SelfHostStalenessGate, EmitDeterminismGate + SelfHostReadsRealBytesGate, SelfHostStalenessGate } import v2.std.algebra { filter, length, list_snoc_item, skip, zip_map } import v2.std.collection { List, Optional, Absent, Present } @@ -131,7 +131,6 @@ fn gate_node(g: Gate) -> Node { RegenVerifyGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_regen_verify } }, children: [] } SelfHostReadsRealBytesGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_self_host_reads_real_bytes } }, children: [] } SelfHostStalenessGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_self_host_staleness } }, children: [] } - EmitDeterminismGate => Node { kind: TypeNode { connective: Atom { identity: ^gunbc_floor_gate_emit_determinism } }, children: [] } } } @@ -183,13 +182,6 @@ fn gate_runnable_profile(g: Gate) -> RunnableResourceProfile { memory: substantial, execution_mode: Wet ) - EmitDeterminismGate => - runnable_resource_profile( - heavy_whole_tree_resolve: true, - spawns_host_compiler: true, - memory: substantial, - execution_mode: Wet - ) EmitHostGate => runnable_resource_profile( heavy_whole_tree_resolve: false, @@ -238,7 +230,6 @@ fn gate_runnable(g: Gate) -> Runnable { function: "self_host_realized_comparison_staleness_gate_holds", profile: profile } - EmitDeterminismGate => RunnableSingleClaim { entry: floor_gate_witness_entry, function: "emit_determinism_gate_passes", profile: profile } } } @@ -656,10 +647,6 @@ fn gunbc_ci_plan_artifact_batches() -> List> { }) } -fn gunbc_ci_emit_determinism_batches() -> List> { - gunbc_ci_floor_realization_plan_for(spec: gunbc_ci_emit_determinism_spec).schedule -} - fn gunbc_ci_floor_schedule_lens_holds() -> Bool { match schedule_lens_verdict_for_ci_floor( plan: gunbc_ci_floor_realization_plan(),