Skip to content
This repository was archived by the owner on Jul 19, 2026. It is now read-only.

Commit 2d24b54

Browse files
committed
chore(governance): add official-upstream review guardrails
1 parent 387b3c4 commit 2d24b54

7 files changed

Lines changed: 310 additions & 0 deletions

File tree

.github/CODEOWNERS

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
# CODEOWNERS protects the official upstream review path.
4+
# It is enforced only when GitHub branch protection requires code-owner review.
5+
6+
* @RMANOV
7+
8+
/.github/ @RMANOV
9+
/scripts/ @RMANOV
10+
11+
/README.md @RMANOV
12+
/NOTICE @RMANOV
13+
/TRADEMARKS.md @RMANOV
14+
/CONTRIBUTING.md @RMANOV
15+
/SECURITY.md @RMANOV
16+
17+
/Cargo.toml @RMANOV
18+
/Cargo.lock @RMANOV
19+
/pyproject.toml @RMANOV
20+
21+
/Project_Docs/provenance/ @RMANOV
22+
/Project_Docs/commercial/ @RMANOV
23+
24+
/crates/strix-python/ @RMANOV
25+
/python/strix/llm/ @RMANOV
26+
/sim/scenarios/ @RMANOV
27+
/demo/ @RMANOV

.github/pull_request_template.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!-- SPDX-License-Identifier: Apache-2.0 -->
2+
3+
## Scope
4+
5+
- [ ] This PR keeps the public repository aligned with the Apache-2.0 open core.
6+
- [ ] This PR does not introduce customer-specific material, internal ledgers, or evaluator collateral.
7+
- [ ] This PR does not add alternate public licensing metadata.
8+
- [ ] This PR does not add local release-authority state or private signing material.
9+
- [ ] This PR does not make the public README claim capabilities or artifacts that are not present in the tree.
10+
11+
## Boundary Checks
12+
13+
- [ ] `python scripts/verify_public_surface.py`
14+
- [ ] `pytest -q`
15+
- [ ] Relevant Rust checks or tests were run, if Rust code changed.
16+
17+
## Release Provenance
18+
19+
- [ ] Official-source, trademark, or release-process changes were reviewed by the maintainer.
20+
- [ ] If this PR affects release artifacts, provenance docs and manifests were updated.

Project_Docs/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,12 @@ Internal run ledgers, deep review packs, and operational tracking material are n
77
Public provenance and boundary notes live here when they are safe to publish.
88
Private companion modules, customer-specific policies, release authority state,
99
and internal pricing or evaluator collateral do not belong in this tree.
10+
11+
## Public Governance Notes
12+
13+
- [provenance/OFFICIAL_RELEASES.md](provenance/OFFICIAL_RELEASES.md) defines
14+
the official source and release-provenance model.
15+
- [provenance/BRANCH_PROTECTION.md](provenance/BRANCH_PROTECTION.md) defines
16+
the recommended GitHub branch-protection settings for the official upstream.
17+
- [commercial/PRIVATE_COMPANION_BOUNDARY.md](commercial/PRIVATE_COMPANION_BOUNDARY.md)
18+
defines the public boundary for private companion work.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Official Upstream Branch Protection
2+
3+
This document describes the recommended GitHub settings for the official STRIX
4+
upstream.
5+
6+
The goal is to protect the official project identity and release channel. It is
7+
not intended to stop Apache-2.0 forks.
8+
9+
## Required Settings
10+
11+
- Require pull requests before merging into `main`.
12+
- Require review from code owners.
13+
- Require status checks before merging.
14+
- Require branches to be up to date before merging when practical.
15+
- Restrict direct pushes to `main` to the maintainer account.
16+
- Block force pushes on protected branches.
17+
- Block branch deletion on protected branches.
18+
- Require conversation resolution before merging.
19+
20+
## Required Checks
21+
22+
At minimum, the official upstream should require:
23+
24+
- Public Surface
25+
- Format
26+
- Clippy
27+
- Python Tests
28+
- Python FFI Smoke Tests
29+
- Python Type Check
30+
- Test (stable)
31+
- Security Audit
32+
33+
If the workflow keeps nightly tests or benchmark compilation as required gates,
34+
keep them required as well. If they are advisory, document that explicitly in
35+
the repository settings and PR description.
36+
37+
## Local Maintainer Guardrails
38+
39+
Install local hooks in maintainer checkouts:
40+
41+
```bash
42+
python scripts/install_local_git_hooks.py --force
43+
```
44+
45+
The hooks run the public-surface guard before commits and pushes. The pre-push
46+
hook also blocks direct pushes to `main` unless the maintainer deliberately sets
47+
`STRIX_ALLOW_MAIN_PUSH=1` for an exceptional release operation.
48+
49+
These hooks are a local safety layer only. GitHub branch protection remains the
50+
authoritative upstream gate.
51+
52+
## Review Discipline
53+
54+
- Treat official identity files as high-risk review surfaces.
55+
- Treat release provenance files as high-risk review surfaces.
56+
- Keep private signing keys, local release-authority state, and customer-specific
57+
material out of the public repository.
58+
- Prefer small PRs with one security or governance purpose per PR.

