↑ ↓ to move · Enter to open · Esc to closeTap a result

Architecture
agent · class installer · opus

compose-service-installer

Adds to docker-compose.yml every service the spec marks materialize, by running docker-architect.

agent · installeropuseffort medium7 allowed paths
§ the agent's definition

compose-service-installer.md, section by section

The sections of the agent's own file, excerpted. Hover or click one to see which project rules and templates it cites. The sections are also chips: click one to jump to it.

nerviz/.claude/agents/compose-service-installer.md

## Why this is an agent (Form 3)

Preserves context, and keeps a model pin from leaking. /new-feature-implement must materialize the service its spec decided at design time, and its own thread cannot do it: ArchHook.java guard refuses a build-class Skill call while its orchestrator phase is open, and skips the call when a classed agent makes it (decision 0126). Running docker-architect here also keeps the skill's body out of the implement thread, and keeps its opus pin out of the turns that follow (decision 0080). The closest rejected form was giving the compose files to java-spring-boot-developer, which would have carried the skill's body into an executor group and widened the executor's territory to infrastructure (@.claude/decisions/0137-pending-compose-service-decided-at-design-materialized-at-implement.md).

## Procedure

  1. Check the input. Read the spec's ## Infrastructure. No materialize row → return
  1. Drop what already exists. List the keys of the compose file's services: block with the
  1. Invoke docker-architect through the Skill tool, with the use case folder as the
  1. Check the result. Run java .claude/hooks/ArchHook.java compose and keep the lines it

---

## Failure mode

No base pair: `` ❌ compose-service-installer stopped — docker-compose.yml (or Dockerfile) missing The base pair is project-bootstrap's. Nothing added; every materialize row stays pending. ``

docker-architect stopped, or compose reports a service red: `` ⚠️ compose-service-installer — <service>: <first line of the failure> Run /docker-architect docs/use-cases/UC-NNN-<slug>/ to finish it. ``

No rollback. The implement run reports each row as it left it, and still prints the /git-publish line: the code is green whether or not the container exists.

---

## Summary format returned to the caller

✅ compose-service-installer complete — UC-NNN-<slug>
- kafka (apache/kafka:<tag>) · added · compose: up, healthy
- postgres (postgres:<tag>) · already present
- Files: docker-compose.yml, .env.example

One line per materialize row, each with one of added, already present or failed: <reason>, and the files written. On failure, the same shape with ❌ or ⚠️ and the one line that matters.

---

## References

  • @.claude/decisions/0137-pending-compose-service-decided-at-design-materialized-at-implement.md
  • @.claude/decisions/0126-guard-call-judges-classed-agent-skill-calls-by-agent-class.md —
  • @.claude/skills/docker-architect/SKILL.md — the procedure this agent runs, and its single
  • @.claude/agents/commons-logging-installer.md — the other installer
Open the full file on GitHub →
§ behavior

What it reads, writes and refuses

The skill's own contract, from SKILL.md, in the original wording.

Class

installer — the territory is agent_classes.installer.overrides.compose-service-installer in @.claude/schemas/extensions.json: the same paths skill_classes.build gives docker-architect (the compose files, docker/**, the Dockerfile, .env, .env.example, .gitignore, .dockerignore). ArchHook.java guard reads this agent's agent_type on every write, including the writes docker-architect makes while it runs here, and refuses a path outside the list with exit 2.

Executor

yes — writes while /new-feature-implement's phase is open. The class grants it (executor: true), and ArchHook.java schema cross-checks this marker against that flag in both directions.

Pattern catalog

not injected — writes no Java (agent_classes.installer.pattern_catalog: false; schema cross-checks this line).

Input (required)

the path of the use case folder, docs/use-cases/UC-NNN-<slug>/, and the materialize rows of its spec's ## Infrastructure, both sent by the caller.

Reads

UC-NNN-spec.md § Infrastructure, docker-compose.yml, and what docker-architect reads for that folder.

Writes

nothing of its own. Every write is docker-architect's, made inside this agent and judged by this agent's territory.

Invokes

docker-architect through the Skill tool, once, with the use case folder. It never writes a service block itself: that skill is the single owner of every service block (decision 0029).

Does not

create docker-compose.yml or the Dockerfile when they are missing — that base pair is project-bootstrap's, written once at generation. Does not touch a row whose decision is exists, external or pending, edit the spec, run git, or ask anything: a background agent has no AskUserQuestion (decision 0123), and docker-architect asks nothing when it is given a use case folder.

Returns

one structured summary (shape below), not the skill's output.

Source: the agent's § Contract, at nerviz e4cae27.

§ guard and hook

What the guard does with it

installer is the agent's class. ArchHook reads its agent_type on every write and judges the path against the list on the left; the skill it invokes gets no territory of its own. The text on the right is the hook's own.

write_allow of class compose-service-installer

  • ✓docker-compose*.yml
  • ✓docker/**
  • ✓Dockerfile
  • ✓.env
  • ✓.env.example
  • ✓.gitignore
  • ✓.dockerignore
  • any path outside this list: refused, exit 2

A refusal exits with code 2 and the message goes back to the model.

PreToolUse  Agent(compose-service-installer)
  audit call                       exit 0
  guard call                       exit 0
SubagentStart
  context subagent                 exit 0
  tests agent-start                exit 0
# installer · opus · write_allow: docker-compose*.yml, docker/**, Dockerfile, .env, .env.example, .gitignore, .dockerignore
§ orchestration

Where it sits on the rail

Each card is a step of /new-feature-implement. The executor runs three times, one block group each; between two groups the orchestrator carries the reports forward. Dashed steps run only when the spec needs them.

← back to the chains