Skip to content

Restructure the agentic pipeline into task skills and orchestrators - #842

Merged
Jack-Edwards merged 11 commits into
Crypter-File-Transfer:stablefrom
Jack-Edwards:chore/plan-outside-container
Aug 5, 2026
Merged

Restructure the agentic pipeline into task skills and orchestrators#842
Jack-Edwards merged 11 commits into
Crypter-File-Transfer:stablefrom
Jack-Edwards:chore/plan-outside-container

Conversation

@Jack-Edwards

@Jack-Edwards Jack-Edwards commented Aug 5, 2026

Copy link
Copy Markdown
Member

A change goes through orchestrators composing a set of task skills. The three skills without a prefix are the ones a user invokes; crypter-step- runs in your session on an orchestrator's behalf, and crypter-devcontainer- runs inside the pipeline container.

Orchestrator Does
/crypter-change "<requirement>" Carries a requirement to a green draft pull request
/crypter-review {pr-number} Puts an existing pull request through the reviewer lenses and posts what survives triage
/crypter-triage-review {pr-number} Rules on the findings left on a pull request and fixes the ones that hold
Task skill Executes in Does
/crypter-step-plan Your session Drafts the plan interactively
/crypter-devcontainer-implement Container Builds the plan into commits on a new branch
/crypter-devcontainer-examine Container Reviews a diff for plan adherence and code quality
/crypter-devcontainer-verify Container Rules on each finding in a report against the code
/crypter-devcontainer-remediate Container Applies triaged findings or a CI failure to an existing branch
/crypter-step-open-pull-request Your session Pushes the branch and opens or updates the draft pull request

Every skill that reads or writes code runs in the container, including both review phases and verification. The session that invokes an orchestrator plans, triages, and talks to GitHub.

The container holds no GitHub credential. Its workspace is an anonymous clone of the org repository with one remote, upstream, whose push url is cleared, so the agents read public code and commit locally. /crypter-change owns the CI loop and runs at most three fix attempts. The pull request it opens stays a draft.

Two mounts carry everything across the boundary: .claude/plans read-only for the plan, and .claude/runs writable so each reviewing agent writes its own findings where you can read them. The branch itself travels over git's ext transport on docker exec.

Notes for review:

  • /crypter-review posts one review with event COMMENT, after triaging the lenses against the code and recording what it kept and dropped in triage.md. It never approves and never requests changes.
  • /crypter-triage-review reads the findings already on a pull request, whoever left them, and gives each one its own finding-verifier with the code and nothing else to judge it by. A finding that does not hold gets one reply on its thread. A finding that holds becomes a commit on the pull request's own branch, which needs a head repository the session can push to. It watches no CI.
  • .claude/settings.json is new to the repository. It registers a PreToolUse hook that refuses a file operation on a path inside the project resolving outside it, which is how a symlink written into .claude/runs would reach the rest of your machine. Claude Code asks each contributor to approve the hook the first time they see it.
  • The container agents run claude --permission-mode auto.
  • CRYPTER_FORK and CRYPTER_FORK_TOKEN are gone from .env; only the two git identity variables remain. Revoke the token once this is merged.
  • .devcontainer/clone-fork.sh is renamed to clone-upstream.sh, and the image drops the GitHub CLI.
  • .claude/agents/plan-author.md is deleted; its guidance is now /crypter-step-plan. ci-watcher runs in your session and reads checks through the GitHub MCP server, or gh where it exists.
  • The reviewer lens table grows from one row to correctness, maintainability, testability and security. The agent definition is unchanged; lenses come from the prompt.
  • The orchestrators create directories under .claude/runs with mode 777. The container's agent is uid 1001 because the base image already has a user on uid 1000, and a bind mount keeps host ownership. Directories created host-side stay deletable host-side.
  • Create .claude/plans and .claude/runs before the first up. They are gitignored, and Docker creates a missing bind-mount source as root.
  • The image changes, so this needs a rebuild, and merging to stable queues the approval-gated publish workflow. Recreate any existing container afterwards.
  • An existing container's workspace keeps its origin remote pointing at the fork. Without a credential those pushes fail, and a fresh workspace has only upstream.

n and others added 3 commits August 4, 2026 14:37
The plan now comes from /crypter-plan-author, a skill an interactive
session runs on the host, where the web, the user's tooling and the user
are all reachable. Compose mounts .claude/plans read-only at /plans, so
the container reads the plan where it was written and keeps its own run
state in the workspace.

/pipeline takes a run id and a branch and starts at the sync, with the
host session driving it once the plan is approved.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The container ran with a fork-scoped PAT and unrestricted egress, since
Claude Code needs the Anthropic API. Rather than harden egress, the
credential is gone: the workspace is now an anonymous clone of the org
repository whose only remote has no push url, so the agents read public
code and commit locally.

Pushing, opening the pull request and the CI loop move to the host, where
the session already has GitHub access. /crypter-publish fetches the branch
out of the container over git's ext transport, pushes it, opens the draft
pull request, and runs at most three fix attempts, handing each failure to
/pipeline-fix through the read-only plans mount.

CRYPTER_FORK and CRYPTER_FORK_TOKEN leave .env, and the image drops the
GitHub CLI, which has nothing left to authenticate with.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One skill drafted a plan and launched a container, and another implemented,
reviewed, triaged and remediated. Neither name described what it did, and
neither part could be used alone.

The work now sits in small skills that compose: crypter-plan, crypter-implement,
crypter-examine, crypter-remediate and crypter-publish. crypter-change carries a
requirement through all of them and owns the CI loop, where the plan, the
findings and every previous attempt are already in context. crypter-review runs
the same lenses against a pull request that already exists, including one raised
by someone else, and reports without posting anything.

