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
2 changes: 1 addition & 1 deletion .github/workflows/autopilot-create-issue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Create or update issue
uses: actions/github-script@v7
uses: actions/github-script@v9
with:
script: |
const run = context.payload.workflow_run;
Expand Down
27 changes: 3 additions & 24 deletions .github/workflows/autopilot-docs-daily.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
GH_TOKEN: ${{ secrets.ORG_READ_TOKEN || github.token }}
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Update status docs
run: |
Expand All @@ -41,28 +41,7 @@ jobs:
table="${table}\n| ${ORG}/${repo} | ${queued} | ${inprogress} | ${blocked} | ${done} |"
done

cat > docs/status.md <<EOF
# Autopilot Status

Last updated: ${ts}

## Repository coverage

${table}
EOF

python - <<'PY'
import re
from pathlib import Path

ts = """Last updated: ${ts}
See `docs/status.md` for the full status table."""
path = Path("README.md")
text = path.read_text()
block = "<!-- AUTOPILOT-STATUS:START -->\n" + ts + "\n<!-- AUTOPILOT-STATUS:END -->"
text = re.sub(r"<!-- AUTOPILOT-STATUS:START -->(.|\n)*?<!-- AUTOPILOT-STATUS:END -->", block, text)
path.write_text(text)
PY
printf '# Autopilot Status\n\nLast updated: %s\n\n## Repository coverage\n\n%b\n' "${ts}" "${table}" > docs/status.md

- name: Create PR
run: |
Expand All @@ -73,7 +52,7 @@ PY
fi
branch="docs/daily-status"
git checkout -B "${branch}"
git add docs/status.md README.md
git add docs/status.md
git commit -m "Update autopilot status"
git push -f origin "${branch}"
gh pr create -t "Daily autopilot status update" -b "Automated docs update." --base main --head "${branch}" || true
13 changes: 10 additions & 3 deletions .github/workflows/autopilot-operator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ jobs:
runs-on: [self-hosted, Windows]
env:
ORG: ${{ vars.ORG }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.ORG_AUTOPILOT_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- name: Ensure Codex on PATH
Expand All @@ -28,7 +27,7 @@ jobs:
}

- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
clean: false

Expand All @@ -44,6 +43,14 @@ jobs:
exit 1
}

- name: Validate org mutation token
shell: pwsh
run: |
if (-not $env:GH_TOKEN) {
Write-Host "ORG_AUTOPILOT_TOKEN is not set."
exit 1
}

- name: Validate Codex auth
shell: pwsh
run: |
Expand Down
19 changes: 3 additions & 16 deletions .github/workflows/autopilot-org-installer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout installer
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Sync intake workflow
run: |
Expand Down Expand Up @@ -60,24 +60,11 @@ jobs:
if [ "${opt_in}" != "true" ]; then
marker="Autopilot Installer"
existing=$(gh issue list -R ${ORG}/${repo} -s open -S "\"${marker}\"" --json number --jq '.[0].number')
body=$(cat <<EOF
${marker}

This repo does not have the Autopilot intake workflow installed.
To opt in, add a file at:

.autopilot/opt-in

Optional: specify monitored workflows in:
.autopilot/workflows.txt

Once opt-in is present, Autopilot will open a PR to install the workflow.
EOF
)
body=$(printf '%s\n' "${marker}" "" "This repo does not have the Autopilot intake workflow installed." "To opt in, add a file at:" "" ".autopilot/opt-in" "" "Optional: specify monitored workflows in:" ".autopilot/workflows.txt" "" "Once opt-in is present, Autopilot will open a PR to install the workflow.")
if [ -n "${existing}" ]; then
gh issue comment -R ${ORG}/${repo} "${existing}" -b "${body}"
else
gh issue create -R ${ORG}/${repo} -t "Autopilot intake not installed" -b "${body}" -l "autofix,queued,docs"
gh issue create -R ${ORG}/${repo} -t "Autopilot opt-in available" -b "${body}"
fi
continue
fi
Expand Down
31 changes: 13 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,22 @@ on:
pull_request:
branches: [main]

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6

