a21466add232d59cdec1604e09d58ca05659a623
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0bc734c020 |
style: ruff format the controller-state-machine branch (288 files)
Applies `ruff format` to the accumulated formatting debt on this branch. Formatting-only — no behavioral changes. Required for CI/lint's format gate (`nox -s format -- --check`), which the branch was failing on 288 tracked files that drifted from ruff's canonical style. In-progress WIP files are intentionally excluded so this commit stays a clean formatting-only diff. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
52208fd214 |
Fork-mode safety: env-derived clone URLs + full upstream protection mirroring
Two fork-mode gaps surfaced once we started preparing for Phase 4:
1. tools/merge_drive.py had hard-coded clone URLs:
GIT_REMOTE = "ssh://git@git.cleverthis.com/cleveragents/cleveragents-core.git"
HTTPS_REMOTE = "https://git.cleverthis.com/cleveragents/cleveragents-core.git"
With FORGEJO_OWNER=drew exported, the driver would correctly
*read* the fork's API state but *push merge commits* to canonical
master — exactly the disaster scenario the sentinel design was
meant to prevent. The URLs are now derived at module-load time
from API_BASE, REPO_OWNER, and REPO_NAME (host extracted from
API_BASE), so the same fork-mode env that retargets the API
automatically retargets the clone destination too. Operators
with unusual on-prem layouts (separate git host vs. API host)
can pin the URLs explicitly via FORGEJO_GIT_REMOTE /
FORGEJO_HTTPS_REMOTE.
2. tools/setup_test_fork.py was only mirroring upstream's required
status_check_contexts, not the *behavioural* settings on the
protection rule. Most importantly, required_approvals on the
fork was the Forgejo default of 0 instead of upstream's 1 —
meaning a green-CI sentinel could be merged with no review,
completely short-circuiting the reviewer ↔ driver handoff that
Phase 4 is supposed to exercise. New
fetch_upstream_protection_settings() reads the four behavioural
fields verbatim (required_approvals, block_on_outdated_branch,
dismiss_stale_approvals, require_signed_commits) and threads
them through apply_branch_protection. Verified end-to-end: the
fork's protection now matches upstream byte-for-byte
(required_approvals=1, block_on_outdated_branch=false,
dismiss_stale_approvals=false, require_signed_commits=false,
plus the same 10 wildcard contexts).
Documents the production launch story in AGENTS.md (long-running
merge_drive daemon, 15-min verify_invariant cron, "every few hours"
flag_stale_prs cron, OpenCode-launched supervisor sessions for
reviewer / implementer / merge supervisors) and shows the exact
two-shell command pattern to reproduce production against the fork.
Also documents that fork-mode does NOT need a FORGEJO_MERGE_BOT_EMAILS
override: the driver hard-codes user.email=forgejo@cleverthis.com
in the local-clone git config, and that's already the default
verifier MERGE_BOT_EMAILS — so driver-merged commits are
recognised as bot-merges in both modes.
7 new unit tests cover the env-derived clone URLs (canonical
default, FORGEJO_OWNER override, FORGEJO_API_BASE host extraction,
explicit FORGEJO_GIT_REMOTE override) and the upstream-settings
mirroring (happy / 404 / partial-payload / threading through run
into apply_branch_protection). 176 tests passing in both canonical
and fork modes (up from 169).
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
5c6f9602df |
Wire CI on sentinel fork: enable Actions/Issues + mirror upstream gate
Forgejo disables has_actions and has_issues on every newly-created fork by default, so the workflow YAML files copied across with the fork never fired. Without runs, the deterministic merge driver had nothing to wait for; without issues, verify_invariant.py could not file auto/invariant-violation issues. Branch-protection on the fork also had an empty required_checks list, so the fork's gate accepted any commit that passed the push whitelist — the opposite of what we need to test the invariant. This commit closes those three gaps in tools/setup_test_fork.py: 1. enable_repo_features() — PATCHes has_actions=true and has_issues= true on the fork after creation. Idempotent (no PATCH when both flags are already on); partial-diff (only PATCHes missing flags); --no-actions / --no-issues opt-outs for scenario tests. 2. fetch_upstream_required_checks() — reads the upstream protection rule's status_check_contexts (with the CI / job* wildcard convention) and returns the list verbatim. Returns [] on any error so callers fall back gracefully. 3. apply_branch_protection() — now accepts a required_checks list and includes enable_status_check + status_check_contexts in the PATCH/POST body. The list-comparator is order-insensitive so reordered upstream entries don't trigger spurious diffs. --no-mirror-checks opts out for no-CI scenarios. Also decouples the upstream selector from FORGEJO_OWNER. The bootstrap now reads FORGEJO_UPSTREAM_OWNER / FORGEJO_UPSTREAM_REPO (defaulting to canonical cleveragents/cleveragents-core) and accepts --upstream-owner / --upstream-repo CLI flags. With this change an operator can have FORGEJO_OWNER=drew exported in their shell to retarget downstream tools (driver / verifier / preflight / duplication) at the fork without accidentally pointing the bootstrap at drew/<repo> as its own upstream. Verified end-to-end against the live fork: - has_actions: False -> True, has_issues: False -> True (HTTP 200). - Fork's branch protection now requires the same 10 wildcard contexts upstream requires (CI / build*, CI / coverage*, CI / docker*, CI / e2e_tests*, CI / integration_tests*, CI / lint*, CI / quality*, CI / security*, CI / typecheck*, CI / unit_tests*). - merge_drive.py --status against the fork returns the same 10 required_checks list — the driver auto-discovers it via the branch-protection API. - An empty smoke commit pushed to one sentinel branch fired a pull_request: synchronize Actions run; all 14 status contexts reported back to the head SHA (5 success, 4 pending, 1 failure — the failure is a real pre-existing lint issue in the diff, which is fine; it confirms the fail-closed path also works). 12 new unit tests cover the three new helpers and the run() -> apply_branch_protection wiring (169 total, up from 157). Documents the Forgejo-specific quirks (has_actions disabled by default, status field name vs GitHub's state, wildcard required-check convention) and the bootstrap env-var split (FORGEJO_UPSTREAM_* for setup_test_fork.py vs FORGEJO_OWNER/_REPO for downstream tools) in AGENTS.md. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
73581e0f19 |
feat(auto-agents): personal-fork sentinel testing scaffolding
Establishes a fully-isolated test surface for the deterministic
auto-agents pipeline so the existing review / implementation / merge
supervisors never touch sentinel PRs. Achieves isolation by leveraging
what the existing supervisors *already* ignore: PRs in any repo other
than cleveragents/cleveragents-core. The supervisors hard-code their
--owner / --repo flags through the list_prs_* wrappers, so PRs in a
fork are absolutely invisible to them. No agent or prompt change is
required — this is entirely additive.
Env-var refactor (8 tools)
All eight pipeline tools now read FORGEJO_OWNER / FORGEJO_REPO /
FORGEJO_API_BASE (and where applicable FORGEJO_ORG /
FORGEJO_DEFAULT_BRANCH / FORGEJO_MERGE_BOT_EMAILS /
FORGEJO_PUSH_WHITELIST) at module-import time:
merge_drive.py, verify_invariant.py, forgejo_audit.py,
audit_branch_protection.py, flag_stale_prs.py,
setup_auto_labels.py, setup_branch_protection.py,
migrate_to_new_driver.py
Defaults preserve canonical-repo behaviour. Setting the env vars
retargets the entire stack at a fork:
export FORGEJO_OWNER=drew
export FORGEJO_REPO=cleveragents-core
export FORGEJO_PUSH_WHITELIST=drew
verify_invariant.py's cursor file is now partitioned by
<owner>.<repo> so a fork run never clobbers the canonical cursor;
the default canonical filename is preserved for backward
compatibility.
tools/setup_test_fork.py (new)
One-shot fork bootstrap. Identifies the calling user via GET /user,
creates the fork via POST /repos/{upstream}/forks if absent, applies
a minimum-viable branch-protection rule on the fork's master, and
provisions the canonical auto/* label set at the repo scope (forks
do not inherit org-level labels). Idempotent. --dry-run projects
what would change without mutating Forgejo.
tools/duplicate_prs_to_fork.py (new)
Duplicates the N most recent open upstream PRs into the personal
fork: fetches each head ref, pushes it to the fork as
tests/sentinel-<N>-<safe-branch>, opens a sentinel PR titled
'[sentinel #<N>] <upstream-title>' with the auto/sentinel label,
and assigns to the calling user (or --assignee). Blacklists every
operational auto/* label so sentinels start automation-clean.
Flags: --count, --pr <N> (repeatable), --fork-owner, --assignee,
--workdir, --no-reset-master, --dry-run.
auto/sentinel label
Added to setup_auto_labels.py so the fork bootstrap creates it.
Identifies sentinel PRs in the fork.
tools/preflight_phase01.sh
Banner now surfaces the Forgejo target (FORGEJO_OWNER/FORGEJO_REPO)
and warns when targeting a non-canonical repo. The harness itself
was already env-driven via the underlying Python tools.
47 new unit tests
test_env_var_overrides.py (22 tests) — every tool's env-var
resolution + canonical defaults + cursor-path partitioning.
test_setup_test_fork.py (11 tests) — fork bootstrap pure-logic +
API-mocked happy / dry-run / failure paths.
test_duplicate_prs_to_fork.py (14 tests) — filter_labels,
existing_fork_pr, duplicate_one (skip / dry-run / created /
failed), run() orchestration with mocked git + API.
tests/auto_agents/ count: 108 -> 155 passing.
AGENTS.md
New 'Targeting a personal-fork test repo' how-to documenting the
env-var override pattern and explaining why the existing
supervisors are absolutely-isolated from a fork without any agent
change. Operational tooling list extended with setup_test_fork.py
and duplicate_prs_to_fork.py. auto/* label registry includes the
new auto/sentinel row.
Verification
pytest tests/auto_agents: 155/155 passing.
setup_test_fork.py --dry-run against production: identifies
drew/cleveragents-core fork (not yet created) and projects the
full label set + branch-protection create plan.
duplicate_prs_to_fork.py --dry-run: correctly aborts with a clear
'fork does not exist; run setup_test_fork.py first' message.
CHANGELOG entry added under [Unreleased] / Added.
Co-authored-by: Cursor <cursoragent@cursor.com>
|