Skip to content
4 changes: 4 additions & 0 deletions dag/extdeps/git/git.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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>) -> String {
fold(argv, init: "", f: (acc, token) => if acc == "" { token } else { concat(concat(acc, " "), token) })
}
Expand Down
16 changes: 16 additions & 0 deletions dag/extdeps/github/actions.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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<WorkflowTrigger>
Expand Down
3 changes: 0 additions & 3 deletions dag/gunbc/ci_diff_defaults.dag

This file was deleted.

6 changes: 3 additions & 3 deletions dag/gunbc/ci_spec.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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
},
Expand All @@ -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
},
Expand Down
5 changes: 3 additions & 2 deletions dag/gunbc/ci_workflow.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
106 changes: 106 additions & 0 deletions dag/gunbc/diff_baseline.dag
Original file line number Diff line number Diff line change
@@ -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
)
}
}
3 changes: 2 additions & 1 deletion dag/gunbc/merge_admission.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions dag/gunbc/merge_admission_produce.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"

Expand Down
21 changes: 21 additions & 0 deletions dag/gunbc/repo_identity.dag
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 2 additions & 1 deletion dag/gunbc/roadmap_dispatch_actuator.dag
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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}"
Expand Down
Loading
Loading