diff --git a/.github/workflows/agent-checks.yml b/.github/workflows/agent-checks.yml new file mode 100644 index 0000000..66d5d91 --- /dev/null +++ b/.github/workflows/agent-checks.yml @@ -0,0 +1,29 @@ +name: Agent checks +on: + pull_request: + branches: [agents, main] + push: + branches: [agents] +permissions: + contents: read +concurrency: + group: agent-checks-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true +jobs: + core: + name: Windows core + runs-on: windows-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + with: + persist-credentials: false + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 + with: + python-version: '3.12' + - name: Validate syntax + run: python -m compileall -q scripts experiments/command_specialist + - name: Path binding and saved evidence invariants + run: python -m unittest discover -s experiments/command_specialist -p test_bindings.py -v + - name: Native and PowerShell contract invariants + run: python -m unittest discover -s experiments/command_specialist -p test_contract.py -v diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6477e37 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,64 @@ +# Agents and main + +`agents` is the development integration branch. `main` is the stable installation +branch. Jon authorized agents-branch automation on September 10, 2026: agents may +prepare owned changes, verify them, open ready PRs against `agents`, and merge them +when the gates below pass. This replaces per-PR human merge requests for `agents`. +Merging into `main`, publication, deployments, model/weight changes, and production +data changes still require explicit approval for the concrete batch. + +Start from fetched `agents` in an owned worktree. Preserve other sessions and reuse +existing PRs for the same work. Run commands from the repository root with Python +3.12. Required checks are: + +``` +python -m compileall -q scripts experiments/command_specialist +python -m unittest discover -s experiments/command_specialist -p test_bindings.py -v +python -m unittest discover -s experiments/command_specialist -p test_contract.py -v +``` + +Also verify the affected user operation through the public CLI and reopen its saved +artifact. Hosted CI has no private model, GPU, or transcripts; it supplements local +product verification. Do not run live model benchmarks concurrently or change the +shared Ollama service. Retain `shell-specialist-pilot` and its existing adapter. + +`.github/workflows/agent-checks.yml` runs the required `Windows core` check on PRs +into both branches and pushes to `agents`. It runs on a hosted Windows runner with +read-only permissions and no persisted checkout credentials. PR code never receives +a privileged merge token. `agents` requires this GitHub Actions check and an up-to-date PR, prevents force +pushes/deletion, and applies protection to administrators. No approving review is +required for `agents`. Main protection is pending Jon's choice of GitHub review +enforcement versus explicit chat approval; do not claim it is server-enforced. + +The local coordinator is `scripts/integrate_agents.py`. After reviewing the diff, +verifying the current head locally, and waiting for CI, run: + +``` +python scripts/integrate_agents.py --pr --verified-head +``` + +It accepts only ready same-repository PRs authored by Noisemaker111 targeting +`agents`; checks the exact head, workflow identity/result, and mergeability; and +merges with GitHub's expected-head condition. It refuses `main`. `--check-only` +checks eligibility without merging. Invoke it from a trusted checkout; passing a +head is the agent's attestation of actual local verification, not proof supplied +by CI. This is agent-operated automation, not an installed unattended scheduler. +The agent continues through merge and merged-revision verification without asking +Jon for routine agents integration approval. + +This repository is a local skill/CLI, with no hosted frontend, backend, database, +queue, production service, or deployment trigger configured here. Dev validation +runs the merged `agents` revision from an isolated worktree with fresh fixtures and +ignored `work/` artifacts. Stable users continue to install `main`; existing local +installations and sessions are not repointed. No automatic host integration is +claimed. Source revision, loaded CLI source, and saved artifact must agree when +reporting dev verification. + +For stable promotion, branch a frozen release candidate from a verified agents +revision and open a ready PR into `main`. Include the full diff, user-facing notes, +verification evidence, and rollback target (the prior main revision). Later agents +changes must not join that candidate silently. Ask Jon to approve that concrete +batch before merging. The agents GitHub PR/check gates are enforced, but this account is shared +by Jon and agents: human authorization is a workflow rule, not an independently +verified GitHub reviewer identity. The coordinator cannot merge stable. No package +or runtime deployment is implied by either merge. diff --git a/scripts/integrate_agents.py b/scripts/integrate_agents.py new file mode 100644 index 0000000..1a25f41 --- /dev/null +++ b/scripts/integrate_agents.py @@ -0,0 +1,69 @@ +"""Integrate a locally verified, CI-green owner PR into agents; never main. + +Run from a trusted agents checkout after inspecting the diff and completing the +real user-operation check. No PR code is downloaded or executed by this tool. +""" +import argparse +import json +import re +import subprocess + +REPO = "Noisemaker111/shell-forensics" +OWNER = "Noisemaker111" + + +def gh(*args): + result = subprocess.run(["gh", *args], check=True, capture_output=True, text=True, encoding="utf-8", timeout=60) + return json.loads(result.stdout) if result.stdout.strip() else None + + +def eligible(pr, verified_head): + if pr["baseRefName"] != "agents": + raise ValueError("Only agents PRs may be integrated; main needs Jon's explicit batch approval") + if pr["state"] != "OPEN" or pr["isDraft"] or pr["isCrossRepository"] or pr["author"]["login"].lower() != OWNER.lower(): + raise ValueError("Require an open, ready, same-repository owner PR") + if pr["headRefOid"] != verified_head: + raise ValueError("Head changed or was not locally verified; recheck the actual user operation") + if pr["mergeable"] != "MERGEABLE" or pr["mergeStateStatus"] != "CLEAN": + raise ValueError("PR must be up to date, mergeable, and pass branch protection") + checks = pr["statusCheckRollup"] + core = [c for c in checks if c.get("name") == "Windows core"] + if not core or any(c.get("status") != "COMPLETED" or c.get("conclusion") != "SUCCESS" for c in core): + raise ValueError("Windows core is missing, pending, or unsuccessful") + if any(c.get("conclusion") not in ("SUCCESS", "NEUTRAL", "SKIPPED") or c.get("status") != "COMPLETED" for c in checks): + raise ValueError("A check is incomplete or unsuccessful") + return core + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--pr", type=int, required=True) + parser.add_argument("--verified-head", required=True, help="Exact head already reviewed and verified through the product") + parser.add_argument("--check-only", action="store_true") + args = parser.parse_args() + fields = "state,isDraft,isCrossRepository,author,baseRefName,headRefOid,mergeable,mergeStateStatus,statusCheckRollup" + pr = gh("pr", "view", str(args.pr), "--repo", REPO, "--json", fields) + checks = eligible(pr, args.verified_head) + for check in checks: + match = re.fullmatch(r"https://github\.com/Noisemaker111/shell-forensics/actions/runs/(\d+)/job/\d+", check.get("detailsUrl", "")) + if not match: + raise ValueError("Required check is not the repository Actions job") + run = gh("api", f"repos/{REPO}/actions/runs/{match[1]}") + if (run["path"] != ".github/workflows/agent-checks.yml" or run["event"] != "pull_request" + or run["head_sha"] != args.verified_head or run["conclusion"] != "success"): + raise ValueError("Required workflow does not validate this PR head") + # Server-side strict checks and the expected-head condition close update races. + latest = gh("pr", "view", str(args.pr), "--repo", REPO, "--json", fields) + eligible(latest, args.verified_head) + if args.check_only: + print(json.dumps({"eligible": True, "pr": args.pr, "base": "agents"})) + return + subprocess.run(["gh", "pr", "merge", str(args.pr), "--repo", REPO, "--squash", "--match-head-commit", args.verified_head], check=True, timeout=60) + receipt = gh("pr", "view", str(args.pr), "--repo", REPO, "--json", "state,baseRefName,mergeCommit,url") + if receipt["state"] != "MERGED" or receipt["baseRefName"] != "agents": + raise RuntimeError("Merge receipt did not confirm agents integration") + print(json.dumps(receipt)) + + +if __name__ == "__main__": + main()