Skip to content

RFC: fully-automated weekly propagation wave (Claude Network Task) — design decisions needed #702

Description

@nathanjohnpayne

Goal

Run the propagation wave every week, fully automated, as a Claude Network Task — headless, scheduled, no human on the happy path — escalating to a human only for the ~10% of runs that hit a genuine judgment call. "Fully automated" here means: automate the happy path end-to-end, and STOP + ALERT (never force) on exceptions.

Hard constraint (requester, 2026-07-06)

The audit must run on the ambient usage credits of my Claude plan — NOT a provisioned CLAUDE_CODE_OAUTH_TOKEN. I don't want to generate/store/rotate a separate claude setup-token secret; the run environment should already be logged into my plan and the audit should bill against it directly.

Mechanical consequence: the phase-4b Claude adapter (review-via-claude.sh) accepts two first-party (plan-billed) auth modes — authMethod=claude.ai (interactive/ambient plan login, read from HOME, which the adapter passes through) and authMethod=oauth_token (CLAUDE_CODE_OAUTH_TOKEN). This constraint selects the claude.ai (ambient login) path and rules out the token path. Therefore the audit can only run where my Claude plan login is already present — a Claude/Network-Task environment — not a stock GitHub Actions runner (which has no ambient Claude login and would require the now-ruled-out token). This reshapes Decision 1 (host) below.

How a wave works today (manual)

A human drives every wave: cut the swipewatch canary (sync-to-downstream.sh --sync-all --repos swipewatch) → wait for the propagation lane to byte-verify the canary head → run scripts/wave-audit.sh (one scoped reviewer-CLI audit of the canonical range) → merge the canary + advance the wave-audit-pass/<sha> watermark → fan out the other 7 consumers with --coderabbit-ignore → approve (lane-exempt) → merge → confirm 8/8.

