PRD 0015: pipelock block remediation #21

Merged
didericis merged 5 commits from prd-0015-pipelock-block into main 2026-05-25 05:15:16 -04:00
Owner

Summary

Adds PRD 0015, the second remediation engine in the stuck-agent recovery flow (overview in PRD 0012, foundation in PRD 0013). Wires the pipelock block path with restart-based reload: the supervisor writes the new allowlist on operator approval and restarts pipelock, plus a proactive pipelock edit <bottle> TUI verb for operator-initiated edits unrelated to a tool call.

v1 uses restart, not SIGHUP — agent's in-flight outbound calls may drop and rely on retry. Hot reload for pipelock is deferred to a follow-up; tradeoff captured in PRD's Open questions.

Depends on PRD 0013 landing first.

## Summary Adds PRD 0015, the second remediation engine in the stuck-agent recovery flow (overview in PRD 0012, foundation in PRD 0013). Wires the pipelock block path with restart-based reload: the supervisor writes the new allowlist on operator approval and restarts pipelock, plus a proactive `pipelock edit <bottle>` TUI verb for operator-initiated edits unrelated to a tool call. v1 uses restart, not SIGHUP — agent's in-flight outbound calls may drop and rely on retry. Hot reload for pipelock is deferred to a follow-up; tradeoff captured in PRD's Open questions. Depends on PRD 0013 landing first.
didericis added 1 commit 2026-05-25 04:54:29 -04:00
docs(prd-0015): pipelock block remediation
test / unit (pull_request) Successful in 17s
test / integration (pull_request) Successful in 1m12s
0197599e49
Adds PRD 0015, the second remediation engine in the stuck-agent
recovery flow (overview in PRD 0012, foundation in PRD 0013). Wires
the pipelock block path with restart-based reload: supervisor writes
the new allowlist on approval and restarts pipelock, proactive
pipelock edit TUI verb, pipelock audit log filled in. SIGHUP reload
for pipelock is deferred to a follow-up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis force-pushed prd-0015-pipelock-block from 92497fc456 to 0197599e49 2026-05-25 04:54:29 -04:00 Compare
didericis added 4 commits 2026-05-25 05:07:28 -04:00
Phase 1 of PRD 0015. New module
claude_bottle/backend/docker/pipelock_apply.py:

- fetch_current_yaml(slug): docker exec cat of the live
  /etc/pipelock.yaml.
- fetch_current_allowlist(slug): parses the yaml, extracts
  api_allowlist, renders as one-per-line for the operator/agent.
- parse_allowlist_content / render_allowlist_content: one-per-line
  with `#` comments + blank-line tolerance, conservative hostname
  validation.
- apply_allowlist_change(slug, new): parses new hosts, fetches +
  parses current yaml, swaps api_allowlist, re-renders via
  pipelock_render_yaml, docker cp into sidecar, docker restart.
  Returns (before, after) as one-per-line strings for the audit diff.
- PipelockApplyError: caller surfaces to operator without crashing
  the dashboard.

v1 uses restart, not SIGHUP — pipelock has no in-process reload
hook; adding one is the PRD's open question. Restart drops in-flight
outbound calls and the agent retries pick up the restarted proxy.

Yaml roundtrip is covered by tests: parse(render(cfg)) preserves
all fields pipelock_render_yaml emits, including tls_interception
+ passthrough_domains.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 2 of PRD 0015. dashboard.approve() now dispatches on the
proposal's tool:

  cred-proxy-block → apply_routes_change   (from PRD 0014)
  pipelock-block   → apply_allowlist_change (new in PRD 0015)
  capability-block → no-op (lands in PRD 0016)

PipelockApplyError joins CredProxyApplyError under the ApplyError
tuple the TUI catches: failures keep the proposal pending and the
status line surfaces the message; no response is written and no
audit entry is appended.

Tests: existing TestApproveReject stubs both apply paths; new
TestPipelockApplyWiring covers the call wiring, failure-propagation,
and real-diff-in-audit invariants.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase 3 of PRD 0015. Adds the proactive `pipelock edit` path,
mirroring routes edit from PRD 0014:

- discover_pipelock_slugs() lists running pipelock sidecars.
- operator_edit_allowlist(slug, new) wraps apply_allowlist_change
  and writes an audit entry tagged ACTION_OPERATOR_EDIT.
- New 'p' keybinding in the main TUI: discover slugs, prompt if
  multiple, fetch current allowlist, open in $EDITOR, apply on
  save.
- Extracts shared scaffolding into _operator_edit_flow used by
  both routes-edit and pipelock-edit — DRY without sacrificing
  the per-verb status-line copy.
- Footer updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
test(pipelock): integration test for apply_allowlist_change (PRD 0015)
test / unit (pull_request) Successful in 16s
test / integration (pull_request) Successful in 1m8s
4fada1651b
Phase 4 of PRD 0015. End-to-end test against real Docker:

- Brings up a real pipelock sidecar via the production
  DockerPipelockProxy bring-up + pipelock_tls_init.
- Calls apply_allowlist_change to add a new host.
- Polls the live /etc/pipelock.yaml until the new host shows up
  (bridging the docker-restart window).
- Verifies api_allowlist contains both old + new hosts and
  tls_interception block is preserved.
- Smaller cases: invalid hostname raises, missing sidecar raises,
  fetch_current_allowlist returns one-per-line format.

Skipped under GITEA_ACTIONS because pipelock_tls_init bind-mounts a
host path that doesn't share fs in the runner, matching the
existing pipelock smoke test's skip pattern.

Drive-by fix: fetch_current_yaml now uses `docker cp` (daemon-API
tarball copy) instead of `docker exec cat` because the pipelock
image is distroless and has no shell utilities.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
didericis merged commit b5d6320320 into main 2026-05-25 05:15:16 -04:00
Sign in to join this conversation.