Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"name": "ccmagic",
"source": "./",
"description": "25 dev workflow skills: tracker-aware ticket lifecycle (Linear, GitHub, JIRA) with an autonomous end-to-end driver, adaptive code review, debugging, design/QA, and git workflow utilities",
"version": "3.20.2",
"version": "3.20.3",
"author": {
"name": "Devon Hillard"
},
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "ccmagic",
"description": "Dev workflow skills for Claude Code: tracker-aware ticket lifecycle (Linear, GitHub, JIRA), adaptive code review, debugging, design/QA, and git workflow utilities",
"version": "3.20.2",
"version": "3.20.3",
"author": { "name": "Devon Hillard", "url": "https://github.com/devondragon" },
"repository": "https://github.com/devondragon/ccmagic",
"license": "Apache-2.0"
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to ccmagic are documented here. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.20.3] - 2026-10

### Fixed

- The guard hook's force-push message now tells agents to run `git pull --rebase origin <branch>` instead of a bare `git pull --rebase`, which fails in a sandboxed session whose branch has no upstream (Cyrus worktrees are created with `--no-track`).

### Documentation

- `docs/harness.md` notes that, inside a sandboxed session, a harness Dockerfile's `FROM` images (including the `# syntax=` frontend image) can come only from registries whose token host is on the session's egress allowlist. On the Cyrus deployment that is Docker Hub's `auth.docker.io` today.

## [3.20.2] - 2026-10

### Fixed
Expand Down
2 changes: 2 additions & 0 deletions docs/harness.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ Rules (`ccm-harness check`): every value literal in the merged model, included a

`ccm-harness` runs Docker and Compose with an emptied environment: only `PATH`, `HOME`, `DOCKER_HOST`, `DOCKER_CONFIG`, `TMPDIR`, the proxy variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, `ALL_PROXY`, and their lowercase forms), and the CA bundle paths (`SSL_CERT_FILE`, `SSL_CERT_DIR`) pass through. The last two groups let image builds reach a registry from a sandboxed session whose network goes through a proxy; none of them hold secrets.

Registries: inside a sandboxed session (Cyrus), Buildx fetches registry pull tokens from the client side, through the session's egress proxy. A harness Dockerfile's `FROM` images, including the `# syntax=` frontend image, can therefore come only from registries whose token host is on the session's egress allowlist. On the Cyrus deployment only Docker Hub's `auth.docker.io` is allowed today. An image from ghcr.io, mcr.microsoft.com, quay.io, or another registry in a `FROM` line needs that registry's token host added to the allowlist (`sandbox.networkPolicy.allow` in Cyrus's `config.json`) first. Images the daemon pulls directly (the runner image and `image:` services) are not affected.

`ccm-harness` needs Docker Compose 2.30 or newer. Its `check` tests were run on Compose 2.30.0, 2.38.2, and 5.6.0; older versions are untested.

Commands: `check`, `up` (with `--prepare`, `--attach RUN`, `--wait [SECONDS]` for runs longer than one tool call), `seed`, `shoot TICKET SCRIPT`, `down`, `sweep [HOURS]`, `evidence TICKET HEAD PNG...`. Each prints one JSON object; the script's header has the details.
2 changes: 1 addition & 1 deletion hooks/pre-tool-use-guard.sh
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ if [ "$is_push" = true ]; then
[ "$force" = true ] || continue
[ -n "$target" ] || target=$(git branch --show-current 2>/dev/null || true)
if [ "$autonomous" = true ]; then
deny "ccmagic: autonomous runs never force-push (target: ${target:-unknown}). If the branch has diverged, rebase onto the remote (git pull --rebase) and push normally; if that conflicts, return needs-human."
deny "ccmagic: autonomous runs never force-push (target: ${target:-unknown}). If the branch has diverged, rebase onto the remote (git pull --rebase origin ${target:-<branch>}) and push normally; if that conflicts, return needs-human."
fi
default=$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null || true)
default=${default#origin/}
Expand Down
13 changes: 12 additions & 1 deletion tests/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,12 @@ guard_force_push_denied_autonomously() {
check "$(denied)" "deny"
}

guard_force_push_message_names_the_remote() {
git checkout -q -b feature/ENG-1-x
hook 'git push -fu origin feature/ENG-1-x' ccmagic:auto-push
check "$(printf '%s' "$OUT" | grep -c 'git pull --rebase origin feature/ENG-1-x')" "1"
}

guard_normal_push_allowed_autonomously() {
git checkout -q -b feature/ENG-1-x
hook 'git pull --rebase && git push -u origin feature/ENG-1-x' ccmagic:auto-push
Expand Down Expand Up @@ -3856,13 +3862,18 @@ skills_auto_ticket_follow_up_dedupe() {
# added lines in hooks/lib-handshake.sh), to check the visual step's handshake
# (the changed case arm in hooks/subagent-stop-handshake.sh, spec decision 4 of
# the UI harness design), and, for every ticket, by the RV-86 finish guard: its
# own hook file and one PreToolUse entry in hooks.json.
# own hook file and one PreToolUse entry in hooks.json. The guard's
# force-push message also changed (one line): it now says
# `git pull --rebase origin <branch>`, because branches in sandboxed sessions
# have no upstream (ccmagic 3.18.3 fixed the same assumption in ccm-pr-reply).
skills_hooks_only_accept_tasks_done() {
local changed
changed=$(git -C "$ROOT" diff "$SPEC_BASELINE" -- hooks ':(exclude)hooks/hooks.json' ':(exclude)hooks/pre-tool-use-finish-guard.sh' |
grep -E '^[-+]' | grep -vE '^(\+\+\+|---) ' || true)
check "$changed" "+# tasks_done: [ids] (optional: work step on a Reeve spec ticket only)
+ if (l ~ /^[[:space:]]*tasks_done:/) continue
- deny \"ccmagic: autonomous runs never force-push (target: \${target:-unknown}). If the branch has diverged, rebase onto the remote (git pull --rebase) and push normally; if that conflicts, return needs-human.\"
+ deny \"ccmagic: autonomous runs never force-push (target: \${target:-unknown}). If the branch has diverged, rebase onto the remote (git pull --rebase origin \${target:-<branch>}) and push normally; if that conflicts, return needs-human.\"
- ccmagic:auto-work|ccmagic:auto-feedback|ccmagic:auto-validate|ccmagic:auto-finish|ccmagic:auto-push) allowed=\"done needs-human\" ;;
+ ccmagic:auto-work|ccmagic:auto-feedback|ccmagic:auto-validate|ccmagic:auto-finish|ccmagic:auto-push|ccmagic:auto-visual) allowed=\"done needs-human\" ;;"
git -C "$ROOT" show "$SPEC_BASELINE:hooks/hooks.json" | jq -S . >"$T/hooks-base"
Expand Down
Loading