Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
99a0600
feat(step7): host qualification and the execution binding, kept apart
claude Sep 16, 2026
7e8dfe0
fix(step7): a qualification is versioned by the T0 it claims to satisfy
claude Sep 16, 2026
830c241
fix(step7): prove the artifact before reading it, and verify the whol…
claude Sep 16, 2026
d0cb8fa
fix(step7): a declaration of false is evidence, not damage
claude Sep 16, 2026
a40bdfd
feat(step7): join the campaign to the freeze, and make the authority …
claude Sep 16, 2026
5c9f2ee
feat(step7): the merge order becomes a predicate on the target tree
claude Sep 17, 2026
81b3a13
fix(step7): the merge gate must run the machinery, not read its name
claude Sep 17, 2026
4ce4101
fix(step7): the witness needs positive controls, or refusal proves no…
claude Sep 17, 2026
96f6377
ci(step7): the merge gate becomes a check, not a script in a drawer
claude Sep 17, 2026
61cfea9
style(step7): drop a noqa the linter does not need
claude Sep 17, 2026
b7216dd
merge: carry the instrument-aware lint fix forward
claude Sep 17, 2026
2e6683d
style(step7): satisfy the linter without moving any meaning
claude Sep 17, 2026
b72cef5
merge: carry the lint fixes forward
claude Sep 17, 2026
9bed20f
style(step7): the gate's own source passes its own repository's linter
claude Sep 17, 2026
346e164
merge: carry the lint fixes forward
claude Sep 17, 2026
1e488fd
fix(step7): an unreachable base is a question unasked, not a refusal
claude Sep 17, 2026
a49ef6c
fix(tests): the wiring suite exposes run(), so the runner can run it
claude Sep 17, 2026
d2278be
merge: carry the caller repair forward
claude Sep 17, 2026
40c7efa
merge: carry the caller repair forward
claude Sep 17, 2026
36c4c50
merge: carry the caller repair forward
claude Sep 17, 2026
715bbe7
fix(tests): rename the helper, not the entry point
claude Sep 17, 2026
1ba9f60
Merge pull request #358 from PhysShell/claude/p022-merge-gate-ci
PhysShell Sep 17, 2026
bc9eb3c
Merge pull request #357 from PhysShell/claude/p022-merge-gate
PhysShell Sep 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/p022-merge-gate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: P-022 merge gate

# The merge order for the #263 preregistration, as a check rather than an
# agreement. `mergegate.py` is run against the commit a merge would PRODUCE —
# for a pull_request event that is `github.sha`, the merge ref, not the branch
# head — because a PR number proves someone pressed a button and says nothing
# about what the merged tree contains.
#
# Two jobs, and the gate needs the controls: a gate whose own controls are
# failing must not be the thing that says a merge is safe. A failed controls job
# leaves the gate job unreported, which a required check reads as unsatisfied.
#
# This workflow is one half of the wiring. The other half is a ruleset requiring
# these two checks; without it the job reports and nothing is prevented. The
# names below are the contexts that ruleset must name:
#
# P-022 merge gate controls
# P-022 merge gate
#
# Least privilege as elsewhere in this repository: read-only, and every
# third-party `uses:` pinned to a commit SHA with its version in a comment.

permissions:
contents: read

on:
pull_request:
workflow_dispatch:

jobs:
controls:
name: P-022 merge gate controls
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
- name: the gate's own controls (fixtures ship the real tools and mutate them)
run: python tests/test_step7_mergegate.py
- name: the wiring's controls (applicability and co-change)
run: python tests/test_step7_mergegate_ci.py

gate:
name: P-022 merge gate
needs: controls
runs-on: ubuntu-latest
steps:
# Full history: the co-change rule compares the gate's own files against
# the base commit, which a shallow clone would not carry.
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.11"
- name: the gate, against the commit this merge would produce
env:
# Read through the environment rather than interpolated into the
# script body, so nothing from the event can be read as shell.
MERGE_COMMIT: ${{ github.sha }}
BASE_COMMIT: ${{ github.event.pull_request.base.sha }}
run: |
set -euo pipefail
if [ -n "${BASE_COMMIT}" ]; then
python scripts/step7/mergegate_ci.py --repo . \
--commit "${MERGE_COMMIT}" --base "${BASE_COMMIT}"
else
python scripts/step7/mergegate_ci.py --repo . --commit "${MERGE_COMMIT}"
fi
27 changes: 27 additions & 0 deletions scripts/step7/examples/host-provisioning.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"_EXAMPLE": "EXAMPLE / NOT EVIDENCE. This file is a shape to copy, never a declaration. It lives outside docs/evidence/ so it cannot be mistaken for one, and it carries no real environment id, fingerprint, operator or host name. A real declaration is signed off by an operator who has checked every line of it, and is content-addressed into a host qualification.",
"_FORM": "This is the PHYSICAL-HOST form: is_vm is false, so the four VM-only fields are explicit 'n/a: <reason>' strings. On a VM invert it — is_vm true, and fixed_vcpu, fixed_ram, live_migration_disabled and dynamic_memory_disabled must ALL be literal true. A VM that cannot promise fixed vCPU, fixed RAM, no live migration and no dynamic memory is not a measurement host, and 'n/a' is not available there.",
"_DECLARED": "Everything here is DECLARED evidence. A guest operating system cannot establish that nobody else is using the machine or that the hypervisor will not migrate it; the checker validates shape and value and binds these bytes by sha256, and never calls them machine proof.",
"_SESSION": "Per-session facts are NOT here. 'No campaign workload is running', 'no interactive user', 'no prohibited background job' are properties of a moment, not of a machine, and belong in own.net/p022/session-declaration, taken fresh for each session.",

"kind": "own.net/p022/host-provisioning",
"schema": 1,

"environment_id": "EXAMPLE-environment-id-assigned-by-the-owner",
"host_fingerprint": "sha256:EXAMPLE-copy-the-value-the-environment-manifest-observed",

"dedicated_to_p022": true,
"no_concurrent_user_workload": true,
"hosted_ci_runner": false,

"virtualization": {
"is_vm": false,
"fixed_vcpu": "n/a: physical host, no hypervisor allocates its CPUs",
"fixed_ram": "n/a: physical host, installed memory does not balloon",
"live_migration_disabled": "n/a: physical host, nothing can migrate it",
"dynamic_memory_disabled": "n/a: physical host, no dynamic memory to disable"
},

"operator": "EXAMPLE-operator-name",
"recorded_at": "EXAMPLE-2026-01-01T00:00:00+00:00"
}
Loading
Loading