Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ __pycache__/
venv/
*.egg-info/
convention-report.json
dist/
.DS_Store
11 changes: 9 additions & 2 deletions projections/devops/ci-gate.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@ python tools/convention_check.py --all # human-readable, sets exit code
- `--json` output is stable for dashboards / PR annotations.
- No GPU, no model downloads, no network — runs on the cheapest runner.
- Same script runs in the editor hook and pre-PR, so CI surprises are rare.
- Green gate = merge-eligible (release clearance). This kit does not
deploy; it is the check that a change is allowed to move toward release.
- Green gate = merge-eligible. Packaging is a separate BUILD-VERIFIED /
PACKAGING-ELIGIBLE check (`tools/release_check.py`) that runs only on a
library checkout: pytest tests/others/test_dependencies.py, `python -m build
--wheel`, then import the new export from the installed wheel with
PYTHONPATH stripped (site-packages, not src/). Same spirit as
overlay_pr_gate.py — invoke the team's tooling; not a new product.
GrokBot DevOps reads that JSON via `--release-json`; it does not run
`python -m build`. Stops at build-verified. Handoff: customer index,
creds, and tag. Not CD. Not a required GitHub check on first-contribution PRs.
- **Projection drift:** `python tools/build_projections.py && git diff --exit-code`
fails if a generated surface was hand-edited instead of `rules.yaml`.
- **Scheduler contract re-verify:** `python tools/verify_scheduler_contract.py`
Expand Down
72 changes: 71 additions & 1 deletion tests/test_tooling.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,10 +390,18 @@ def _gate_json(self, example_dir: str) -> str:
self.assertIn('"findings"', proc.stdout)
return proc.stdout

def _sim(self, role: str, gate_json: str | None = None, context: bool = True) -> str:
def _sim(
self,
role: str,
gate_json: str | None = None,
context: bool = True,
release_json: Path | None = None,
) -> str:
cmd = [sys.executable, str(ROOT / "tools" / "grokbot_sim.py"), "--role", role]
if context:
cmd.extend(["--context", str(self.CONTEXT)])
if release_json is not None:
cmd.extend(["--release-json", str(release_json)])
if gate_json is None:
gate_json = self._gate_json("scaffolded_scheduler")
proc = subprocess.run(
Expand Down Expand Up @@ -493,6 +501,68 @@ def test_change_context_example_schema(self):
self.assertIn(data["state"], ("scaffolded", "gate-green", "tests-pass", "merge-eligible"))
self.assertIn("EulerLite", data["pr"]["title"] + data["pr"]["issue"])

def test_devops_packaging_not_wired_without_json(self):
out = self._sim("devops")
self.assertIn("## Packaging", out)
self.assertIn("packaging check not wired", out)
self.assertNotIn("BUILD-VERIFIED", out)

def test_devops_packaging_fed_by_release_json(self):
with tempfile.TemporaryDirectory() as td:
report = Path(td) / "packaging.json"
report.write_text(json.dumps({
"verdict": "BUILD-VERIFIED / PACKAGING-ELIGIBLE",
"object": "PNDMLiteScheduler",
"diffusers_file": "/tmp/site-packages/diffusers/__init__.py",
"steps": [
{"name": "dependency_contract", "status": "pass",
"command": "pytest tests/others/test_dependencies.py -q"},
],
"advisories": ["step() still TODO(engineer); this is a scaffold, not a product release"],
"note": "Stops at build-verified. Handoff: the customer's index, creds, and tag. Not CD.",
}))
out = self._sim("devops", release_json=report)
self.assertIn("## Packaging", out)
self.assertIn("BUILD-VERIFIED / PACKAGING-ELIGIBLE", out)
self.assertIn("PNDMLiteScheduler", out)
self.assertIn("site-packages", out)
self.assertIn("TODO(engineer)", out)
self.assertNotIn("release-eligible", out.lower())
self.assertNotIn("packaging check not wired", out)
pm = self._sim("pm")
self.assertNotIn("## Packaging", pm)

def test_grokbot_sim_does_not_invoke_build(self):
src = (ROOT / "tools" / "grokbot_sim.py").read_text()
self.assertNotIn("python -m build", src.replace("never runs python -m build", ""))
from grokbot_sim import VALID_STATES # noqa: WPS433
self.assertEqual(
VALID_STATES,
("scaffolded", "gate-green", "tests-pass", "merge-eligible"),
)


class TestReleaseCheckWrapper(unittest.TestCase):
def test_refuses_kit_standin(self):
proc = subprocess.run(
[sys.executable, str(ROOT / "tools" / "release_check.py"),
"--object", "PNDMLiteScheduler", "--json"],
cwd=ROOT,
capture_output=True,
text=True,
check=False,
env={**os.environ, "DIFFUSERS_ROOT": str(ROOT)},
)
self.assertEqual(proc.returncode, 2, proc.stdout + proc.stderr)
self.assertIn("not the kit stand-in", proc.stderr)

def test_verdict_wording(self):
src = (ROOT / "tools" / "release_check.py").read_text()
self.assertIn("BUILD-VERIFIED / PACKAGING-ELIGIBLE", src)
self.assertNotIn("release-eligible", src.lower())
self.assertNotIn('"ship"', src.lower())
self.assertIn("TODO(engineer)", src)


class TestGrokbotIphonePack(unittest.TestCase):
def test_profiles_cover_three_roles_and_never_gate(self):
Expand Down
11 changes: 9 additions & 2 deletions tools/build_projections.py
Original file line number Diff line number Diff line change
Expand Up @@ -511,8 +511,15 @@ def build_devops():
"- `--json` output is stable for dashboards / PR annotations.",
"- No GPU, no model downloads, no network — runs on the cheapest runner.",
"- Same script runs in the editor hook and pre-PR, so CI surprises are rare.",
"- Green gate = merge-eligible (release clearance). This kit does not",
" deploy; it is the check that a change is allowed to move toward release.",
"- Green gate = merge-eligible. Packaging is a separate BUILD-VERIFIED /",
" PACKAGING-ELIGIBLE check (`tools/release_check.py`) that runs only on a",
" library checkout: pytest tests/others/test_dependencies.py, `python -m build",
" --wheel`, then import the new export from the installed wheel with",
" PYTHONPATH stripped (site-packages, not src/). Same spirit as",
" overlay_pr_gate.py — invoke the team's tooling; not a new product.",
" GrokBot DevOps reads that JSON via `--release-json`; it does not run",
" `python -m build`. Stops at build-verified. Handoff: customer index,",
" creds, and tag. Not CD. Not a required GitHub check on first-contribution PRs.",
"- **Projection drift:** `python tools/build_projections.py && git diff --exit-code`",
" fails if a generated surface was hand-edited instead of `rules.yaml`.",
"- **Scheduler contract re-verify:** `python tools/verify_scheduler_contract.py`",
Expand Down
43 changes: 41 additions & 2 deletions tools/grokbot_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,13 @@ def _finding_line(f: dict, rec: dict | None = None) -> str:
)