Findings become artifacts on the host. A second mount carries .claude/runs into
the container, and each reviewing agent writes its own file there, so what was
found and what was rejected can be read without docker exec. The orchestrators
create those directories because the container's agent is uid 1001 and host
files are uid 1000.

The reviewer's lens table grows from one row to correctness, maintainability,
testability and security.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Jack-Edwards Jack-Edwards changed the title Move planning and publishing out of the pipeline container Restructure the agentic pipeline into task skills and orchestrators Aug 5, 2026
n and others added 8 commits August 4, 2026 22:57
crypter-examine claimed the branch it reviewed, so it failed whenever anything
else already held that ref. It only reads, and a detached worktree reads just as
well.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pipeline reviewed its own branch. Acting on what it raised:

crypter-publish becomes crypter-open-pull-request, since the skill only
ever opens a draft and taking a pull request out of draft is the user's
call.

ci-watcher loses the fork parameter it was never given and resolves the
repository from the path it already has, and reads the branch's version
of a file rather than whatever the working tree is checked out at.

crypter-review forces its fetch refspec, so a pull request that was
rebased or amended can be reviewed again.

The mount table named the run-id directory as the mount source; the
mounts are the parents. The pre-flight check tested the parent of /runs,
which uid 1001 cannot write to by design, so it now tests the run
directory the caller just created. The launch steps create both mount
sources, because Docker creates a missing bind-mount source as root.

The documentation credited a fork-scoped token with bounding the
container and claimed it holds no credential; the Claude Code credential
is still there, so it says GitHub credential and names the container as
a trust boundary. The orchestrators state that they run from the main
checkout, which is where the mounts resolve.
The agents run unattended, which is the whole reason permissions had to
be resolved without a prompt. Auto mode does that without handing them
the bypass, and the unprivileged user in the image stands on its own
merits rather than on what the bypass refuses to do as root.
.claude/runs is a writable mount into the pipeline container, and
/crypter-review runs agents over diffs written by people outside this
project. A symlink left in that directory resolves on the host side, so
reading a findings file can read any file the user can, and writing
triage.md can overwrite one.

The hook blocks a path inside the project that resolves outside it.
Paths that point outside to begin with are untouched, since asking for
one is deliberate. It is written in Node because the build already
depends on it, so it runs where a shell script would not.
A review that lands only on the reviewer's disk asks the reviewer to
retype it. crypter-review now triages what the lenses raised and posts a
single review that comments, with a line comment for each finding that
lands inside the diff and the rest in the body.

It never approves and never requests changes. The pull request may
belong to someone else, and either verdict is the user's to give.

Triage comes first, and it is written to triage.md, so the findings that
reach the author are the ones that survived a read against the code, and
so a later remediation run has something to start from.
A finding on a pull request is a claim, and acting on a wrong one means
changing working code. crypter-triage-review gives each finding its own
verifier with the code and nothing else to judge it by, so no agent ever
rules on a finding it raised.

What holds becomes a commit. What does not gets a reply on its thread
carrying the evidence, so the person who raised it can answer. What the
verifier cannot settle goes to the user untouched.

The commits go back to the pull request's own branch, which is only
possible when it comes from a repository the session can push to.
Otherwise the replies stand on their own and the author does the fixing.
The skills are visible wherever the repository is checked out, and four
of them only work inside the pipeline container, where /work/Crypter,
/plans and /runs exist. The prefix says which is which, so the skills a
user invokes are the ones without it.
The two skills a session runs on behalf of an orchestrator now say so in
their names. crypter-step- rather than crypter-change-step- because
open-pull-request is a step of crypter-triage-review as well.

What is left unprefixed is what a user invokes: crypter-change,
crypter-review, crypter-triage-review.
@Jack-Edwards
Jack-Edwards merged commit 139efed into Crypter-File-Transfer:stable Aug 5, 2026
13 checks passed
Jack-Edwards added a commit that referenced this pull request Aug 6, 2026
* Build and review in per-run container workspaces

The container kept a long-lived clone of the org repository in a named volume.
Nothing ever advanced its working tree, so the pipeline's own skills and agents
were frozen at whatever commit the volume was created with. The rename in #842
made that visible: the host began invoking crypter-devcontainer-implement while
the container still only knew crypter-implement, and both orchestrators broke.

Workspaces are now created per run at /work/{run-id} and deleted when the run
ends, cloned from a read-only mount of the host's .git. A copy of the code no
longer outlives the run that made it, so it cannot drift. The mount being
read-only is what makes sharing history safe: the container reads commits and
cannot move a ref or add an object. The host working tree is not mounted, so
uncommitted work stays invisible to the agents.

The orchestrator owns the lifecycle and the container skills assume a workspace
exists, mirroring how /runs already works. This also retires the worktree
add/remove pairing in every container skill, along with the stale worktree a
crashed run used to leave behind.

The image is built locally instead of pulled from GHCR. It carries tooling and
no source, so a change to workspace.sh or the Dockerfile is a rebuild rather
than a publish waiting on an approval.

NuGet and pnpm caches move to named volumes. Workspaces are ephemeral, so
without them every run would restore from nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Stop publishing the devcontainer image

The image is built locally and carries tooling rather than source, so nothing
consumes the published copy. Publishing it on every merge to stable left a
registry image nobody pulled and an approval gate on changes that only ever
affect the machine making them.

pr-build-devcontainer stays. It pushes nothing and catches a Dockerfile that
does not build, which is the only part of the publish path that was earning its
keep.

The devcontainer environment on the repository has no jobs left referencing it
and can be deleted from the repository settings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: n <e@f>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant