Skip to content

Commit f308446

Browse files
authored
Merge pull request #8 from SaiFlyBy/flyby/cell-enforcement-pro-20261105
Enforce coordinated target-cell ownership
2 parents 379e579 + fe60aae commit f308446

24 files changed

Lines changed: 3434 additions & 64 deletions

‎.github/AGENTS.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,13 @@ Code coverage is Rust-only; do not infer Python integration coverage from it. Th
1313
## Build environment
1414

1515
Swatinem/rust-cache hashes the CARGO* and RUST* variables visible at the cache step. Workflows sharing a cache must set the same variables before that step. A mismatch restores nothing without failing the job. Keep the existing tests and checks unless an evidence-based change justifies adjusting them.
16+
17+
18+
The manual `cell-candidate` gate is the bounded exception for this owner-admitted
19+
cell-coordination implementation. It uses only the already-qualified repository
20+
runner `[self-hosted, Linux, X64, worktrunk-docker]`, requires the owner as both
21+
actor and triggering actor, checks out the fixed implementation branch and
22+
refuses a SHA mismatch before executing candidate code. Live Actions policy
23+
10761 must remain active for all workflows, allowing only user 226779263. Normal
24+
PR/CI routing is unchanged. This gate has contents-read credentials only and
25+
does not permit arbitrary repositories, refs, commands or fork candidates.

‎.github/actionlint.yaml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
self-hosted-runner:
2+
labels:
3+
- worktrunk-docker
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Prepare dedicated Worktrunk Linux runner
2+
description: Install the pinned Rust toolchain and minimal build dependencies in the owner-only runner.
3+
runs:
4+
using: composite
5+
steps:
6+
- name: Check runner platform
7+
shell: bash
8+
run: |
9+
test "$RUNNER_OS" = Linux
10+
test "$RUNNER_ARCH" = X64
11+
- name: Install native Rust dependencies
12+
shell: bash
13+
run: |
14+
sudo apt-get update
15+
sudo apt-get install -y --no-install-recommends pkg-config libssl-dev
16+
- name: Set up Python
17+
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
18+
with:
19+
python-version: '3.12'
20+
- name: Read checked-in Rust toolchain
21+
id: toolchain
22+
shell: bash
23+
run: |
24+
python - <<'PYTHON'
25+
import os, tomllib
26+
with open("rust-toolchain.toml", "rb") as source:
27+
toolchain = tomllib.load(source)["toolchain"]
28+
with open(os.environ["GITHUB_OUTPUT"], "a") as output:
29+
print("channel=" + toolchain["channel"], file=output)
30+
print("components=" + ",".join(toolchain.get("components", [])), file=output)
31+
PYTHON
32+
- name: Set up Rust
33+
uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8
34+
with:
35+
toolchain: ${{ steps.toolchain.outputs.channel }}
36+
components: ${{ steps.toolchain.outputs.components }}

