diff --git a/dag/extdeps/git/git.dag b/dag/extdeps/git/git.dag index 7192efdc375..36f2377edc6 100644 --- a/dag/extdeps/git/git.dag +++ b/dag/extdeps/git/git.dag @@ -241,6 +241,10 @@ fn git_remote_ref_parts(ref: GitRef) -> GitRemoteRefParts { } } +fn git_remote_ref(parts: GitRemoteRefParts) -> GitRef { + concat(concat(parts.remote, "/"), parts.ref_name) as GitRef +} + fn git_shell_join_argv(argv: List) -> String { fold(argv, init: "", f: (acc, token) => if acc == "" { token } else { concat(concat(acc, " "), token) }) } diff --git a/dag/extdeps/github/actions.dag b/dag/extdeps/github/actions.dag index 5d941160a36..1c18a151fd3 100644 --- a/dag/extdeps/github/actions.dag +++ b/dag/extdeps/github/actions.dag @@ -33,6 +33,22 @@ data github_actions_runtime: GitHubActionsRuntime = { oidc_request_token_env: "ACTIONS_ID_TOKEN_REQUEST_TOKEN" } +data github_ci_event_surface_note: String = "GITHUB_EVENT_NAME and GITHUB_BASE_REF are default environment variables GitHub Actions sets on every job (docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables, read 2026-07-24). GITHUB_EVENT_NAME carries the wire name of the triggering event (the runtime projection of WorkflowTrigger: push / pull_request / workflow_dispatch / schedule / merge_group). GITHUB_BASE_REF carries the base (target) branch name of a pull request — set ONLY on pull_request / pull_request_target events, empty for every other event. extdeps owns these names (the shape GitHub provides); the policy that maps them to a diff baseline is a workflow-layer fact (gunbc.diff_baseline), not modeled here (extdeps must not depend upward — DESIGN §3(c))." + +data github_event_name_env: NonEmptyStr = "GITHUB_EVENT_NAME" + +data github_base_ref_env: NonEmptyStr = "GITHUB_BASE_REF" + +data github_event_name_push: String = "push" + +data github_event_name_pull_request: String = "pull_request" + +data github_event_name_workflow_dispatch: String = "workflow_dispatch" + +data github_event_name_merge_group: String = "merge_group" + +data github_event_name_schedule: String = "schedule" + type Workflow { name: String on: List diff --git a/dag/gunbc/ci_diff_defaults.dag b/dag/gunbc/ci_diff_defaults.dag deleted file mode 100644 index 63370d5762b..00000000000 --- a/dag/gunbc/ci_diff_defaults.dag +++ /dev/null @@ -1,3 +0,0 @@ -module gunbc.ci_diff_defaults - -data ci_merge_base_ref: String = "origin/main" diff --git a/dag/gunbc/ci_spec.dag b/dag/gunbc/ci_spec.dag index ec9df572d6a..073abbd0ecc 100644 --- a/dag/gunbc/ci_spec.dag +++ b/dag/gunbc/ci_spec.dag @@ -23,7 +23,7 @@ import gunbc.commit_workflow { project_ci_floor_witness_entries } import std.realization_schedule { ScheduleWitnessEntry } -import gunbc.ci_diff_defaults { ci_merge_base_ref } +import gunbc.repo_identity { gunbc_merge_target_ref } import gunbc.generated_artifact { committed_generated_artifact_paths } import gunbc.ci_failure_class { infra_retry_grep_alternation } import gunbc.ci_deploy_access { DeployAccess, ci_deploy_srv1_access } @@ -131,7 +131,7 @@ data gunbc_ci_spec: CiSpec = { witness_entries: gunbc_ci_floor_witness_entries, discovery_scan_dirs: witness_discovery_scan_dirs, diff_policy: { - base: ci_merge_base_ref, + base: gunbc_merge_target_ref as String, head: "HEAD", mode: DiffMergeBase }, @@ -144,7 +144,7 @@ data gunbc_ci_regen_spec: CiSpec = { witness_entries: [], discovery_scan_dirs: [], diff_policy: { - base: ci_merge_base_ref, + base: gunbc_merge_target_ref as String, head: "HEAD", mode: DiffMergeBase }, diff --git a/dag/gunbc/ci_workflow.dag b/dag/gunbc/ci_workflow.dag index 6619edabc8e..e02636025e9 100644 --- a/dag/gunbc/ci_workflow.dag +++ b/dag/gunbc/ci_workflow.dag @@ -39,6 +39,7 @@ import v2.workflow.ci_release_build_emit { ci_release_build_script } import std.disposition { Disposition, RealizationDispatch, Scaffold, SingleAuthority, Terminal } import std.decl_ref { DeclarationRef, WholeDeclaration } import std.types { Duration } +import gunbc.repo_identity { gunbc_default_branch_name } import extdeps.github.actions { Workflow, Job, Step, RunStep, UsesStep, WorkflowTrigger, Push, PullRequest, WorkflowDispatch, MergeGroup, @@ -633,9 +634,9 @@ data ci_workflow: Workflow = { name: "ci", on: [ WorkflowDispatch { inputs: [] }, - Push { branches: ["main"], paths: [] }, + Push { branches: [gunbc_default_branch_name], paths: [] }, PullRequest { - branches: ["main"], + branches: [gunbc_default_branch_name], types: [Opened, Synchronize, Reopened, ReadyForReview] }, MergeGroup diff --git a/dag/gunbc/diff_baseline.dag b/dag/gunbc/diff_baseline.dag new file mode 100644 index 00000000000..d1501ed785d --- /dev/null +++ b/dag/gunbc/diff_baseline.dag @@ -0,0 +1,106 @@ +module gunbc.diff_baseline + +import std.types { String, GitRef } +import std.logic { Bool } +import extdeps.git { GitRemoteRefParts, git_remote_ref } +import extdeps.github.actions { + github_event_name_pull_request, + github_event_name_push, + github_event_name_workflow_dispatch, + github_event_name_merge_group, + github_event_name_schedule +} + +data diff_baseline_law: String = "DiffBaseline is the typed answer to 'what does the affected-set diff observe HEAD against?' — the de-fork of the origin/main policy-as-literal (DESIGN §3(c)). Three arms, no fourth default: MergeTarget (the PR's actual base, derived from the CI event's GITHUB_BASE_REF), PushParent (today's policy — the repo's default merge target, for push / workflow_dispatch / merge_group / schedule / the local-or-bootstrap window with no CI event; a merge_group queue run has no base ref and its tree diffs against the default branch, and the falsifier's scheduled cold-control steps that do not set GUNBC_CI_DIFF_BASE land here too — exactly the pre-de-fork behavior), OperatorOverride (the typed GUNBC_CI_DIFF_BASE arm, used by the falsifier's HEAD~20 cold control and manual runs). resolve_diff_baseline is PURE over the observed event facts (CiDiffEvent) so its arms are RED-testable without live git; the effectful env read lives in the caller (v2.workflow.floor_diff_observe.floor_observe_ci_diff_event)." + +data diff_baseline_fail_closed_law: String = "A CI event this resolver cannot map to a base REFUSES (BaselineRefused) — it never substitutes a constant. The refusal propagates through floor_diff_observe's UnifiedDiffFail / NameStatusDiffFail into the executor's diff observation (cli_run.rs floor_git_diff_range), which HALTS the floor with a typed AFFECTED-SET REFUSAL (cause=DiffObservationRefusal): 'refuses every enrolled row rather than widening to a full-corpus run' (operator ruling 2026-07-05). The ignorance state is a loud, located, HARD STOP — NOT a run-everything widen (an earlier draft of this note misdescribed it as widen-to-whole-tree; the executor refuses, it does not widen — DESIGN §5, the exact absorbing-fallback shape being avoided). Only a genuinely-unrecognized event name lands here: the five live workflow triggers (push / pull_request / workflow_dispatch / merge_group from ci_workflow, schedule from falsifier_workflow) all map to a base arm, so a future-added trigger halts loudly until it is modeled, rather than silently defaulting to origin/main — the exact defect being deleted, now typed." + +data diff_baseline_bootstrap_arm_note: String = "The empty-event-name arm ('' — no GITHUB_EVENT_NAME) is an EXPLICIT recognized state, not the catch-all: it is the local / pre-CI-bootstrap window where there is no GitHub event at all, and its correct baseline is the repo's default merge target (PushParent), which is exactly today's behavior for a bare local floor_diff_observe invocation (DiffPolicy.base = origin/main). It is grouped with push / workflow_dispatch / merge_group / schedule because all mean 'observe against the default branch'. The catch-all (a future GitHub trigger not yet modeled — ⊤-as-ignorance, genuinely not-knowing the base) is the final else → BaselineRefused, which HALTS the floor (never a silent origin/main). Conflating the two would be the absorbing fallback DESIGN §5 forbids; keeping them distinct keeps 'no CI event' (a known state, PushParent) apart from 'CI event we cannot map' (real ignorance, halt)." + +type DiffBaseline + = MergeTarget { ref: GitRef } + | PushParent { ref: GitRef } + | OperatorOverride { ref: GitRef } + +type CiDiffEvent { + override_ref: String? + event_name: String + base_ref: String? +} + +type DiffBaselineResolution + = BaselineSelected { baseline: DiffBaseline } + | BaselineRefused { reason: String } + +fn diff_baseline_ref(b: DiffBaseline) -> GitRef { + match b { + MergeTarget { ref: r } => r + PushParent { ref: r } => r + OperatorOverride { ref: r } => r + } +} + +fn resolve_merge_target(base_ref: String?, default_remote_name: String) -> DiffBaselineResolution { + match base_ref { + Present { value: b } => + if b == "" { + BaselineRefused { reason: "pull_request event but GITHUB_BASE_REF is empty — cannot derive merge target; halting the floor (AFFECTED-SET REFUSAL)" } + } else { + BaselineSelected { + baseline: MergeTarget { + ref: git_remote_ref(parts: GitRemoteRefParts { remote: default_remote_name, ref_name: b }) + } + } + } + Absent => + BaselineRefused { reason: "pull_request event but GITHUB_BASE_REF is unset — cannot derive merge target; halting the floor (AFFECTED-SET REFUSAL)" } + } +} + +fn resolve_diff_baseline_by_event( + event_name: String, + base_ref: String?, + push_parent_ref: GitRef, + default_remote_name: String +) -> DiffBaselineResolution { + if event_name == github_event_name_pull_request { + resolve_merge_target(base_ref: base_ref, default_remote_name: default_remote_name) + } else if event_name == github_event_name_push + || event_name == github_event_name_workflow_dispatch + || event_name == github_event_name_merge_group + || event_name == github_event_name_schedule + || event_name == "" { + BaselineSelected { baseline: PushParent { ref: push_parent_ref } } + } else { + BaselineRefused { + reason: concat(concat("unrecognized CI event '", event_name), "' for diff-baseline selection — halting the floor (AFFECTED-SET REFUSAL)") + } + } +} + +fn resolve_diff_baseline( + event: CiDiffEvent, + push_parent_ref: GitRef, + default_remote_name: String +) -> DiffBaselineResolution { + match event.override_ref { + Present { value: r } => + if r == "" { + resolve_diff_baseline_by_event( + event_name: event.event_name, + base_ref: event.base_ref, + push_parent_ref: push_parent_ref, + default_remote_name: default_remote_name + ) + } else { + BaselineSelected { baseline: OperatorOverride { ref: r as GitRef } } + } + Absent => + resolve_diff_baseline_by_event( + event_name: event.event_name, + base_ref: event.base_ref, + push_parent_ref: push_parent_ref, + default_remote_name: default_remote_name + ) + } +} diff --git a/dag/gunbc/merge_admission.dag b/dag/gunbc/merge_admission.dag index 828be5675d3..ed69b3d0d92 100644 --- a/dag/gunbc/merge_admission.dag +++ b/dag/gunbc/merge_admission.dag @@ -25,6 +25,7 @@ import gunbc.ci_gate { import std.content_hash { content_hash_atom, content_hash_combine, content_hash_tagged } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } +import gunbc.repo_identity { gunbc_default_branch_name } type MergeStrategy = SquashMerge @@ -53,7 +54,7 @@ type AdmissionPolicy | KeyedReceipt data gunbc_repo_standard: RepoStandard = RepoStandard { - default_branch: "main", + default_branch: gunbc_default_branch_name, allowed_strategies: [SquashMerge], require_up_to_date: false, merge_queue_required: false, diff --git a/dag/gunbc/merge_admission_produce.dag b/dag/gunbc/merge_admission_produce.dag index b6d62e3de42..9b186ba2e30 100644 --- a/dag/gunbc/merge_admission_produce.dag +++ b/dag/gunbc/merge_admission_produce.dag @@ -3,7 +3,7 @@ module gunbc.merge_admission_produce import std.types { ContentHash, CommitSha, String, Int, List, Bool, GitRef } import std.disposition { Disposition, Scaffold, SingleAuthority } import std.decl_ref { DeclarationRef, WholeDeclaration } -import gunbc.ci_diff_defaults { ci_merge_base_ref } +import gunbc.repo_identity { gunbc_merge_target_ref } import extdeps.git import extdeps.github.checks { CheckConclusion, @@ -20,7 +20,7 @@ import gunbc.merge_admission { } import gunbc.ci_layer_roots { witness_layer_roots, witness_layer_source_flags, witness_layer_source_flags_rooted } -data merge_admission_merge_base_ref: String = ci_merge_base_ref +data merge_admission_merge_base_ref: String = gunbc_merge_target_ref as String data merge_admission_receipt_schema: String = "gunbc.merge_admission_receipt.v1" diff --git a/dag/gunbc/repo_identity.dag b/dag/gunbc/repo_identity.dag new file mode 100644 index 00000000000..d3ddda390fb --- /dev/null +++ b/dag/gunbc/repo_identity.dag @@ -0,0 +1,21 @@ +module gunbc.repo_identity + +import std.types { String, GitRef } +import extdeps.git { GitRemoteRefParts, git_remote_ref, default_remote } + +data repo_identity_authority_note: String = "The single authority for this repo's default-branch and default-remote facts (DESIGN §3). Before this carrier the merge-target ref origin/main was a String literal forked across four workflow sites (gunbc.ci_diff_defaults.ci_merge_base_ref, gunbc.roadmap_dispatch_actuator.dispatch_git_remote_ref, and — transitively — gunbc.ci_spec.diff_policy.base and gunbc.merge_admission_produce.merge_admission_merge_base_ref), the doctrine's own flagship policy-as-literal tell (§3(c): 'a literal it should receive as a parameter — origin/main...HEAD'). All four now read gunbc_merge_target_ref here; the branch name reads gunbc_default_branch_name (gunbc.ci_workflow Push/PullRequest branch filters and gunbc.merge_admission gunbc_repo_standard.default_branch). Perturbing gunbc_default_branch_name moves every consumer — the anti-fork oracle (ci_spec_witness_test.witness_repo_identity_single_authority_perturbation)." + +data repo_identity_carrier_shape_note: String = "The composed ref is grounded on GitRemoteRefParts { remote, ref_name } — the exact structured subset git_remote_ref_parts already PRODUCES and git_fetch_no_tags_shell already CONSUMES — not on full extdeps.git GitBranch × GitRemote, which would force fabricated url / fetch_refspec / is_head / upstream fields no consumer reads (§2 anemic-inflation; §6 model just-in-time). git_remote_ref completes the §4 bidirectional parts↔ref relation (the forward reading of the same rows git_remote_ref_parts reads backward)." + +data repo_identity_section_3c_discharge_note: String = "DISCHARGED 2026-07-24: DESIGN §3(c)'s own flagship policy-as-literal tell — the forked origin/main merge-base String, gunbc.ci_diff_defaults deleted — was consolidated onto this single authority. The remaining origin/main literals in the tree are prose (this note, DESIGN §3(c)'s doctrinal example) and test oracles (roadmap_dispatch_actuator_witness / affected_set_floor_runner_test expected values), which are history and value-oracles, not carriers. The recorded mark IS the carrier per §6 (no parallel-ledger intake doc). Displaced cost: #7129's probe branch, cut off a PR head, could not get an ordinary-diff run because the base was a constant origin/main instead of the PR's true base; gunbc.diff_baseline's MergeTarget arm derives it from the CI event (this PR is the enabling dependency for that probe)." + +data gunbc_default_remote_name: String = default_remote + +data gunbc_default_branch_name: String = "main" + +data gunbc_merge_target: GitRemoteRefParts = GitRemoteRefParts { + remote: gunbc_default_remote_name, + ref_name: gunbc_default_branch_name, +} + +data gunbc_merge_target_ref: GitRef = git_remote_ref(parts: gunbc_merge_target) diff --git a/dag/gunbc/roadmap_dispatch_actuator.dag b/dag/gunbc/roadmap_dispatch_actuator.dag index f2a48492505..87ca4acb8cd 100644 --- a/dag/gunbc/roadmap_dispatch_actuator.dag +++ b/dag/gunbc/roadmap_dispatch_actuator.dag @@ -46,6 +46,7 @@ import extdeps.llm.cli { shape_claude_invoke_argv } import std.disposition { Disposition, Scaffold, RealizationDispatch } import std.decl_ref { DeclarationRef, WholeDeclaration } import gunbc.host_layout { srv1_dispatch_worktree_root } +import gunbc.repo_identity { gunbc_merge_target_ref } data dispatch_tmux_process_fingerprint_observation_dissolution_trigger: Disposition = Scaffold { dissolves_to: RealizationDispatch, @@ -61,7 +62,7 @@ data dispatch_repo: String = "gunbc" data dispatch_worktree_root_note: String = "PROJECTION of gunbc.host_layout.srv1_dispatch_worktree_root — the single path authority shared with the live_deploy membership that provisions the directory (DESIGN §3; its former private copy deleted, belt-B lane 2026-07-20)." data dispatch_worktree_root: String = srv1_dispatch_worktree_root as String -data dispatch_git_remote_ref: String = "origin/main" +data dispatch_git_remote_ref: String = gunbc_merge_target_ref as String data dispatch_tmux_session_prefix: String = "gunbc-dispatch-" data dispatch_claude_process_fingerprint: NonEmptyStr = "claude" as NonEmptyStr data claude_skip_permission_settings_json: String = "{\"skipDangerousModePermissionPrompt\":true}" diff --git a/dag/test/claim/diff_baseline_witness_test.dag b/dag/test/claim/diff_baseline_witness_test.dag new file mode 100644 index 00000000000..7792be16855 --- /dev/null +++ b/dag/test/claim/diff_baseline_witness_test.dag @@ -0,0 +1,120 @@ +module test.claim.diff_baseline_witness + +import std.logic { Bool } +import std.types { String, GitRef } +import gunbc.diff_baseline { + CiDiffEvent, + DiffBaseline, + DiffBaselineResolution, + MergeTarget, + PushParent, + OperatorOverride, + BaselineSelected, + BaselineRefused, + resolve_diff_baseline, + diff_baseline_ref +} +import gunbc.repo_identity { gunbc_merge_target_ref, gunbc_default_remote_name } +import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } + +data live_tree_disposition: LiveTreeDisposition = SubstrateInputsOnly + +fn resolve_case(override_ref: String?, event_name: String, base_ref: String?) -> DiffBaselineResolution { + resolve_diff_baseline( + event: CiDiffEvent { override_ref: override_ref, event_name: event_name, base_ref: base_ref }, + push_parent_ref: gunbc_merge_target_ref, + default_remote_name: gunbc_default_remote_name + ) +} + +fn arm_tag(res: DiffBaselineResolution) -> String { + match res { + BaselineSelected { baseline: MergeTarget { ref: _ } } => "merge_target" + BaselineSelected { baseline: PushParent { ref: _ } } => "push_parent" + BaselineSelected { baseline: OperatorOverride { ref: _ } } => "operator_override" + BaselineRefused { reason: _ } => "refused" + } +} + +fn selected_ref(res: DiffBaselineResolution) -> String { + match res { + BaselineSelected { baseline: b } => diff_baseline_ref(b: b) as String + BaselineRefused { reason: _ } => "<>" + } +} + +fn witness_stacked_pr_selects_actual_base() -> Bool { + let res = resolve_case(override_ref: none, event_name: "pull_request", base_ref: Present { value: "feature/B" }) + arm_tag(res: res) == "merge_target" + && selected_ref(res: res) == "origin/feature/B" + && selected_ref(res: res) != "origin/main" +} + +fn witness_main_pr_equivalent_to_push_parent() -> Bool { + let res = resolve_case(override_ref: none, event_name: "pull_request", base_ref: Present { value: "main" }) + arm_tag(res: res) == "merge_target" + && selected_ref(res: res) == "origin/main" + && selected_ref(res: res) == (gunbc_merge_target_ref as String) +} + +fn witness_push_keeps_todays_policy() -> Bool { + let res = resolve_case(override_ref: none, event_name: "push", base_ref: none) + arm_tag(res: res) == "push_parent" + && selected_ref(res: res) == "origin/main" +} + +fn witness_workflow_dispatch_keeps_todays_policy() -> Bool { + let res = resolve_case(override_ref: none, event_name: "workflow_dispatch", base_ref: none) + arm_tag(res: res) == "push_parent" + && selected_ref(res: res) == "origin/main" +} + +fn witness_bootstrap_no_event_is_push_parent() -> Bool { + let res = resolve_case(override_ref: none, event_name: "", base_ref: none) + arm_tag(res: res) == "push_parent" + && selected_ref(res: res) == "origin/main" +} + +fn witness_operator_override_is_typed_arm() -> Bool { + let res = resolve_case(override_ref: Present { value: "HEAD~20" }, event_name: "schedule", base_ref: none) + arm_tag(res: res) == "operator_override" + && selected_ref(res: res) == "HEAD~20" +} + +fn witness_merge_group_uses_push_parent() -> Bool { + let res = resolve_case(override_ref: none, event_name: "merge_group", base_ref: none) + arm_tag(res: res) == "push_parent" + && selected_ref(res: res) == "origin/main" +} + +fn witness_schedule_no_override_is_push_parent() -> Bool { + let res = resolve_case(override_ref: none, event_name: "schedule", base_ref: none) + arm_tag(res: res) == "push_parent" + && selected_ref(res: res) == "origin/main" +} + +fn witness_unknown_event_refuses() -> Bool { + arm_tag(res: resolve_case(override_ref: none, event_name: "deployment_status", base_ref: none)) == "refused" +} + +fn witness_pull_request_empty_base_refuses() -> Bool { + arm_tag(res: resolve_case(override_ref: none, event_name: "pull_request", base_ref: Present { value: "" })) == "refused" +} + +fn witness_pull_request_absent_base_refuses() -> Bool { + arm_tag(res: resolve_case(override_ref: none, event_name: "pull_request", base_ref: none)) == "refused" +} + +test fn diff_baseline_resolution_holds() -> Bool { + witness_stacked_pr_selects_actual_base() + && witness_main_pr_equivalent_to_push_parent() + && witness_push_keeps_todays_policy() + && witness_workflow_dispatch_keeps_todays_policy() + && witness_bootstrap_no_event_is_push_parent() + && witness_operator_override_is_typed_arm() + && witness_merge_group_uses_push_parent() + && witness_schedule_no_override_is_push_parent() + && witness_unknown_event_refuses() + && witness_pull_request_empty_base_refuses() + && witness_pull_request_absent_base_refuses() +} diff --git a/docs/probes/witness_entry_eligibility_census.tsv b/docs/probes/witness_entry_eligibility_census.tsv index b43239194db..30b5a318e58 100644 --- a/docs/probes/witness_entry_eligibility_census.tsv +++ b/docs/probes/witness_entry_eligibility_census.tsv @@ -1,4 +1,4 @@ -# witness_entry_eligibility_census stamp=2026-07-24T00:55Z grain=entry_closure roots=witness_layer_roots(test.dag with test fn/data) +# witness_entry_eligibility_census stamp=2026-07-24T02:09Z grain=entry_closure roots=witness_layer_roots(test.dag with test fn/data) entry module_path subject_module subject_decl disposition retained_or_ineligible_reason first_error_class execution_leg dag/test/claim/accelerator_demo_execution_witness_test.dag test.claim.accelerator_demo_execution_witness test.claim.accelerator_demo_execution_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg dag/test/claim/accelerator_demo_gpu_witness_test.dag test.claim.accelerator_demo_gpu_witness test.claim.accelerator_demo_gpu_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg @@ -85,6 +85,7 @@ dag/test/claim/design_palette_witness_test.dag test.claim.design_palette_witness dag/test/claim/design_register_lift_parity_witness_test.dag test.claim.design_register_lift_parity_witness test.claim.design_register_lift_parity_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg dag/test/claim/dhcp_v4_mac_pinned_witness_test.dag test.claim.dhcp_v4_mac_pinned_witness test.claim.dhcp_v4_mac_pinned_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg dag/test/claim/diagnostics_test.dag test.claim.diagnostics_test test.claim.diagnostics_test witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg +dag/test/claim/diff_baseline_witness_test.dag test.claim.diff_baseline_witness test.claim.diff_baseline_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg dag/test/claim/dispatch_presentation_witness_test.dag test.claim.dispatch_presentation_witness test.claim.dispatch_presentation_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg dag/test/claim/doc_reachability_witness_test.dag test.claim.doc_reachability_witness test.claim.doc_reachability_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg dag/test/claim/docker_container_stats_witness_test.dag test.claim.docker_container_stats_witness test.claim.docker_container_stats_witness witness_entry InterpretedRetained BulkFlipPendingCensusIncomplete CensusPending InterpretedLeg diff --git a/docs/probes/witness_entry_eligibility_histogram.txt b/docs/probes/witness_entry_eligibility_histogram.txt index ed51f488b4e..017302b1daa 100644 --- a/docs/probes/witness_entry_eligibility_histogram.txt +++ b/docs/probes/witness_entry_eligibility_histogram.txt @@ -1,6 +1,6 @@ -# witness_entry_eligibility_histogram stamp=2026-07-24T00:55Z total_entries=881 +# witness_entry_eligibility_histogram stamp=2026-07-24T02:09Z total_entries=882 disposition reason count -InterpretedRetained BulkFlipPendingCensusIncomplete 701 +InterpretedRetained BulkFlipPendingCensusIncomplete 702 EmitIneligible OfflineLocalRecipe 82 EmitIneligible LongLaneScheduled 64 InterpretedRetained EmitOnDemandFamilyGrain 26 diff --git a/src/v2/compiler/self_host/witness_entry_eligibility_census.dag b/src/v2/compiler/self_host/witness_entry_eligibility_census.dag index 2154b282332..cf046024ddc 100644 --- a/src/v2/compiler/self_host/witness_entry_eligibility_census.dag +++ b/src/v2/compiler/self_host/witness_entry_eligibility_census.dag @@ -36,7 +36,7 @@ data witness_entry_eligibility_census_histogram_path: String = "docs/probes/witn data witness_entry_eligibility_census_carrier_path_dissolve_on: String = "cli_run/emit HAND-RUST rel literals mirror these rows until load-time projection (review 41784); delete mirrors when wired" -data witness_entry_eligibility_census_entry_count: Int = 881 +data witness_entry_eligibility_census_entry_count: Int = 882 data witness_entry_eligibility_census_stamp: String = "2026-07-23T16:10Z" diff --git a/src/v2/test/claim/ci_spec_witness_test.dag b/src/v2/test/claim/ci_spec_witness_test.dag index e6ec48f345f..7d1765c19b9 100644 --- a/src/v2/test/claim/ci_spec_witness_test.dag +++ b/src/v2/test/claim/ci_spec_witness_test.dag @@ -17,7 +17,12 @@ import gunbc.ci_spec { ci_documentation_only_floor_shortcut_retired_note, } import v2.workflow.ci_release_build_emit { gunbc_ci_run_script, ci_release_build_script } -import gunbc.ci_diff_defaults { ci_merge_base_ref } +import gunbc.repo_identity { + gunbc_merge_target_ref, + gunbc_default_remote_name, + gunbc_default_branch_name, +} +import extdeps.git { GitRemoteRefParts, git_remote_ref } import gunbc.ci_materialization { ci_floor_resolve_receipt_gate_script, ci_floor_materialization_receipt_gate_script, @@ -209,7 +214,20 @@ fn witness_documentation_only_floor_shortcut_retired() -> Bool { fn witness_merge_base_ref_single_authority() -> Bool { merge_admission_merge_base_ref == gunbc_ci_spec.diff_policy.base - && merge_admission_merge_base_ref == ci_merge_base_ref + && merge_admission_merge_base_ref == (gunbc_merge_target_ref as String) + && (gunbc_merge_target_ref as String) == "origin/main" +} + +fn witness_repo_identity_single_authority_perturbation() -> Bool { + let real = git_remote_ref( + parts: GitRemoteRefParts { remote: gunbc_default_remote_name, ref_name: gunbc_default_branch_name } + ) + let perturbed = git_remote_ref( + parts: GitRemoteRefParts { remote: gunbc_default_remote_name, ref_name: "develop" } + ) + (real as String) == (gunbc_merge_target_ref as String) + && (real as String) != (perturbed as String) + && (perturbed as String) == "origin/develop" } fn witness_documentation_only_gate_skip_prefix_dissolve_on_named() -> Bool { @@ -275,6 +293,7 @@ test fn ci_spec_witnesses() -> Bool { witness_floor_disposition_marker_init_dissolve_on_named() && witness_floor_disposition_marker_init_emits_dissolve_on_in_floor_runner() && witness_merge_base_ref_single_authority() && + witness_repo_identity_single_authority_perturbation() && witness_documentation_only_gate_skip_prefix_dissolve_on_named() && witness_documentation_only_gate_skip_emits_dissolve_on_in_runners() && witness_collapsed_run_script_is_build_plus_floor() && diff --git a/src/v2/test/claim/execution/floor_diff_observe_witness_test.dag b/src/v2/test/claim/execution/floor_diff_observe_witness_test.dag index 7e4d245acc6..3c4e8aaf0a2 100644 --- a/src/v2/test/claim/execution/floor_diff_observe_witness_test.dag +++ b/src/v2/test/claim/execution/floor_diff_observe_witness_test.dag @@ -1,7 +1,7 @@ module v2.test.execution.floor_diff_observe_witness import std.logic { Bool } -import gunbc.ci_spec { DiffPolicy, DiffMergeBase } +import gunbc.ci_spec { DiffMergeBase } import v2.workflow.floor_diff_observe { FloorUnifiedDiffResult, UnifiedDiffOk, @@ -9,9 +9,9 @@ import v2.workflow.floor_diff_observe { FloorNameStatusDiffResult, NameStatusDiffOk, NameStatusDiffFail, - floor_observe_git_diff_unified, + floor_run_git_diff_unified, + floor_run_git_diff_name_status, floor_observe_git_diff_unified_for_ci, - floor_observe_git_diff_name_status, floor_observe_git_diff_name_status_for_ci } import v2.std.live_tree { LiveTreeDisposition, SubstrateInputsOnly } @@ -26,13 +26,7 @@ fn witness_ci_policy_observation_succeeds() -> Bool { } fn witness_invalid_base_fails_closed() -> Bool { - match floor_observe_git_diff_unified( - policy: DiffPolicy { - base: "__gunbc_invalid_diff_base__", - head: "HEAD", - mode: DiffMergeBase - } - ) { + match floor_run_git_diff_unified(base: "__gunbc_invalid_diff_base__", head: "HEAD", mode: DiffMergeBase) { UnifiedDiffFail { reason: _ } => true UnifiedDiffOk { text: _ } => false } @@ -46,13 +40,7 @@ fn witness_ci_policy_name_status_observation_succeeds() -> Bool { } fn witness_invalid_base_name_status_fails_closed() -> Bool { - match floor_observe_git_diff_name_status( - policy: DiffPolicy { - base: "__gunbc_invalid_diff_base__", - head: "HEAD", - mode: DiffMergeBase - } - ) { + match floor_run_git_diff_name_status(base: "__gunbc_invalid_diff_base__", head: "HEAD", mode: DiffMergeBase) { NameStatusDiffFail { reason: _ } => true NameStatusDiffOk { changed_paths: _, departed_paths: _ } => false } diff --git a/src/v2/test/claim/self_host/witness_entry_eligibility_census_test.dag b/src/v2/test/claim/self_host/witness_entry_eligibility_census_test.dag index e1a060e9c99..ace1f86c8df 100644 --- a/src/v2/test/claim/self_host/witness_entry_eligibility_census_test.dag +++ b/src/v2/test/claim/self_host/witness_entry_eligibility_census_test.dag @@ -12,11 +12,11 @@ import v2.compiler.self_host.witness_bulk_routing { } import v2.std.algebra { length } -data witness_entry_eligibility_census_test_note: String = "Census receipt: full witness_layer_roots roster (881 entry closures; floor >=600 via witness_entry_eligibility_census_meets_floor) strictly exceeds the 16-entry stratified sample. Discovery exclusions do not shrink the census carrier — explicit-roster grains (execution/, long/, etc.) still need leg rows. ROADMAP 2a 744 totality is the bulk-flip dissolve trigger, not this Lane B landing witness. COUNT 876 -> 880 (CI floor endgame, 2026-07-23): +1 for cheap_gate_pool_test.dag (this PR) AND +3 healing main's latent census drift — main declared 876 while its live find was 879, undetected because affected-set selection skips this witness on PRs that do not touch its closure; this plumbing PR's affected set is the first to run it against the drifted tree. The count lives in four hand-synced copies (this literal, the note, witness_entry_eligibility_census_entry_count, the committed TSV rows) — a §3 parallel-representation the emit transport reconciles at regen but no CI witness walls between regens; restructuring is main's carrier, out of this PR's scope. COUNT 880 -> 881 (schedule-retention M2, 2026-07-24): +1 for executor_schedule_retention_test.dag, this PR's new in-corpus witness; the test literal and note were the two hand-synced copies the regen transport does not touch, and this PR's affected set is the first to run this witness against the bumped module constant." +data witness_entry_eligibility_census_test_note: String = "Census receipt: full witness_layer_roots roster (882 entry closures; floor >=600 via witness_entry_eligibility_census_meets_floor) strictly exceeds the 16-entry stratified sample. Discovery exclusions do not shrink the census carrier — explicit-roster grains (execution/, long/, etc.) still need leg rows. ROADMAP 2a 744 totality is the bulk-flip dissolve trigger, not this Lane B landing witness. COUNT 876 -> 880 (CI floor endgame, 2026-07-23): +1 for cheap_gate_pool_test.dag (this PR) AND +3 healing main's latent census drift — main declared 876 while its live find was 879, undetected because affected-set selection skips this witness on PRs that do not touch its closure; this plumbing PR's affected set is the first to run it against the drifted tree. The count lives in four hand-synced copies (this literal, the note, witness_entry_eligibility_census_entry_count, the committed TSV rows) — a §3 parallel-representation the emit transport reconciles at regen but no CI witness walls between regens; restructuring is main's carrier, out of this PR's scope. COUNT 880 -> 881 (schedule-retention M2, 2026-07-24): +1 for executor_schedule_retention_test.dag, this PR's new in-corpus witness; the test literal and note were the two hand-synced copies the regen transport does not touch, and this PR's affected set is the first to run this witness against the bumped module constant. COUNT 881 -> 882 (gunbc#7146, 2026-07-24): +1 for diff_baseline_witness_test.dag, the diff-baseline modeling cleanup's RED-control witness — a census collision with the schedule-retention M2 bump above (both independently went 880 -> 881 for different witnesses; the merge is 882)." test fn witness_entry_eligibility_census_count_holds() -> Bool { witness_entry_eligibility_census_meets_floor() - && witness_entry_eligibility_census_entry_count == 881 + && witness_entry_eligibility_census_entry_count == 882 && witness_entry_eligibility_census_entry_count > witness_bulk_entry_count_expected && length(xs: witness_bulk_routing_sample_roster) == witness_bulk_entry_count_expected } diff --git a/src/v2/workflow/floor_diff_observe.dag b/src/v2/workflow/floor_diff_observe.dag index dd751175070..5746854a0f0 100644 --- a/src/v2/workflow/floor_diff_observe.dag +++ b/src/v2/workflow/floor_diff_observe.dag @@ -7,7 +7,15 @@ import gunbc.ci_spec { DiffTwoDot, gunbc_ci_spec } -import std.types { String, NonEmptyStr } +import gunbc.diff_baseline { + CiDiffEvent, + DiffBaselineResolution, + BaselineSelected, + BaselineRefused, + resolve_diff_baseline, + diff_baseline_ref +} +import std.types { String, NonEmptyStr, GitRef } import std.logic { Bool } import extdeps.git import extdeps.git { @@ -20,8 +28,10 @@ import extdeps.git { git_diff_status_entries_touched_paths, git_diff_status_entries_departed_paths, git_diff_status_entries_any_unknown, - git_diff_name_status_field_separator + git_diff_name_status_field_separator, + git_remote_ref_parts } +import extdeps.github.actions { github_event_name_env, github_base_ref_env } import extdeps.shell type FloorUnifiedDiffResult @@ -40,8 +50,27 @@ fn floor_env_override_or(env_name: NonEmptyStr, default: String) -> String { } } -fn floor_resolved_base(policy: DiffPolicy) -> String { - floor_env_override_or(env_name: "GUNBC_CI_DIFF_BASE" as NonEmptyStr, default: policy.base) +fn floor_env_optional(env_name: NonEmptyStr) -> String? { + shell.Env.Get(name: env_name).value +} + +data floor_diff_baseline_law: String = "The affected-set base is RESOLVED from the CI event, not a constant (gunbc.diff_baseline; the de-fork of the origin/main policy-as-literal, DESIGN §3(c)). floor_observe_ci_diff_event reads the three env facts — GUNBC_CI_DIFF_BASE (the gunbc OperatorOverride channel, e.g. the falsifier's HEAD~20 cold control), GITHUB_EVENT_NAME, GITHUB_BASE_REF (the GitHub surface extdeps.github.actions owns) — and resolve_diff_baseline (pure) maps them: pull_request → MergeTarget against the PR's ACTUAL base (was silently origin/main — the stacked-PR over-selection defect #7129 hit); push / workflow_dispatch / merge_group / schedule / local-bootstrap → PushParent (today's DiffPolicy.base, origin/main — merge_group queue runs and the falsifier's scheduled cold-control steps have no base ref and diff their tree against the default branch, matching the pre-de-fork behavior so the floor RUNS rather than aborting). GUNBC_CI_DIFF_BASE → OperatorOverride. A genuinely-unrecognized event yields BaselineRefused → UnifiedDiffFail / NameStatusDiffFail → the executor HALTS the floor with a typed AFFECTED-SET REFUSAL (refuses every enrolled row, never widens to a full-corpus run; operator ruling 2026-07-05). The PushParent ref carries its own remote, so the MergeTarget remote is derived from it (git_remote_ref_parts) rather than re-imported." + +fn floor_observe_ci_diff_event() -> CiDiffEvent { + CiDiffEvent { + override_ref: floor_env_optional(env_name: "GUNBC_CI_DIFF_BASE" as NonEmptyStr), + event_name: floor_env_override_or(env_name: github_event_name_env, default: ""), + base_ref: floor_env_optional(env_name: github_base_ref_env) + } +} + +fn floor_resolve_baseline(policy: DiffPolicy) -> DiffBaselineResolution { + let push_parent = policy.base as GitRef + resolve_diff_baseline( + event: floor_observe_ci_diff_event(), + push_parent_ref: push_parent, + default_remote_name: git_remote_ref_parts(ref: push_parent).remote + ) } fn floor_resolved_head(policy: DiffPolicy) -> String { @@ -55,20 +84,29 @@ fn floor_git_diff_range_of_mode(mode: DiffMode) -> GitDiffRange { } } +fn floor_run_git_diff_unified(base: String, head: String, mode: DiffMode) -> FloorUnifiedDiffResult { + let result = git.Core.DiffUnified0(base: base, head: head, range: floor_git_diff_range_of_mode(mode: mode)) + if result.success { + UnifiedDiffOk { text: result.diff } + } else { + UnifiedDiffFail { reason: "git diff -U0 exited nonzero: invalid ref or not a git repository" } + } +} + fn floor_observe_git_diff_unified(policy: DiffPolicy) -> FloorUnifiedDiffResult { let unified_injection = shell.Env.Get(name: "GUNBC_CI_DIFF_UNIFIED" as NonEmptyStr) match unified_injection.value { Present { value: injected } => UnifiedDiffOk { text: injected } - Absent => { - let base = floor_resolved_base(policy: policy) - let head = floor_resolved_head(policy: policy) - let result = git.Core.DiffUnified0(base: base, head: head, range: floor_git_diff_range_of_mode(mode: policy.mode)) - if result.success { - UnifiedDiffOk { text: result.diff } - } else { - UnifiedDiffFail { reason: "git diff -U0 exited nonzero: invalid ref or not a git repository" } + Absent => + match floor_resolve_baseline(policy: policy) { + BaselineRefused { reason: reason } => UnifiedDiffFail { reason: reason } + BaselineSelected { baseline: baseline } => + floor_run_git_diff_unified( + base: diff_baseline_ref(b: baseline) as String, + head: floor_resolved_head(policy: policy), + mode: policy.mode + ) } - } } } @@ -96,25 +134,34 @@ fn floor_decode_injected_name_status(injected: String) -> String { join(split(s: injected, delimiter: "\\000"), git_diff_name_status_field_separator) } +fn floor_run_git_diff_name_status(base: String, head: String, mode: DiffMode) -> FloorNameStatusDiffResult { + let result = git.Core.DiffNameStatus( + base: base, + head: head, + range: floor_git_diff_range_of_mode(mode: mode) + ) + if !result.success { + NameStatusDiffFail { reason: "git diff --name-status -z exited nonzero: invalid ref or not a git repository" } + } else { + floor_name_status_from_raw(raw: result.raw) + } +} + fn floor_observe_git_diff_name_status(policy: DiffPolicy) -> FloorNameStatusDiffResult { let name_status_injection = shell.Env.Get(name: "GUNBC_CI_DIFF_NAME_STATUS" as NonEmptyStr) match name_status_injection.value { Present { value: injected } => floor_name_status_from_raw(raw: floor_decode_injected_name_status(injected: injected)) - Absent => { - let base = floor_resolved_base(policy: policy) - let head = floor_resolved_head(policy: policy) - let result = git.Core.DiffNameStatus( - base: base, - head: head, - range: floor_git_diff_range_of_mode(mode: policy.mode) - ) - if !result.success { - NameStatusDiffFail { reason: "git diff --name-status -z exited nonzero: invalid ref or not a git repository" } - } else { - floor_name_status_from_raw(raw: result.raw) + Absent => + match floor_resolve_baseline(policy: policy) { + BaselineRefused { reason: reason } => NameStatusDiffFail { reason: reason } + BaselineSelected { baseline: baseline } => + floor_run_git_diff_name_status( + base: diff_baseline_ref(b: baseline) as String, + head: floor_resolved_head(policy: policy), + mode: policy.mode + ) } - } } }