.github/workflows/weekly-drift-audit.yml (Mon 08:00 UTC) already detects drift (runs sync-to-downstream.sh --audit + audit-propagation-lane.sh) and maintains the propagation-drift tracking issue (#455), but nothing acts on it. A recent cloud dispatch of the audit half also failed outright because the headless container had no op (1Password biometric), no gh, and no plan-authed claude/codex — which surfaced the core obstacle directly.

The crux — resolved by investigation

The wave is mechanical orchestration except the audit, which shells out to a reviewer CLI that must be plan-authed (subscription, never API key). Findings:

  • 1Password biometric is not needed. The scripts authenticate purely via GH_TOKEN; the existing weekly workflows already run headless in Actions using REVIEWER_ASSIGNMENT_TOKEN (nathanpayne-claude PAT, cross-repo read+write) and AUTHOR_MERGE_TOKEN (nathanjohnpayne author PAT). sync-to-downstream.sh is already CI-ready with GH_TOKEN set — no Mac/biometric assumption. All the mechanical steps (cut, fan-out, approve, merge, escalate) need only these PATs.
  • The Claude audit runs on ambient plan credits — confirmed, and this is the requested path. review-via-claude.sh verifies apiProvider=firstParty and accepts authMethod=claude.ai (scripts/phase-4b/lib.sh:894), and it passes HOME through its scrubbed child env (review-via-claude.sh:188). So in an environment already logged into the Claude plan, claude auth status reports claude.ai, the adapter's plan-auth check passes, and claude -p bills the plan with no token to manage — exactly the requester's constraint. (The CLAUDE_CODE_OAUTH_TOKEN path exists too but is explicitly out of scope per the constraint above.) Codex remains headless-unfriendly (needs an interactive ~/.codex/auth.json, auth_mode=chatgpt) and is not the chosen reviewer.
  • One wrinkle — reviewer selection. Sync canaries are stamped Authoring-Agent: claude, and p4b_select_reviewer forbids same-agent review, so a claude-authored canary defaults to a codex reviewer. To use the Claude adapter, the wave audit must present a nominal non-claude author (e.g. phase-4b-review.sh --author codex --reviewer nathanpayne-claude, or a propagation_audit.reviewer config knob). Justification: the wave audit is an aggregate second-pass re-check of already-upstream-reviewed canonical content, so "claude re-reads a claude-stamped mirror" is a safety net, not a self-review of novel code. The relaxation is scoped to the wave audit only.
  • Watermark math favors making the audit actually run. The watermark advances ONLY on a posted APPROVED (or empty scope); a fail-open week (reviewer unavailable, wave-audit.sh exit 4/5) never advances it, so the audit range chains and grows unboundedly until a real audit clears it.

Decisions needed

1. Host model (reshaped by the plan-credits constraint)

Because the audit must run under the ambient Claude plan login (no token), a stock GitHub Actions runner cannot perform the audit itself — the plan login isn't there. That leaves:

  • (B) Claude scheduled cloud agent ("Network Task") — now the direct fit. The whole wave runs as a Claude agent under your plan; the audit uses ambient plan credits natively. Simplest match to the constraint and to "Network Task." Cost: the boring ~85 min of polling/merging also runs as an agent (burns plan reasoning tokens on mechanical work). Depends on what the Network Task mechanism guarantees (repo checkout, secret store for the PATs, ~30–90 min runtime, ambient claude plan login).
  • (C) Hybrid — recommended. A GitHub Actions cron (mirroring weekly-drift-audit.yml) does all the deterministic, PAT-only mechanics (drift gate → cut canary → poll lane → … → fan-out → approve → merge → escalate), and dispatches the Claude Network Task only for the audit step, which runs on ambient plan credits. Keeps the robust/observable GHA backbone, spends plan credits only on the ~5-min audit (not the 85 min of polling), and still honors the constraint. Cost: the GHA↔Network-Task handoff (trigger the audit, wait for the verdict/watermark, continue) is the extra wiring to build.
  • (A) Pure GitHub Actions cron — no longer viable as-is. A stock runner has no ambient Claude plan login, so it cannot run the audit without the ruled-out token. Only workable if the audit were dropped (fail-open, see Decision 2C) — not recommended.

Recommendation: C (GHA mechanics + Network-Task audit on plan credits); B if you'd rather one agent own the whole run and don't mind it spending plan credits on the mechanical parts.

2. Audit reviewer

  • (A) Claude adapter under the ambient plan login — recommended, matches the constraint. claude -p, authMethod=claude.ai, plan-billed, no token. Needs the scoped same-agent relaxation above and a run environment carrying your plan login (per Decision 1). Keeps the watermark advancing so the range stays bounded.
  • (B) Codex, provisioned. Preserves cross-agent independence, but requires pre-provisioning ~/.codex/auth.json (auth_mode=chatgpt) into a persistent CODEX_HOME — brittle, and orthogonal to the plan-credits constraint (it's a ChatGPT plan, not the Claude one).
  • (C) Fail-open on CI + lane. Skip the fresh audit; rely on byte-verification + consumer CI. Simplest, needs no plan auth at all, but the watermark never advances → un-audited range grows unboundedly, and we lose the aggregate fresh-eyes catch the wave-audit posture exists for.

Recommendation: A.

3. Escalation channel

  • (A) GitHub issue + label — recommended. Open/update a tracking issue with a label (needs-human-review / policy-violation), like weekly-drift-audit does for Cross-repo propagation drift detected #455. In-repo, auditable, dedup-friendly, no external dependency.
  • (B) Issue + out-of-band ping. The above plus the existing request-label-removal.sh iMessage ping (MERGEPATH_NOTIFY_IMESSAGE_TO) or another channel, so exceptions reach you same-day without watching the repo.

Recommendation: A, with B as a cheap add-on if you want same-day reaction.

Escalation model (what the automated run must STOP + ALERT on, never force)

  • Audit CHANGES_REQUESTED (exit 1) → source fix + re-cut needed → issue, no fan-out.
  • Audit fail-closed (exit 3) → infra/config → issue, hard stop.
  • Reviewer unavailable (exit 4/5) → alert + hold (fail-open grows the range).
  • Canary CI red → issue, no fan-out.
  • A fan-out consumer's CI red → skip that consumer (issue), others proceed.
  • A consumer carrying a protected label (human-hold / needs-human-review / policy-violation / needs-external-review) → skip its merge; these are human-remove-only.

Reusable vs. build

Already reusable (almost everything):

  • scripts/wave-audit.sh (fully built), scripts/sync-to-downstream.sh (--audit drift gate exit 0/1, --sync-all, --skip-existing, --coderabbit-ignore, --dry-run, --no-pr), watermark tags, lane byte-verification.
  • .github/workflows/weekly-drift-audit.yml — the schedule template + drift signal + issue-maintenance pattern.
  • scripts/phase-4b/* adapters + scripts/phase-4b-review.sh (already files step-9 escalation issues); scripts/gh-as-author.sh / gh-as-reviewer.sh — standalone, hook-independent, self-verifying (the gh-pr-guard PreToolUse hook is Claude-Code-only and does NOT fire in a runner, but the wrappers enforce identity themselves).
  • Secrets already provisioned + battle-tested weekly: REVIEWER_ASSIGNMENT_TOKEN, AUTHOR_MERGE_TOKEN.
  • Safety rails already in place: watermark chaining, lane-verification precondition, deterministic branch names, --skip-existing idempotency, requires-closure gate, .sync-overrides.yml filter, config-range validation, author-identity verification.

To build:

  • The scheduler/orchestrator per the host decision (GHA workflow + Network-Task audit dispatch for C; a scheduled agent for B).
  • A run environment for the audit that carries the ambient Claude plan login (the constraint) — no CLAUDE_CODE_OAUTH_TOKEN secret.
  • The reviewer-direction wiring (nominal non-claude author for the wave audit) — a small change in the wave-audit.sh / phase-4b-review.sh invocation, or a propagation_audit.reviewer config knob.
  • For hybrid (C): the GHA↔Network-Task handoff (dispatch the audit, wait for verdict/watermark, resume the mechanical fan-out).
  • The wave-specific escalation issue/label + dedup.
  • A drift gate so the run no-ops cleanly when --audit reports no drift.
  • A dry-run / verification path (--dry-run end-to-end on a schedule-dispatch before enabling the real cron).

Remaining open questions

  1. What the "Claude Network Task" mechanism actually provides — repo checkout, a secret store for the two PATs, max runtime, and (load-bearing for the constraint) an ambient Claude plan login so claude -p bills the plan without a token. This is now the key viability question for hosts B/C.
  2. Cadence/timing — same Mon 08:00 as the drift audit, or offset after it (so the wave only runs when the fresh audit reports drift)?
  3. Scope of "fully automated" for exceptions — should the agent form attempt source-fix-and-re-cut on a real audit finding, or always escalate that to a human (v1 recommendation: always escalate; the source fix is judgment-heavy)?

Design notes

Produced from three parallel codebase sweeps (scheduling surfaces, the phase-4b reviewer auth model, and the escalation/guard machinery); file:line references for the load-bearing findings are inline. This issue is the decision record — once (1)/(2)/(3) are chosen (and the plan-credits constraint is now fixed), the build is mostly wiring existing machinery into a scheduled entrypoint plus the audit-on-plan-credits handoff.

Metadata

Metadata

Assignees

No one assigned

    Labels

    automationWorkflow and process automationenhancementNew feature or request

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions