chore(dev): enforce fresh-base startup guard for CORE agents (#849)
This commit is contained in:
parent
2dc1462b40
commit
3d267b1fa3
4 changed files with 205 additions and 5 deletions
9
.github/copilot-instructions.md
vendored
9
.github/copilot-instructions.md
vendored
|
|
@ -112,7 +112,14 @@ Learning is reviewed mutation:
|
|||
|
||||
## Validation
|
||||
|
||||
Use CLI suites:
|
||||
Before starting any task, run the startup guard to ensure a fresh base:
|
||||
|
||||
```bash
|
||||
source scripts/agent_startup.sh
|
||||
# For PR-resume tasks: CODEX_ALLOW_NON_MAIN_BASE=1 source scripts/agent_startup.sh
|
||||
```
|
||||
|
||||
Then use CLI suites to validate your work:
|
||||
|
||||
```bash
|
||||
core test --suite smoke -q
|
||||
|
|
|
|||
11
GPT55.md
11
GPT55.md
|
|
@ -15,13 +15,18 @@ generic chatbot, not an infrastructure playground.
|
|||
1. **Read this file in full.**
|
||||
2. **Read `AGENTS.md` in full.**
|
||||
3. **Read `docs/runtime_contracts.md` in full.**
|
||||
4. **Run the smoke suite:**
|
||||
4. **Run the startup guard** — enforces fresh-base and clean-tree invariants:
|
||||
```bash
|
||||
source scripts/agent_startup.sh
|
||||
```
|
||||
For a PR-resume task: `CODEX_ALLOW_NON_MAIN_BASE=1 source scripts/agent_startup.sh`
|
||||
5. **Run the smoke suite:**
|
||||
```bash
|
||||
core test --suite smoke -q
|
||||
```
|
||||
5. **Check for a handoff doc** — read the most recent `HANDOFF-*.md` if one
|
||||
6. **Check for a handoff doc** — read the most recent `HANDOFF-*.md` if one
|
||||
exists dated within the last 3 days.
|
||||
6. **State your task scope** — before editing, name the module(s) and the
|
||||
7. **State your task scope** — before editing, name the module(s) and the
|
||||
invariant you will prove was not violated.
|
||||
|
||||
---
|
||||
|
|
|
|||
12
GROK.md
12
GROK.md
|
|
@ -104,7 +104,16 @@ Never use `git reset --hard`, broad `git checkout .`, broad `git restore .`, `gi
|
|||
|
||||
### 2. Establish a clean, current baseline
|
||||
|
||||
After the tree is clean or unknown work is safely stashed:
|
||||
**Run the startup guard first** — it automates steps 2–4 and will hard-stop if the worktree is stale:
|
||||
|
||||
```bash
|
||||
source scripts/agent_startup.sh
|
||||
```
|
||||
|
||||
For a new task (default, no env vars) the script requires `HEAD == origin/main` and a clean tree.
|
||||
For a PR-resume task, set `CODEX_ALLOW_NON_MAIN_BASE=1`; the script then verifies `origin/main` is a strict ancestor of `HEAD`.
|
||||
|
||||
If you cannot source the script, perform the equivalent steps manually:
|
||||
|
||||
```bash
|
||||
git fetch origin --prune
|
||||
|
|
@ -115,6 +124,7 @@ git status --short --branch
|
|||
|
||||
If `main` cannot fast-forward, stop and report the exact state. Do not merge, rebase, or resolve conflicts unless explicitly instructed.
|
||||
|
||||
|
||||
### 3. Prefer a new worktree for non-trivial implementation
|
||||
|
||||
For non-trivial runtime, reasoning, eval, teaching, pack, or multi-file work, create a fresh worktree from current `origin/main`:
|
||||
|
|
|
|||
178
scripts/agent_startup.sh
Executable file
178
scripts/agent_startup.sh
Executable file
|
|
@ -0,0 +1,178 @@
|
|||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# scripts/agent_startup.sh
|
||||
#
|
||||
# Codex / Gemini environment startup guard for CORE.
|
||||
#
|
||||
# PURPOSE
|
||||
# Catch stale-base worktrees before they create replay/conflict PRs.
|
||||
# Every new implementation task must run from HEAD == origin/main on a
|
||||
# clean tree. PR-resume tasks may run from a branch, but only when
|
||||
# origin/main is a proper ancestor of HEAD.
|
||||
#
|
||||
# USAGE
|
||||
# source scripts/agent_startup.sh # default (strict)
|
||||
# CODEX_ALLOW_DIRTY=1 source ... # allow dirty worktree (debug only)
|
||||
# CODEX_ALLOW_NON_MAIN_BASE=1 source ... # allow HEAD ahead of origin/main
|
||||
# # (PR-resume; ancestry still checked)
|
||||
#
|
||||
# EXIT CODES
|
||||
# 0 All checks passed — safe to proceed.
|
||||
# 1 Guard failed — do NOT start the task.
|
||||
#
|
||||
# ENVIRONMENT CONTROLS
|
||||
# CODEX_ALLOW_DIRTY=1 Bypass dirty-tree check (debug/wip only).
|
||||
# CODEX_ALLOW_NON_MAIN_BASE=1 Allow HEAD ahead of origin/main, but only
|
||||
# when origin/main is a strict ancestor of HEAD.
|
||||
#
|
||||
# NON-GOALS
|
||||
# This script does NOT touch runtime, evals, reports, teaching proposals,
|
||||
# packs, policy, identity, recall, vault, field, algebra, or serving paths.
|
||||
# =============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
_PASS="✓"
|
||||
_FAIL="✗"
|
||||
_WARN="!"
|
||||
|
||||
_ok() { printf '%s %s\n' "$_PASS" "$*"; }
|
||||
_fail() { printf '%s %s\n' "$_FAIL" "$*" >&2; }
|
||||
_warn() { printf '%s %s\n' "$_WARN" "$*" >&2; }
|
||||
_sep() { printf '%s\n' "────────────────────────────────────────────────────────────────────────"; }
|
||||
|
||||
_die() {
|
||||
_fail "$*"
|
||||
_fail "Aborting. Fix the issue above, then re-run this script."
|
||||
# Use 'return' rather than 'exit' so sourced scripts don't kill the shell.
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── header ───────────────────────────────────────────────────────────────────
|
||||
|
||||
_sep
|
||||
printf 'CORE agent startup guard | %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
|
||||
_sep
|
||||
|
||||
# ── 0. repository root ────────────────────────────────────────────────────────
|
||||
|
||||
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
|
||||
_die "Not inside a git repository."
|
||||
}
|
||||
_ok "repo root: $REPO_ROOT"
|
||||
|
||||
# ── 1. fetch origin --prune ───────────────────────────────────────────────────
|
||||
|
||||
printf 'Fetching origin --prune …\n'
|
||||
if git fetch origin --prune --quiet; then
|
||||
_ok "fetch origin --prune"
|
||||
else
|
||||
_die "git fetch origin --prune failed. Check network / remote config."
|
||||
fi
|
||||
|
||||
# ── 2. diagnostic: HEAD, branch, origin/main, merge-base ──────────────────────
|
||||
|
||||
HEAD_SHA="$(git rev-parse HEAD)"
|
||||
BRANCH="$(git rev-parse --abbrev-ref HEAD)"
|
||||
ORIGIN_MAIN_SHA="$(git rev-parse origin/main 2>/dev/null)" || {
|
||||
_die "origin/main does not exist. Ensure origin is correctly configured."
|
||||
}
|
||||
MERGE_BASE="$(git merge-base HEAD origin/main 2>/dev/null)" || {
|
||||
_die "Could not compute merge-base between HEAD and origin/main."
|
||||
}
|
||||
|
||||
printf '\n'
|
||||
printf ' HEAD : %s\n' "$HEAD_SHA"
|
||||
printf ' branch : %s\n' "$BRANCH"
|
||||
printf ' origin/main : %s\n' "$ORIGIN_MAIN_SHA"
|
||||
printf ' merge-base : %s\n' "$MERGE_BASE"
|
||||
printf '\n'
|
||||
|
||||
# ── 3. dirty-tree check ────────────────────────────────────────────────────────
|
||||
|
||||
DIRTY="$(git status --porcelain)"
|
||||
if [ -n "$DIRTY" ]; then
|
||||
if [ "${CODEX_ALLOW_DIRTY:-0}" = "1" ]; then
|
||||
_warn "Dirty worktree detected (CODEX_ALLOW_DIRTY=1, continuing in debug mode)."
|
||||
else
|
||||
_fail "Dirty worktree detected. Uncommitted changes are present:"
|
||||
printf '%s\n' "$DIRTY" >&2
|
||||
_die "Set CODEX_ALLOW_DIRTY=1 only for explicit debug sessions. For a new task, start from a clean tree."
|
||||
fi
|
||||
else
|
||||
_ok "working tree is clean"
|
||||
fi
|
||||
|
||||
# ── 4. base guard: HEAD == origin/main (default) or ancestry check (resume) ───
|
||||
|
||||
if [ "$HEAD_SHA" = "$ORIGIN_MAIN_SHA" ]; then
|
||||
_ok "HEAD == origin/main (fresh base, new task allowed)"
|
||||
else
|
||||
if [ "${CODEX_ALLOW_NON_MAIN_BASE:-0}" = "1" ]; then
|
||||
# PR-resume mode: origin/main must be an ancestor of HEAD.
|
||||
if [ "$MERGE_BASE" = "$ORIGIN_MAIN_SHA" ]; then
|
||||
_ok "CODEX_ALLOW_NON_MAIN_BASE=1 — origin/main is ancestor of HEAD (PR-resume allowed)"
|
||||
else
|
||||
_fail "CODEX_ALLOW_NON_MAIN_BASE=1 is set, but origin/main is NOT an ancestor of HEAD."
|
||||
_fail " HEAD : $HEAD_SHA"
|
||||
_fail " origin/main : $ORIGIN_MAIN_SHA"
|
||||
_fail " merge-base : $MERGE_BASE"
|
||||
_die "Your branch has diverged from origin/main. Rebase onto origin/main before resuming."
|
||||
fi
|
||||
else
|
||||
_fail "HEAD is not origin/main."
|
||||
_fail " HEAD : $HEAD_SHA (branch: $BRANCH)"
|
||||
_fail " origin/main : $ORIGIN_MAIN_SHA"
|
||||
_fail ""
|
||||
_fail "For a new implementation task, the worktree must start from origin/main."
|
||||
_fail "If you are resuming a PR, set CODEX_ALLOW_NON_MAIN_BASE=1 and ensure"
|
||||
_fail "origin/main is an ancestor of your branch."
|
||||
_die "Stale base detected. This is the condition that caused #844 conflict PRs."
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 5. changed files vs origin/main ───────────────────────────────────────────
|
||||
|
||||
printf '\nChanged files vs origin/main:\n'
|
||||
DIFF_FILES="$(git diff --name-status origin/main HEAD 2>/dev/null)"
|
||||
if [ -z "$DIFF_FILES" ]; then
|
||||
printf ' (none — HEAD is origin/main)\n'
|
||||
else
|
||||
printf '%s\n' "$DIFF_FILES" | sed 's/^/ /'
|
||||
fi
|
||||
printf '\n'
|
||||
|
||||
# ── 6. uv availability ────────────────────────────────────────────────────────
|
||||
|
||||
if command -v uv >/dev/null 2>&1; then
|
||||
UV_VERSION="$(uv --version 2>/dev/null || echo 'unknown')"
|
||||
_ok "uv found: $UV_VERSION"
|
||||
else
|
||||
_die "uv is not installed or not in PATH. Install uv before proceeding: https://docs.astral.sh/uv/"
|
||||
fi
|
||||
|
||||
# ── 7. uv sync --frozen (only when uv.lock exists) ────────────────────────────
|
||||
|
||||
LOCKFILE="$REPO_ROOT/uv.lock"
|
||||
if [ -f "$LOCKFILE" ]; then
|
||||
printf 'Running uv sync --frozen …\n'
|
||||
if uv sync --frozen; then
|
||||
_ok "uv sync --frozen succeeded"
|
||||
else
|
||||
_die "uv sync --frozen failed. The lock file may be out of date or the environment is broken."
|
||||
fi
|
||||
else
|
||||
_warn "uv.lock not found — skipping uv sync (expected for projects without a lock file)"
|
||||
fi
|
||||
|
||||
# ── 8. final git status ────────────────────────────────────────────────────────
|
||||
|
||||
_sep
|
||||
printf 'Final git status:\n'
|
||||
git status --short --branch
|
||||
_sep
|
||||
|
||||
_ok "Startup guard passed — safe to begin task."
|
||||
printf '\n'
|
||||
Loading…
Reference in a new issue