- name: Install workflow validator dependency
run: python -m pip install --disable-pip-version-check PyYAML==6.0.2

- name: Validate workflow YAML
run: |
python -c "
import yaml, glob, sys
files = glob.glob('.github/workflows/*.yml')
errors = []
for f in files:
try:
with open(f) as fh:
yaml.safe_load(fh)
print(' OK:', f)
except yaml.YAMLError as e:
# GitHub Actions parses some heredoc patterns that Python
# yaml.safe_load rejects (heredoc terminators at column 0
# inside block scalars). Warn but do not fail.
print(' WARN:', f, '-', str(e).split('\n')[0])
print('Validated', len(files), 'workflow files')
"
run: python tests/validate_workflows.py

- name: Validate control-plane contracts
shell: pwsh
run: ./tests/contract-tests.ps1
24 changes: 24 additions & 0 deletions .planning/phases/01-enterprise-audit/01-UAT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Enterprise Control-Plane Audit

Audit source: `gsd-audit-fix --severity all --max 8`

## Findings

| ID | Severity | Classification | Description | File references |
| --- | --- | --- | --- | --- |
| F-01 | high | auto-fixable | Unlabeled issues bypass the documented `autofix + queued` authorization gate. | `scripts/autopilot-operator.ps1` |
| F-02 | high | auto-fixable | Issues with exhausted `try-3` attempts can be selected repeatedly. | `scripts/autopilot-operator.ps1` |
| F-03 | high | auto-fixable | Untrusted issue and comment text is passed to Codex without prompt-injection boundaries. | `scripts/autopilot-operator.ps1` |
| F-04 | high | auto-fixable | The operator stages arbitrary generated files without sensitive-path or change-size policy enforcement. | `scripts/autopilot-operator.ps1` |
| F-05 | high | auto-fixable | Changes without supported verification can still be pushed and marked done. | `scripts/autopilot-operator.ps1` |
| F-06 | high | auto-fixable | The operator workflow uses the repository-scoped token for org-wide mutations. | `.github/workflows/autopilot-operator.yml`, `docs/runbooks/operator.md` |
| F-07 | medium | auto-fixable | CI intentionally ignores workflow YAML errors and has no control-plane contract tests. | `.github/workflows/ci.yml`, `tests/contract-tests.ps1` |
| F-08 | medium | auto-fixable | The installer creates actionable autofix issues for repositories that have not opted in. | `.github/workflows/autopilot-org-installer.yml` |
| M-01 | high | manual-only | Provisioning a production GitHub App or fine-grained token and org-level RBAC requires administrator action. | Organization settings |
| M-02 | medium | manual-only | Replacing secret-based Codex auth with workload identity depends on provider support and an architecture decision. | `.github/workflows/autopilot-operator.yml` |

## Verification policy

- Run `pwsh -NoProfile -File tests/contract-tests.ps1` after each applicable fix once the contract suite exists.
- Run `git diff --check` before every commit.
- Retain this audit and the final verification report as GSD evidence.
31 changes: 31 additions & 0 deletions .planning/phases/01-enterprise-audit/01-VERIFICATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Enterprise Control-Plane Verification

Status: passed with manual deployment prerequisites

## Fixed findings

| ID | Status | Evidence |
| --- | --- | --- |
| F-01 | fixed | `0733a4b` removes unlabeled issue discovery and promotion. |
| F-02 | fixed | `0532832` excludes and defensively skips exhausted `try-3` issues. |
| F-03 | fixed | `35119dc` adds an explicit untrusted-content boundary to Codex prompts. |
| F-04 | fixed | `36daf3c`, `e34d380` enforce sensitive-path and change-size policies. |
| F-05 | fixed | `7bbcf3c`, `bc1e0e8` require supported verification by default and document exceptions. |
| F-06 | fixed | `f15b9f8`, `bc1e0e8` require and document the org mutation token contract. |
| F-07 | fixed | `26cf4f4`, `f2e6e43` repair workflow YAML, add contract tests, and migrate actions to Node.js 24-compatible majors. |
| F-08 | fixed | `c7b10fc` makes pre-opt-in installer notices non-actionable. |