Project_Docs/provenance/OFFICIAL_RELEASES.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,18 @@ python scripts/strix_release_manifest.py manifest --output release-manifest.json
7575

7676
The manifest is useful provenance by itself, but it becomes authoritative only
7777
when paired with a published signature or another verifiable release process.
78+
79+
## Local Hook Setup
80+
81+
Maintainer machines should install local hooks:
82+
83+
```bash
84+
python scripts/install_local_git_hooks.py --force
85+
```
86+
87+
The hooks run the public-surface guard before commits and pushes. The pre-push
88+
hook also blocks direct pushes to `main` unless `STRIX_ALLOW_MAIN_PUSH=1` is set
89+
for an intentional maintainer release operation.
90+
91+
These hooks are not a substitute for GitHub branch protection. They are a local
92+
friction layer that catches mistakes before they reach the official upstream.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
from __future__ import annotations
4+
5+
import importlib.util
6+
import stat
7+
from pathlib import Path
8+
9+
10+
ROOT = Path(__file__).resolve().parents[2]
11+
SCRIPT_PATH = ROOT / "scripts" / "install_local_git_hooks.py"
12+
13+
14+
def load_hooks_module():
15+
spec = importlib.util.spec_from_file_location("install_local_git_hooks", SCRIPT_PATH)
16+
assert spec is not None
17+
module = importlib.util.module_from_spec(spec)
18+
assert spec.loader is not None
19+
spec.loader.exec_module(module)
20+
return module
21+
22+
23+
def test_hook_bodies_run_public_surface_guard() -> None:
24+
hooks = load_hooks_module()
25+
26+
for name in hooks.HOOK_NAMES:
27+
body = hooks.hook_body(name)
28+
assert "scripts/verify_public_surface.py" in body
29+
assert body.startswith("#!/bin/sh\nset -eu\n")
30+
31+
32+
def test_pre_push_blocks_direct_main_without_explicit_override() -> None:
33+
hooks = load_hooks_module()
34+
35+
body = hooks.hook_body("pre-push")
36+
assert "refs/heads/main" in body
37+
assert "STRIX_ALLOW_MAIN_PUSH" in body
38+
39+
40+
def test_install_hooks_writes_executable_files(tmp_path: Path) -> None:
41+
hooks = load_hooks_module()
42+
git_dir = tmp_path / ".git"
43+
44+
written = hooks.install_hooks(git_dir, force=False)
45+
46+
assert {path.name for path in written} == {"pre-commit", "pre-push"}
47+
for hook_path in written:
48+
mode = hook_path.stat().st_mode
49+
assert mode & stat.S_IXUSR
50+
assert "scripts/verify_public_surface.py" in hook_path.read_text(encoding="utf-8")
51+
52+
53+
def test_install_hooks_requires_force_for_existing_hooks(tmp_path: Path) -> None:
54+
hooks = load_hooks_module()
55+
git_dir = tmp_path / ".git"
56+
hooks.install_hooks(git_dir, force=False)
57+
58+
try:
59+
hooks.install_hooks(git_dir, force=False)
60+
except SystemExit as exc:
61+
assert "already exists" in str(exc)
62+
else:
63+
raise AssertionError("expected SystemExit for existing hooks")
64+
65+
hooks.install_hooks(git_dir, force=True)

