diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 1be20d18..a63c2945 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -31,8 +31,11 @@ Use the narrowest relevant validation first: - Bash command changes: run the focused BATS tests; - general Base changes: run `env -u BASE_HOME ./bin/base-test` when practical. +Copilot cloud-agent sessions may run `.github/workflows/copilot-setup-steps.yml` +before work starts. That workflow is only a lightweight environment guardrail; +pull requests still need the focused validation above and normal CI review. + Do not require GitHub Copilot for Base development, add personal Copilot or Codex settings, store credentials, or introduce third-party agent methodology as a repo requirement. Translate useful external workflow ideas into smaller Base-native guidance. - diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 00000000..505f1fef --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,43 @@ +name: Copilot setup steps + +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + + - name: Set up Python + uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 + with: + python-version: "3.13" + + - name: Install Python dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements-dev.txt + + - name: Run lightweight Base validation + env: + PYTHONPATH: lib/python:cli/python + run: | + python -m compileall -q cli/python lib/python tests + python -m pytest tests/test_github_workflows.py tests/test_bootstrap_docs.py -q diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c86d066..31df9bed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ and Base versions are tracked in the repo-root `VERSION` file. - Added `basectl history --report` to generate local Markdown or JSON activity summaries from recent command history and log metadata without dumping raw logs or uploading telemetry. +- Added a bounded `copilot-setup-steps` workflow and documentation for optional + GitHub Copilot cloud-agent setup without changing normal local development or + CI behavior. ### Changed diff --git a/docs/README.md b/docs/README.md index 9d560364..e7625cae 100644 --- a/docs/README.md +++ b/docs/README.md @@ -120,6 +120,8 @@ reference. The filename should answer "what is this about?" `basectl repo check`, `basectl repo configure`, and the optional `basectl repo agent-guidance` layer for standardizing new Base-managed repositories. +- [Copilot Cloud-Agent Guardrails](copilot-cloud-agent.md) records the optional + GitHub Copilot setup workflow and why repository hooks are deferred. - [`basectl repo` Ownership Map](repo-command-ownership.md) maps the current `repo.sh` responsibility boundaries and records the safe split direction. - [Remote Installer Policy](remote-installer-policy.md) defines the allowed diff --git a/docs/copilot-cloud-agent.md b/docs/copilot-cloud-agent.md new file mode 100644 index 00000000..b87d12a7 --- /dev/null +++ b/docs/copilot-cloud-agent.md @@ -0,0 +1,47 @@ +# Copilot Cloud-Agent Guardrails + +Base supports GitHub Copilot cloud-agent work as an optional hosted agent +surface. It does not require Copilot for local development, does not store +personal credentials for agents, and does not replace normal CI or human review. + +## Setup Workflow + +`.github/workflows/copilot-setup-steps.yml` follows GitHub's special +`copilot-setup-steps` workflow shape. Copilot cloud agent runs the job before +starting work, while GitHub Actions also runs it when the workflow file changes +so the setup can be reviewed like normal CI. + +The Base workflow is intentionally small: + +- checks out the repository +- installs Python development dependencies from `requirements-dev.txt` +- compiles Python sources and tests +- runs the workflow-policy and bootstrap-doc tests + +It does not install Homebrew packages, clone sibling repositories, use personal +tokens, or run Base's full Bats suite. Those checks remain normal CI and review +responsibilities for the pull request. + +## Hooks Decision + +Base does not add `.github/hooks/*` in this slice. GitHub Copilot hooks can run +custom shell commands at agent lifecycle points, but Base does not yet have a +repository policy for which hook triggers are appropriate, how hook output +should be interpreted, or how hook failures should guide hosted agents. + +Until that policy exists, the safer guardrail is the bounded setup workflow plus +the existing repository instructions in `.github/copilot-instructions.md`. + +## Verification + +For Copilot-created pull requests: + +1. Review the draft PR like any other Base PR. +2. Check that normal CI still runs and passes. +3. Look for the `Copilot setup steps` workflow result when the setup workflow + itself changes or when manually testing it from the Actions tab. +4. Confirm the PR body lists the focused local validation the agent ran. + +If Copilot cloud-agent setup fails, the agent still starts with the environment +state available at that point. Treat setup failure as a signal to inspect the +PR more carefully, not as a merge decision by itself. diff --git a/tests/test_github_workflows.py b/tests/test_github_workflows.py index 9ab56082..0d652fec 100644 --- a/tests/test_github_workflows.py +++ b/tests/test_github_workflows.py @@ -11,6 +11,7 @@ REPO_ROOT = Path(__file__).resolve().parents[1] WORKFLOW_DIR = REPO_ROOT / ".github" / "workflows" COPILOT_INSTRUCTIONS = REPO_ROOT / ".github" / "copilot-instructions.md" +COPILOT_SETUP_WORKFLOW = WORKFLOW_DIR / "copilot-setup-steps.yml" BASE_PROJECT_CONFIG = REPO_ROOT / ".github" / "base-project.yml" IMPLEMENTATION_ISSUE_TEMPLATE = REPO_ROOT / ".github" / "ISSUE_TEMPLATE" / "implementation.yml" FULL_COMMIT_SHA_ACTION_REF = re.compile(r"^[^@]+@[0-9a-f]{40}$") @@ -258,6 +259,35 @@ def test_copilot_repository_instructions_stay_anchored_to_base_guidance() -> Non assert "Do not require GitHub Copilot" in text +def test_copilot_setup_steps_are_bounded_to_cloud_agent_setup() -> None: + workflow = load_workflow(COPILOT_SETUP_WORKFLOW) + triggers = workflow.get("on") or workflow.get(True) + jobs = workflow["jobs"] + setup_job = jobs["copilot-setup-steps"] + + assert workflow["name"] == "Copilot setup steps" + assert triggers == { + "workflow_dispatch": None, + "push": {"paths": [".github/workflows/copilot-setup-steps.yml"]}, + "pull_request": {"paths": [".github/workflows/copilot-setup-steps.yml"]}, + } + assert workflow["permissions"] == {"contents": "read"} + assert set(jobs) == {"copilot-setup-steps"} + assert setup_job["runs-on"] == "ubuntu-latest" + assert setup_job["timeout-minutes"] == 15 + + run_commands = "\n".join( + step.get("run", "") + for step in setup_job["steps"] + if isinstance(step, dict) + ) + assert "python -m pip install -r requirements-dev.txt" in run_commands + assert "python -m compileall -q cli/python lib/python tests" in run_commands + assert "python -m pytest tests/test_github_workflows.py tests/test_bootstrap_docs.py -q" in run_commands + assert "BASE_BASH_LIBS_DIR" not in run_commands + assert "secrets." not in run_commands + + def test_implementation_issue_template_is_copilot_ready_and_project_aligned() -> None: template = load_yaml_mapping(IMPLEMENTATION_ISSUE_TEMPLATE) project_config = load_yaml_mapping(BASE_PROJECT_CONFIG)["project"]