Skip to content

feat(loops): external scheduler + scheduler-side kill switch (phy, 8cj) - #22

Merged
adithya0597 merged 3 commits into
feat/loop-engineering-v3from
feat/loop-scheduler
Jul 21, 2026
Merged

feat(loops): external scheduler + scheduler-side kill switch (phy, 8cj)#22
adithya0597 merged 3 commits into
feat/loop-engineering-v3from
feat/loop-scheduler

Conversation

@adithya0597

Copy link
Copy Markdown
Owner

Implements the first two loop-operationalization beads (epic `builder-guild-tic`) via `/buildloop` run `bl-20260720-sched`. Turns the daily-triage loop from configured-but-dormant into scheduled — with enforcement in the platform layer, not the agent's prompt.

What lands

  • phy — external scheduler: .github/workflows/loop-triage.ymlon: schedule cron (09:17 UTC) + workflow_dispatch; caps in-file (timeout-minutes: 15 + --max-turns 25); concurrency group forbidding overlap.
  • 8cj — scheduler-side kill switch: job-level if: vars.LOOP_PAUSE_ALL != 'true' (GitHub evaluates it before any agent code runs) + gh workflow disable as the unconditional hard-off; three-layer kill-switch documented consistently in LOOP.md / safety.md / budget.md.

Security architecture (two separated jobs — the safe-outputs pattern)

Three review rounds (2 codex passes + 1 confirm + a RED-TEAM lens) found and closed a real guard-bypass class. Final design:

  • triage job: contents: read, persist-credentials: false, runs the agent with no push token; emits only STATE.md + run-log.md as an artifact.
  • commit job: needs: triage, contents: write, a fresh checkout the agent never touched, transplants exactly those two paths, guards (working tree may differ only on the two files), commits + pushes.

This structurally kills "untrusted agent contaminates a later privileged step" ($GITHUB_ENV poisoning, planted git hooks, push-before-guard, diff HEAD missing agent commits). All 4 actions SHA-pinned; ubuntu-24.04; input-discipline prompt (public text and prior STATE.md prose treated as untrusted data).

Report-only, gated, honest

  • Deterministic guard restricts the commit surface to the two state files (verified: passes M/A on those two, fails deletion/rename/symlink/out-of-surface).
  • Merge-gated operational proofs (a real scheduled run; kill-switch skip proof; secret setup) are tracked separately in bead `builder-guild-006` — they can only be proven once this is on the default branch.

Accepted residual (founder decision — bead `builder-guild-bgo`)

API-key egress from the agent job: bounded to credit-burn (rotatable; the write token never meets untrusted code), so not auto-hardened at L1. Options (harden-runner egress-allowlist / org spend cap) filed for your call.

🤖 Generated with Claude Code

adithya0597 and others added 2 commits July 20, 2026 16:29
…y-triage (phy, 8cj)

Workflow .github/workflows/loop-triage.yml: daily cron 09:17 UTC + workflow_dispatch,
caps in-file (timeout-minutes 15 + --max-turns 25), deterministic report-only guard
step enforcing the STATE.md/run-log.md surface, minimal contents:write permissions.
Three-layer kill switch documented in LOOP.md, budget.md, safety.md: LOOP_PAUSE_ALL
variable (scheduler pause) / gh workflow disable (hard-off) / STATE.md flag (in-band).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d-team)

Security review (2 codex passes + 1 confirm + RED-TEAM lens) drove a re-architecture:
- Two separated jobs (gh-aw safe-outputs pattern): agent runs contents:read + no push
  token, emits only STATE.md/run-log.md as an artifact; a fresh-checkout commit job the
  agent never touched holds the write token and transplants exactly those two paths.
  Kills the whole 'untrusted agent contaminates a later privileged step' class
  ($GITHUB_ENV poisoning, planted git hooks, push-before-guard, diff-HEAD-misses-commit).
- persist-credentials:false; all 4 actions SHA-pinned; ubuntu-24.04; input-discipline
  prompt (public text AND prior STATE.md prose = untrusted data); doc precision on
  max-turns!=spend and LOOP_PAUSE_ALL fail-open semantics.