‎.github/tests/ci_policy_test.py‎

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,16 @@
2525
'affected.yaml': {'collect-affected', 'affected-tests'},
2626
'ci.yaml': {'lint', 'feature-check', 'test', 'fast-checks', 'changes'},
2727
'coverage.yaml': {'code-coverage'},
28-
'mission-control-bridge.yaml': {'bridge'},
28+
'mission-control-bridge.yaml': {'bridge', 'cell-candidate'},
2929
'nightly.yaml': {'gate'},
3030
'publish-docs.yaml': {'build-docs'},
3131
'release.yaml': {'plan'},
3232
}
3333
ENTRY_GUARD = ("${{ github.event_name != 'pull_request' || "
3434
"(github.event.pull_request.head.repo.id != null && "
3535
'github.event.pull_request.head.repo.id == github.event.repository.id) }}')
36+
BRIDGE_GUARD = "${{ (" + ENTRY_GUARD[4:-3] + ")" + " && (github.event_name != 'workflow_dispatch' || inputs.candidate_sha == '') }}"
37+
CANDIDATE_GUARD = "${{ github.repository_id == '1443337118' && github.actor_id == '226779263' && github.triggering_actor == 'SaiFlyBy' && github.event_name == 'workflow_dispatch' && inputs.candidate_sha != '' }}"
3638
AFFECTED_GUARD = ("${{ github.event_name == 'pull_request' && "
3739
'github.event.pull_request.head.repo.id != null && '
3840
'github.event.pull_request.head.repo.id == github.event.repository.id }}')
@@ -129,7 +131,7 @@ def test_workflow_events_and_no_reporting_services(self):
129131
self.assert_events(name, text)
130132
for unwanted in ('codecov/codecov-action', 'CODECOV_TOKEN',
131133
'TEND_BOT_TOKEN', 'max-sixty/tend/',
132-
'gist.github.com/', 'self-hosted',
134+
'gist.github.com/',
133135
'signpath/github-action-submit-signing-request',
134136
'SIGNPATH_API_TOKEN', 'cargo publish --package worktrunk'):
135137
self.assertNotIn(unwanted.lower(), text.lower())
@@ -152,6 +154,8 @@ def assert_pr_jobs_guarded(self, name, text):
152154
self.assertIsNotNone(condition, f'unguarded entry job: {name}/{job}')
153155
expected = (COLLECT_GUARD if (name, job) == ('affected.yaml', 'collect-affected')
154156
else AFFECTED_GUARD if (name, job) == ('affected.yaml', 'affected-tests')
157+
else BRIDGE_GUARD if (name, job) == ('mission-control-bridge.yaml', 'bridge')
158+
else CANDIDATE_GUARD if (name, job) == ('mission-control-bridge.yaml', 'cell-candidate')
155159
else ENTRY_GUARD)
156160
self.assertEqual(condition.group(1), expected)
157161
else:
@@ -206,6 +210,42 @@ def test_multiline_or_quoted_job_condition_is_rejected(self):
206210
with self.subTest(replacement=replacement), self.assertRaises(AssertionError):
207211
self.assert_pr_jobs_guarded('ci.yaml', source.replace(old, replacement))
208212

213+
214+
def test_cell_qualification_route_is_owner_only_fixed_branch_and_exact_sha(self):
215+
files = workflows()
216+
labels = '[self-hosted, Linux, X64, worktrunk-docker]'
217+
lint_config = (ROOT / '.github/actionlint.yaml').read_text(encoding='utf-8')
218+
self.assertEqual(['worktrunk-docker'], re.findall(r'(?m)^ - (.+)$', lint_config))
219+
for name, source in files.items():
220+
if name != 'mission-control-bridge.yaml':
221+
self.assertNotIn('self-hosted', source)
222+
workflow = files['mission-control-bridge.yaml']
223+
parsed = jobs(workflow)
224+
block = parsed['cell-candidate']
225+
self.assertNotIn('self-hosted', parsed['bridge'])
226+
self.assertEqual(1, workflow.count(labels))
227+
self.assertIn(' runs-on: ' + labels, block)
228+
self.assertIn('ref: refs/heads/flyby/cell-enforcement-pro-20261105', block)
229+
self.assertIn('repository: SaiFlyBy/worktrunk', block)
230+
self.assertIn('ref: ${{ github.workflow_sha }}', block)
231+
self.assertIn('[[ "$EXPECTED_CANDIDATE" =~ ^[0-9a-f]{40}$ ]]', block)
232+
self.assertIn('test "$(git rev-parse HEAD)" = "$EXPECTED_CANDIDATE"', block)
233+
self.assertLess(block.index('test "$(git rev-parse HEAD)" = "$EXPECTED_CANDIDATE"'),
234+
block.index('cargo build --locked --bin wt'))
235+
self.assertIn('python tools/run_template_suite.py --expected-sha "$EXPECTED_CANDIDATE" --require-clean', block)
236+
self.assertIn('MC_SOURCE_PROOF_REPO="$RUNNER_TEMP/../worktrunk-cell-fixtures/29efd0ffcac21717fd789da2c7020a7e02501e00/', block)
237+
self.assertIn('WORKTRUNK_PRIVATE_PROOF="$HOME/worktrunk-cell-private-proof/', block)
238+
self.assertIn(' umask 077', block)
239+
self.assertIn('persist-credentials: false', block)
240+
self.assertNotIn('secrets.', block)
241+
self.assertNotIn('pull_request_target', workflow)
242+
setup = (ROOT / '.github/actions/self-hosted-setup/action.yaml').read_text(encoding='utf-8')
243+
actions = re.findall(r'(?m)^\s*(?:-\s*)?uses:\s*([^\s#]+)', block + setup)
244+
self.assertTrue(actions)
245+
for action in actions:
246+
if action != './.github/actions/self-hosted-setup':
247+
self.assertRegex(action, r'@[0-9a-f]{40}$')
248+
209249
def test_coverage_and_test_artifacts_are_retained(self):
210250
files = workflows()
211251
self.assertIn('cargo llvm-cov nextest --features shell-integration-tests', files['coverage.yaml'])
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Publish proof metadata while retaining original-fixture log contents privately."""
2+
from __future__ import annotations
3+
4+
import argparse
5+
import hashlib
6+
import json
7+
from pathlib import Path
8+
9+
10+
def checksum(path: Path) -> str:
11+
return hashlib.sha256(path.read_bytes()).hexdigest()
12+
13+
14+
def retain(private_root: Path, public_root: Path, expected_sha: str, provenance: dict) -> dict:
15+
canonical = private_root / "canonical"
16+
manifest = json.loads((canonical / "manifest.json").read_text(encoding="utf-8"))
17+
summary = json.loads((canonical / "summary.json").read_text(encoding="utf-8"))
18+
if manifest["observed_head"] != expected_sha or manifest["expected_head"] != expected_sha:
19+
raise ValueError("private proof does not identify the approved candidate")
20+
if public_root.exists() and any(public_root.iterdir()):
21+
raise ValueError("public receipt destination is occupied")
22+
public_root.mkdir(parents=True, exist_ok=True)
23+
public_manifest = {key: manifest.get(key) for key in (
24+
"schema", "started_at", "finished_at", "result", "origin_url", "branch", "observed_head",
25+
"observed_tree", "expected_head", "dirty", "python", "platform", "runner", "layer_order",
26+
)}
27+
fixture = {key: provenance[key] for key in ("repository", "commit", "tree", "sealed_checksums")}
28+
layers = []
29+
for layer in summary.get("layers", []):
30+
commands = []
31+
for command in layer.get("commands", []):
32+
row = {key: command.get(key) for key in (
33+
"state", "exit_code", "timed_out", "elapsed_seconds", "started_at", "finished_at",
34+
"stdout_sha256", "stderr_sha256",
35+
)}
36+
raw = command["command"]
37+
row["command_sha256"] = hashlib.sha256(json.dumps(raw, separators=(",", ":"), ensure_ascii=True).encode()).hexdigest()
38+
row["command"] = ["<verified-original-source>/archive/mission-control-v6" if index and raw[index-1] == "--mission-root" else value
39+
for index, value in enumerate(raw)]
40+
commands.append(row)
41+
layers.append({"layer": layer["layer"], "required": layer["required"], "state": layer["state"], "commands": commands})
42+
receipt = {"state": summary["state"], "started_at": summary.get("started_at"),
43+
"finished_at": summary.get("finished_at"), "layers": layers,
44+
"log_contents_published": False, "private_evidence_retained": True,
45+
"runner_stdout_sha256": checksum(private_root / "runner.stdout.txt"),
46+
"runner_stderr_sha256": checksum(private_root / "runner.stderr.txt")}
47+
for name, value in (("manifest.json", public_manifest), ("summary.json", receipt), ("fixture-provenance.json", fixture)):
48+
(public_root / name).write_text(json.dumps(value, indent=2) + "\n", encoding="utf-8")
49+
return receipt
50+
51+
52+
if __name__ == "__main__":
53+
parser = argparse.ArgumentParser()
54+
parser.add_argument("--private-root", required=True, type=Path)
55+
parser.add_argument("--public-root", required=True, type=Path)
56+
parser.add_argument("--candidate", required=True)
57+
parser.add_argument("--provenance", required=True, type=Path)
58+
args = parser.parse_args()
59+
result = retain(args.private_root, args.public_root, args.candidate,
60+
json.loads(args.provenance.read_text(encoding="utf-8")))
61+
print(json.dumps({"state": result["state"], "layers": [{"layer": row["layer"], "state": row["state"]} for row in result["layers"]],
62+
"log_contents_published": False}))

0 commit comments

Comments
 (0)