CWS-94: Harden PR Scripts for Stack, Sandbox, and Agent Workflows — Design Spec

Status: Design approved, pending implementation Parent: CWS-81 Date: 2026-03-22 Linear: CWS-94


Problem Statement

The PR workflow scripts (create-pr.sh, finalize-merge.sh) and the rollout governance CI check were built for a single-branch, phase-based, gh-always-works model. CWS-93 removed phase rollout support from the validator and active-plan.yaml, but left the scripts and CI workflow referencing the removed phase_branch_pattern. Both make create-pr and make finalize-merge are broken TODAY — the scripts require phase_branch_pattern from active-plan.yaml but it was already removed. Additionally:

Design Decisions

D1: Exempt patterns for governance validator

Add exempt_branch_patterns list to active-plan.yaml. The validator skips branch-name validation for branches matching any exempt pattern but still validates plan structure.

exempt_branch_patterns:
  - '^dependabot/'
  - '^renovate/'
  - '^gh-pages$'

D2: Remove all phase logic from scripts

Remove phase_branch_pattern references from create-pr.sh and finalize-merge.sh. Remove phase-specific gating (prior-phase-merged checks). Simplify both scripts to task-branch-only mode.

D3: gh CLI with curl fallback

Note: gh and gt are in the Claude Code sandbox allowlist, so gh should work in most agent sessions. The curl fallback exists for CI environments where gh is not installed, or environments where gh auth is not configured.

Both scripts attempt gh first. If gh fails (auth check or command execution), fall back to curl + GitHub REST API. Token resolution order: (1) GITHUB_TOKEN env var, (2) gh auth token output, (3) fail with clear error. Extract the fallback logic into scripts/lib/github-api.sh as shared functions:

D4: Non-interactive mode

finalize-merge.sh accepts --yes or --no-interactive flag. When set, skip the read -r confirm prompt and proceed directly. The flag is also honored by make finalize-merge PR=... YES=1.

D5: Stack merge support

finalize-merge.sh detects Graphite stacks via gt log --stack. When a stack is detected and --stack flag is passed:

Single-PR merge remains the default (no --stack flag).

D6: Per-branch PR body generation (template-driven)

Replace the hardcoded “Standardize the change…” body with dynamic content that respects .github/pull_request_template.md for ALL PRs — single or stacked. The template defines the canonical PR body structure; create-pr.sh must populate its sections programmatically rather than bypassing it.

Generated body sections (mapped to template):

For stacked PRs (gt submit --stack), the same template sections apply to each PR in the stack. Graphite’s --no-interactive mode replaces the template body with whatever create-pr.sh generates, so the script must produce template-compliant output.

D7: Agent-context staleness mechanism

Post-merge refresh (in finalize-merge.sh): After successful merge, update the “Stale After” timestamp in docs/agent-context.md to now + 24 hours. This is a mechanical timestamp bump, not a full content refresh. Commit the update as part of the merge flow if on main.

Session-start staleness warning (Claude Code hook): Add a SessionStart hook that reads the “Stale After” timestamp from docs/agent-context.md and emits a warning if current time exceeds it. The hook does NOT auto-refresh — it warns the agent to perform a Linear sync before executing.

Implementation: shell script at .claude/hooks/check-agent-context-staleness.sh that parses the timestamp and outputs a warning message. Registered in .claude/settings.json under hooks.SessionStart with matcher startup|resume|compact. Must fail-closed (exit 2 on error, never fail-open).

D8: Opportunistic codex → agent-agnostic renaming

As we modify files in this task, rename Codex-specific nomenclature:

D9: repo-flow skill as authoritative workflow reference

Update .agents/skills/repo-flow/SKILL.md to:

D10: Fix run-workflow-checks.sh backtick interpolation

Replace backtick git command interpolation with Open3.capture2 or IO.popen in the inline Ruby within run-workflow-checks.sh (the backtick-interpolated git commands in the governance change detection block). This prevents semgrep from flagging the file on future edits.

D11: Bump GitHub Actions to latest versions

Bump actions/checkout from @v4 to @v6 (latest as of March 2026, see https://github.com/actions/checkout/releases/tag/v6.0.2) across all workflow files. This resolves the Node.js 20 deprecation warning on PR #46.

Scope Exclusions

File Map

PR 1 — Phase removal + governance fix + rename (urgent, unblocks PR #46)

PR 2 — Fallback, non-interactive, stack merge, dynamic body

PR 3 — Staleness mechanism + repo-flow skill update

Validation

Risks