## Validation

- `python tests/validate_workflows.py`: passed, 5 workflow files.
- `powershell -NoProfile -ExecutionPolicy Bypass -File tests/contract-tests.ps1`: passed.
- `python -m compileall tests`: passed.
- `yamllint` with GitHub Actions-compatible truthy rule disabled: passed.
- `git diff --check`: passed.
- Remote CI run `27290403822`: passed without Node.js runtime deprecation annotations.

## Manual-only findings

- M-01: An administrator must provision `ORG_AUTOPILOT_TOKEN` using a short-lived GitHub App installation token or a fine-grained token with access only to opted-in repositories. Required repository permissions are Contents write, Issues write, and Pull requests write.
- M-02: Secretless Codex authentication remains an architecture decision dependent on provider workload-identity support.
- M-03: A sandboxed staging organization and self-hosted Windows runner are required for a live end-to-end test of issue intake, mutation, verification, push, and pull-request creation.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ flowchart LR
## Quick start

1. Set org variable `ORG` in GitHub Actions for this repo.
2. Install `autopilot-create-issue.yml` into target repos, or use `autopilot-org-installer.yml`.
3. Ensure a self-hosted Windows runner with Codex and `OPENAI_API_KEY` is online.
4. Trigger `autopilot-operator.yml` manually to validate the setup.
2. Configure the least-privilege `ORG_AUTOPILOT_TOKEN` secret for opted-in repository mutations.
3. Install `autopilot-create-issue.yml` into target repos, or use `autopilot-org-installer.yml`.
4. Ensure a self-hosted Windows runner with Codex and `OPENAI_API_KEY` is online.
5. Trigger `autopilot-operator.yml` manually to validate the setup.

## Enterprise proof points

Expand All @@ -50,7 +51,7 @@ flowchart LR
- Acts only on issues labeled `autofix + queued`.
- Skips issues labeled `risky` or `needs-design`.
- Minimal diffs only - no secrets, no destructive operations.
- Best-effort verification before PR creation.
- Required supported verification before PR creation, with explicit approved exceptions only.

## Workflows

Expand Down
8 changes: 7 additions & 1 deletion docs/runbooks/operator.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@ $env:DRY_RUN = "true"
- `MAX_ISSUES` (default 5)
- `DRY_RUN` (default false)
- `BASE_BRANCH_OVERRIDE` (optional)
- `MAX_CHANGED_FILES` (default 20)
- `MAX_CHANGED_LINES` (default 1000)
- `ALLOW_UNVERIFIED` (default false; approved exceptions only)


## GitHub authorization
The scheduled workflow requires an `ORG_AUTOPILOT_TOKEN` secret backed by a short-lived GitHub App installation token or fine-grained token. Grant access only to opted-in repositories with Issues and Pull requests write plus Contents write. The repository-scoped `GITHUB_TOKEN` cannot perform cross-repository control-plane mutations.
## Behavior
- Only processes issues labeled `autofix` + `queued`
- Skips `risky` and `needs-design`
- Attempts best-effort verification
- Requires a supported verification command unless an approved exception sets `ALLOW_UNVERIFIED=true`
- Opens a PR if changes are safe and tests pass

## Logging
Expand Down
8 changes: 8 additions & 0 deletions memory/examples/20260610_f01_label-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-01 label-gated intake

Issue Description: Unlabeled issues bypassed the documented automation authorization gate.
State: The operator searched for all open unlabeled issues and promoted them into the queue.
Action: Removed unlabeled-issue discovery and automatic promotion.
Result: Only issues explicitly labeled `autofix` and `queued` are executable.
Diff Patch: Removed the `no:label` query and unlabeled issue mutation block.
Rationale: Explicit opt-in labels are a security boundary.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f02_bounded-attempts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-02 bounded attempts