Accepted residual (bead builder-guild-bgo): API-key egress (bounded: credit-burn only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
adithya0597 added a commit that referenced this pull request Jul 20, 2026
…ts, loops/ restructure, PR #22 scheduler+kill-switch

Append session block on top; refresh Next Steps 0 (merge #22 -> bead 006 ops
proof -> bgo egress decision -> ye0 settings-fix), Key Files (scheduler on
#22 branch + local audits), Tracker Delta (opened 13 / closed 3 / 23 open of
145), Status + Branch graph verified live.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, $0 marginal)

The scheduled loop-triage job needs a stored credential to run Claude in
headless CI (no interactive subscription session on a runner). Wire the
claude-code-action to claude_code_oauth_token (from `claude setup-token`)
instead of the metered anthropic_api_key: reuses the Claude Code subscription
at $0 marginal, so there is no per-token billing to exfiltrate/cap (bgo moot).

Founder still sets the secret; this only changes which input the workflow reads.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Founder highlights — this PR touches t2 (highest-risk) surface.

Files that triggered t2:

  • .github/workflows/loop-triage.yml
  • loops/safety.md

adithya0597 added a commit that referenced this pull request Jul 21, 2026
…ublish gate, guard, env)

Cherry-set of the pure bug-fix layer from feat/loop-engineering-v3, split out
per adversarial review so fixes are not bundled with loop automation infra:

- tools/publish_gate.sh: NUL-delimited staged-file loop — the previous
  `for f in $FILES` word-split let a secret in a spaced filename exit CLEAN
  (P1; functionally re-proven: spaced-filename ghp_ token now BLOCKS exit 1)
- 03-evals/src/eval_{corrective,ocr,planner}.py: diagnostics interpolate the
  resolved Neo4j URI instead of hardcoded bolt://localhost:7688
- 03-evals/{golden_v1_review.md,src/{golden_v1_draft,test_g3,h3_instr}.py}:
  stale claims corrected (6 roles -> 3 roles; docstring drift)
- tools/run_guard.py: self_test docstring matches ALLOWLIST-exemption behavior
  (GUARD_SELFTEST_OK)
- 01-context/setup_a2.sh: pin neo4j-graphrag
- .env.example: correct Neo4j/paperclip facts; add LANGFUSE_HOST/BG_EMBED_MODEL
- .gitignore: ignore .claude/lessons.md (local working notes)

Loop scheduler/heartbeat/rulesets stay on the feature branch (PR #21/#22).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
adithya0597 added a commit that referenced this pull request Jul 21, 2026
…ublish gate, guard, env) (#23)

Cherry-set of the pure bug-fix layer from feat/loop-engineering-v3, split out
per adversarial review so fixes are not bundled with loop automation infra:

- tools/publish_gate.sh: NUL-delimited staged-file loop — the previous
  `for f in $FILES` word-split let a secret in a spaced filename exit CLEAN
  (P1; functionally re-proven: spaced-filename ghp_ token now BLOCKS exit 1)
- 03-evals/src/eval_{corrective,ocr,planner}.py: diagnostics interpolate the
  resolved Neo4j URI instead of hardcoded bolt://localhost:7688
- 03-evals/{golden_v1_review.md,src/{golden_v1_draft,test_g3,h3_instr}.py}:
  stale claims corrected (6 roles -> 3 roles; docstring drift)
- tools/run_guard.py: self_test docstring matches ALLOWLIST-exemption behavior
  (GUARD_SELFTEST_OK)
- 01-context/setup_a2.sh: pin neo4j-graphrag
- .env.example: correct Neo4j/paperclip facts; add LANGFUSE_HOST/BG_EMBED_MODEL
- .gitignore: ignore .claude/lessons.md (local working notes)

Loop scheduler/heartbeat/rulesets stay on the feature branch (PR #21/#22).

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@adithya0597
adithya0597 merged commit 130a2e1 into feat/loop-engineering-v3 Jul 21, 2026
8 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant