Building an open-source CC-style harness directly on the raw Anthropic SDK (no Claude Code binary, just the Messages API) taught me that the harness quietly makes a pile of decisions on your behalf that you never notice until you implement them yourself. It also genuinely changed how I use Claude Code day to day. Here are the five decisions that surprised me most, worth knowing even if you never run anything but CC. Your CLAUDE.md doesn’t replace the system prompt. It layers on top of one. You can observe this behaviorally: instructions that seem to get “ignored” are often quietly fighting something already established underneath. In my own harness the mechanism is explicit, framework base first, your overlay appended under it. I can’t see exactly how CC composes yours in, but the behavior matches: your file doesn’t win by default. Once I made the layering visible in code, the ordering started to matter a lot. If your CLAUDE.md isn’t landing, try assuming there’s a base prompt above it and write to override rather than to state. Hooks are control flow, not notifications. This is the most underused lever I know of. CC exposes PreToolUse hooks that can actually block a tool call before it runs. Not just log it, block it. That turns “please don’t edit files in /prod” from a hope into an actual guardrail. In my own harness I run the hook chain sequentially and a single block short-circuits the rest, but that’s an implementation choice I made, not necessarily how CC handles the chaining internally. The usable takeaway either way: hooks as a kill switch, not a logger. The second you fan out subagents, you need an abort tree. Naive version: parent spawns children, you await them. Then one child wedges and your whole session hangs. What you actually want is a tree of abort signals, where the parent aborting cascades down to every child, but a child dying notifies up without auto-killing the parent. I got this wrong twice before I got it right. If you orchestrate subagents in CC, the practical implication is to structure independent tasks so they don’t block each other. If one agent’s work isn’t a prerequisite for another’s, don’t await them in sequence. One stuck task shouldn’t be able to hold the whole session hostage. Parallel subagents want a DAG, not a for-loop. If your tasks have dependencies (research, then implement, then verify), the clean structure is a dependency graph executed layer by layer: everything with no unmet deps runs in parallel, the next layer waits, and one failed node fails its dependents fast instead of letting the rest grind on. A flat “loop and await all” both leaves parallelism on the table and hangs on a single bad node. “Done” has to be a defined state or the agent rambles. The single biggest behavior win was forcing every turn to end in one explicit terminal state (done, blocked, or needs-input) with evidence, instead of trailing off into “I could also…”. Pair that with gating irreversible actions (deletes, pushes, anything external) behind explicit recent intent, and the agent stops doing surprising things while you’re away from the keyboard. What I’m not claiming: I haven’t reverse-engineered CC’s source. These are decisions my harness makes explicit, not a window into the binary. No SWE-bench numbers I’d defend yet. And bypass-by-default for unattended runs is a real sharp edge, not a feature. If you want to poke at a harness where all of this is editable code instead of a closed binary, link’s in the comments. Mostly I’m curious what other people have hit, especially on the abort/hang stuff, because that took me three rewrites and I’m still not sure I love my design. submitted by /u/Slowstonks40
Originally posted by u/Slowstonks40 on r/ClaudeCode