def brief(role: str, gate: dict, rules: dict, context: dict | None = None) -> str:
def brief(
role: str,
gate: dict,
rules: dict,
context: dict | None = None,
packaging: dict | None = None,
) -> str:
context = context or {}
findings = gate.get("findings") or []
blocking = gate.get(
Expand Down Expand Up @@ -226,6 +232,30 @@ def brief(role: str, gate: dict, rules: dict, context: dict | None = None) -> st
"this briefing never fails a job and never merges.",
)
)
lines.append("")
lines.append("## Packaging")
if packaging and packaging.get("verdict"):
lines.append(
_bullet("ci", f"verdict: {packaging.get('verdict')}")
)
if packaging.get("object"):
lines.append(_bullet("ci", f"object: {packaging.get('object')}"))
proven = packaging.get("diffusers_file") or ""
if proven:
lines.append(_bullet("ci", f"diffusers.__file__: {proven}"))
for st in packaging.get("steps") or []:
lines.append(
_bullet(
"ci",
f"{st.get('name')}: {st.get('status')} ({st.get('command')})",
)
)
for adv in packaging.get("advisories") or []:
lines.append(_bullet("ci", f"advisory: {adv}"))
if packaging.get("note"):
lines.append(_bullet("ci", packaging["note"]))
else:
lines.append(_bullet("ci", "packaging check not wired"))
elif role == "pm":
lines.append("## DoD state")
if declared_state in VALID_STATES:
Expand Down Expand Up @@ -401,6 +431,12 @@ def main(argv=None) -> int:
metavar="FILE.json",
help="optional change-context JSON (pr / ci / state)",
)
ap.add_argument(
"--release-json",
metavar="FILE.json",
help="JSON from a prior `release_check.py --object <detected> --json` "
"run on the fork. Reads that report; never runs python -m build.",
)
ap.add_argument(
"gate_json",
nargs="?",
Expand All @@ -410,7 +446,10 @@ def main(argv=None) -> int:
args = ap.parse_args(argv)
gate = read_gate(args.gate_json)
context = read_context(args.context)
sys.stdout.write(brief(args.role, gate, load_rules(), context))
packaging = None
if args.release_json:
packaging = json.loads(Path(args.release_json).read_text(encoding="utf-8"))
sys.stdout.write(brief(args.role, gate, load_rules(), context, packaging))
return 0


Expand Down
Loading
Loading