scripts/install_local_git_hooks.py

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/usr/bin/env python3
2+
# SPDX-License-Identifier: Apache-2.0
3+
4+
"""Install local STRIX git hooks for public-surface and upstream safety checks."""
5+
6+
from __future__ import annotations
7+
8+
import argparse
9+
import os
10+
import stat
11+
import subprocess
12+
import sys
13+
from pathlib import Path
14+
15+
16+
ROOT = Path(__file__).resolve().parents[1]
17+
HOOK_NAMES = ("pre-commit", "pre-push")
18+
19+
20+
def default_git_dir() -> Path:
21+
output = subprocess.check_output(["git", "rev-parse", "--git-dir"], cwd=ROOT, text=True).strip()
22+
git_dir = Path(output)
23+
if not git_dir.is_absolute():
24+
git_dir = ROOT / git_dir
25+
return git_dir
26+
27+
28+
def hook_body(name: str) -> str:
29+
if name == "pre-commit":
30+
return """#!/bin/sh
31+
set -eu
32+
33+
repo_root="$(git rev-parse --show-toplevel)"
34+
cd "$repo_root"
35+
36+
python_cmd="${PYTHON:-python3}"
37+
if ! command -v "$python_cmd" >/dev/null 2>&1; then
38+
python_cmd=python
39+
fi
40+
41+
"$python_cmd" scripts/verify_public_surface.py
42+
43+
if git diff --cached --name-only | grep -Eq '(^|/)strix-release-authority\\.json$'; then
44+
echo "Refusing to commit local STRIX release authority state." >&2
45+
exit 1
46+
fi
47+
"""
48+
if name == "pre-push":
49+
return """#!/bin/sh
50+
set -eu
51+
52+
repo_root="$(git rev-parse --show-toplevel)"
53+
cd "$repo_root"
54+
55+
python_cmd="${PYTHON:-python3}"
56+
if ! command -v "$python_cmd" >/dev/null 2>&1; then
57+
python_cmd=python
58+
fi
59+
60+
"$python_cmd" scripts/verify_public_surface.py
61+
62+
while read local_ref local_oid remote_ref remote_oid; do
63+
case "$remote_ref" in
64+
refs/heads/main|refs/heads/master)
65+
if [ "${STRIX_ALLOW_MAIN_PUSH:-}" != "1" ]; then
66+
echo "Direct push to protected branch blocked by local STRIX hook." >&2
67+
echo "Push a PR branch, or set STRIX_ALLOW_MAIN_PUSH=1 for an intentional maintainer release." >&2
68+
exit 1
69+
fi
70+
;;
71+
esac
72+
done
73+
"""
74+
raise ValueError(f"unknown hook: {name}")
75+
76+
77+
def install_hooks(git_dir: Path, force: bool) -> list[Path]:
78+
hooks_dir = git_dir / "hooks"
79+
hooks_dir.mkdir(parents=True, exist_ok=True)
80+
81+
written: list[Path] = []
82+
for name in HOOK_NAMES:
83+
hook_path = hooks_dir / name
84+
if hook_path.exists() and not force:
85+
raise SystemExit(f"{hook_path} already exists; pass --force to replace it")
86+
87+
hook_path.write_text(hook_body(name), encoding="utf-8")
88+
current_mode = hook_path.stat().st_mode
89+
hook_path.chmod(current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
90+
written.append(hook_path)
91+
92+
return written
93+
94+
95+
def parse_args() -> argparse.Namespace:
96+
parser = argparse.ArgumentParser(description=__doc__)
97+
parser.add_argument("--git-dir", type=Path, help="Override the target .git directory")
98+
parser.add_argument("--force", action="store_true", help="Replace existing local hooks")
99+
return parser.parse_args()
100+
101+
102+
def main() -> int:
103+
args = parse_args()
104+
git_dir = args.git_dir if args.git_dir is not None else default_git_dir()
105+
written = install_hooks(git_dir, args.force)
106+
for hook_path in written:
107+
print(hook_path)
108+
return 0
109+
110+
111+
if __name__ == "__main__":
112+
try:
113+
raise SystemExit(main())
114+
except subprocess.CalledProcessError as exc:
115+
print(f"git command failed: {exc}", file=sys.stderr)
116+
raise SystemExit(1) from exc

0 commit comments

Comments
 (0)