Issue Description: Issues at `try-3` could be selected and executed repeatedly.
State: Search and processing logic did not exclude exhausted issues.
Action: Excluded `try-3` from discovery and added a defensive runtime guard.
Result: Exhausted issues remain available for human review but are not re-executed.
Diff Patch: Added `-label:try-3` and an attempt-limit guard.
Rationale: Bounded retries prevent runaway cost and repeated unsafe mutations.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f03_prompt-boundary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-03 prompt-injection boundary

Issue Description: Untrusted issue and comment content was mixed with operator instructions.
State: Repository users could place instruction-like text directly in the Codex prompt.
Action: Added explicit security policy and untrusted-content delimiters around all issue data.
Result: The model receives a clear instruction hierarchy and treats issue content as data.
Diff Patch: Added security rules plus BEGIN/END UNTRUSTED markers.
Rationale: Prompt boundaries reduce indirect prompt-injection risk at the AI execution boundary.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f04_change-policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-04 generated change-set policy

Issue Description: The operator staged every generated file without policy checks.
State: Sensitive files or unexpectedly large patches could be committed automatically.
Action: Added changed-file discovery and sensitive-path, file-count, and line-count guards.
Result: Unsafe change sets fail before verification, staging, push, or PR creation.
Diff Patch: Added `Assert-SafeChangeSet` and configurable limits.
Rationale: AI-generated output needs a deterministic enforcement boundary.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f05_verification-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-05 verification gate

Issue Description: The operator could push changes and mark issues done without running tests.
State: Unsupported repositories silently used `verification=skipped`.
Action: Blocked unverified changes by default and documented an explicit exception flag.
Result: PR creation now requires supported verification unless an operator approves an exception.
Diff Patch: Added the `ALLOW_UNVERIFIED` gate and runbook configuration.
Rationale: Verification is a release gate, not advisory metadata.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f06_org-token.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-06 org mutation token

Issue Description: The org-wide operator used a repository-scoped `GITHUB_TOKEN`.
State: Cross-repository clones, pushes, issue edits, and PR creation could not work reliably.
Action: Required a dedicated least-privilege `ORG_AUTOPILOT_TOKEN` and added fail-fast validation.
Result: The workflow contract now matches its org-wide mutation responsibilities.
Diff Patch: Replaced the token binding and documented required authorization.
Rationale: Control-plane credentials must have explicit, auditable scope.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f07_action-runtimes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-07 supported GitHub Action runtimes

Issue Description: Green remote CI warned that checkout and github-script action majors used deprecated Node.js 20.
State: GitHub will force Node.js 24 on June 16, 2026 and remove Node.js 20 on September 16, 2026.
Action: Updated to the verified latest supported majors, `actions/checkout@v6` and `actions/github-script@v9`, including templates.
Result: Workflows use Node.js 24-compatible action releases and CI prevents regression to deprecated majors.
Diff Patch: Updated action majors and added a contract assertion.
Rationale: Platform deprecation warnings are reliability defects with fixed deadlines.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f07_ci-contracts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-07 CI contracts

Issue Description: CI ignored YAML parser errors and did not test control-plane safety contracts.
State: Invalid workflows and authorization regressions could merge with green CI.
Action: Repaired fragile workflow heredocs, added workflow validation and control-plane contract tests, and made CI fail on errors.
Result: CI now enforces workflow syntax and critical operator safety boundaries.
Diff Patch: Added tests and replaced warn-only validation.
Rationale: Control-plane safeguards require executable regression tests.
8 changes: 8 additions & 0 deletions memory/examples/20260610_f08_opt-in-notice.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# F-08 non-actionable opt-in notice

Issue Description: The installer queued an autofix issue in repositories that had not opted in.
State: A notification issue itself satisfied the operator execution gate.
Action: Changed the pre-opt-in notice to an unlabeled informational issue.
Result: Non-opted-in repositories cannot be enrolled through an automated issue side effect.
Diff Patch: Removed actionable labels and clarified the notice title.
Rationale: Enrollment must be an explicit repository-owner decision.
Loading