CB-617 Unit A: role charter via file, not argv #121

Closed
agent wants to merge 0 commits from worker/cb-617a-5c2f4a-1 into main
Member

Fixes CB-617 (gitea #120), Unit A: the launcher change. See #119 for the parent ticket.

The defect

A configured multi-line fleet.charters.<role> was composed into roleCharter + "\n\n" + REPLY_CHARTER and passed as one inline --append-system-prompt argv element on Claude Code profiles. herdr refuses to shell-encode a multi-line inline argument (invalid_agent_argument), so any claude-code profile with a multi-line role charter configured (e.g. architect on opus) failed to spawn.

The fix

  • Split delivery: the role charter now writes to a per-spawn temp file and mounts via --append-system-prompt-file <path> (confirmed by the ticket author that Claude Code accepts this flag with a multi-line file). The file is best-effort cleaned via deleteOnExit, the same disposable-worker-config pattern OpenCodeLauncher#writeConfig already uses for its own charter file.
  • REPLY_CHARTER keeps its inline --append-system-prompt delivery -- it is one line, demonstrably encodes, and must reach a member with no repo checkout to write a file into.
  • Both adapters now pass --agent <role> when a role agent-definition file exists under the worker's resolved cwd (.claude/agents/<role>.md for claude-code, .opencode/agent/<role>.md for opencode). Unit B (a separate PR) authors those files; absent either file, nothing extra is added and the member still spawns exactly as before.
  • --model/-m is untouched -- the profile stays authoritative for the model on both backends.
  • CharterReceipt is unchanged: HerdrPeerLauncher.LaunchSpec now also carries roleCharter/replyCharter/cwd alongside the existing composed charter field, and the receipt still fingerprints that same composed string. The digest still identifies what the member actually launched with, because the composed text is the same logical content whether delivered as one inline argument or split across a file + an inline flag.

Tests

  • ClaudeCodeLauncherTest.roleCharterTravelsByFileAndReplyCharterStaysInline drives the real spawn() entry point with a multi-line role charter and asserts no resulting argv element contains a newline, that --append-system-prompt-file points at a file holding exactly the role charter text, and that only the one-line reply charter rides inline via --append-system-prompt. This is the regression test for the reported herdr failure.
  • ClaudeCodeLauncherTest.agentFlagIsPassedWhenTheRoleAgentDefinitionFileExists / noAgentFlagWhenTheRoleAgentDefinitionFileIsAbsent, and the equivalent pair in OpenCodeLauncherTest, drive spawn() with a @TempDir cwd to cover both branches of the --agent lookup.
  • Two pre-existing tests were updated because they pinned the old, buggy inline-argv behavior (profileWithoutMcpStillGetsItsRoleCharter renamed to ...AsAFileNotInline; the role+reply composition test now checks the split delivery instead of one combined inline string).

mvn clean install from the repo root: BUILD SUCCESS, Tests run: 874, Failures: 0, Errors: 0, Skipped: 0.

Fixes CB-617 (gitea #120), Unit A: the launcher change. See #119 for the parent ticket. ## The defect A configured multi-line `fleet.charters.<role>` was composed into `roleCharter + "\n\n" + REPLY_CHARTER` and passed as one inline `--append-system-prompt` argv element on Claude Code profiles. herdr refuses to shell-encode a multi-line inline argument (`invalid_agent_argument`), so any claude-code profile with a multi-line role charter configured (e.g. `architect` on `opus`) failed to spawn. ## The fix - Split delivery: the role charter now writes to a per-spawn temp file and mounts via `--append-system-prompt-file <path>` (confirmed by the ticket author that Claude Code accepts this flag with a multi-line file). The file is best-effort cleaned via `deleteOnExit`, the same disposable-worker-config pattern `OpenCodeLauncher#writeConfig` already uses for its own charter file. - `REPLY_CHARTER` keeps its inline `--append-system-prompt` delivery -- it is one line, demonstrably encodes, and must reach a member with no repo checkout to write a file into. - Both adapters now pass `--agent <role>` when a role agent-definition file exists under the worker's resolved cwd (`.claude/agents/<role>.md` for claude-code, `.opencode/agent/<role>.md` for opencode). Unit B (a separate PR) authors those files; absent either file, nothing extra is added and the member still spawns exactly as before. - `--model`/`-m` is untouched -- the profile stays authoritative for the model on both backends. - `CharterReceipt` is unchanged: `HerdrPeerLauncher.LaunchSpec` now also carries `roleCharter`/`replyCharter`/`cwd` alongside the existing composed `charter` field, and the receipt still fingerprints that same composed string. The digest still identifies what the member actually launched with, because the composed text is the same logical content whether delivered as one inline argument or split across a file + an inline flag. ## Tests - `ClaudeCodeLauncherTest.roleCharterTravelsByFileAndReplyCharterStaysInline` drives the real `spawn()` entry point with a multi-line role charter and asserts no resulting argv element contains a newline, that `--append-system-prompt-file` points at a file holding exactly the role charter text, and that only the one-line reply charter rides inline via `--append-system-prompt`. This is the regression test for the reported herdr failure. - `ClaudeCodeLauncherTest.agentFlagIsPassedWhenTheRoleAgentDefinitionFileExists` / `noAgentFlagWhenTheRoleAgentDefinitionFileIsAbsent`, and the equivalent pair in `OpenCodeLauncherTest`, drive `spawn()` with a `@TempDir` cwd to cover both branches of the `--agent` lookup. - Two pre-existing tests were updated because they pinned the old, buggy inline-argv behavior (`profileWithoutMcpStillGetsItsRoleCharter` renamed to `...AsAFileNotInline`; the role+reply composition test now checks the split delivery instead of one combined inline string). `mvn clean install` from the repo root: BUILD SUCCESS, Tests run: 874, Failures: 0, Errors: 0, Skipped: 0.
agent added 1 commit 2026-08-22 12:05:25 +02:00
CB-617 Unit A: role charter via file, not argv
CI / build (pull_request) Successful in 1m16s
CI / contract (pull_request) Successful in 1m28s
7e97f5bff5
herdr refused to shell-encode a multi-line inline --append-system-prompt
argument (invalid_agent_argument), which broke any Claude Code profile with
a configured multi-line fleet.charters.<role>. Split delivery: the role
charter now writes to a temp file and mounts via
--append-system-prompt-file; the one-line REPLY_CHARTER keeps its inline
--append-system-prompt delivery, since it must reach a member with no repo
checkout. Also pass --agent <role> when a role's agent-definition file
exists under the worker's cwd (.claude/agents/<role>.md for claude-code,
.opencode/agent/<role>.md for opencode); absent either file, nothing extra
is added and the member still spawns.

The base's LaunchSpec now carries roleCharter/replyCharter/cwd alongside
the existing composed charter field, so CharterReceipt keeps fingerprinting
the same composed text it always did -- unchanged, since the digest covers
the full logical charter content regardless of how it is delivered.
Owner

Closing as already landed. Checked with git diff --name-only main...pr121: this branch has zero commits and zero files not in main. The CB-617 Unit A work reached main by another path, and CB-618 has since fixed two defects on top of it (writeCharterFile is present in ClaudeCodeLauncher).

Closed as part of CB-621, which needs an empty PR list before the repo is transferred to the new org.

Closing as already landed. Checked with `git diff --name-only main...pr121`: this branch has **zero commits and zero files not in `main`**. The CB-617 Unit A work reached `main` by another path, and CB-618 has since fixed two defects on top of it (`writeCharterFile` is present in `ClaudeCodeLauncher`). Closed as part of CB-621, which needs an empty PR list before the repo is transferred to the new org.
ltms closed this pull request 2026-08-22 21:33:24 +02:00
Some checks are pending
CI / build (pull_request) Successful in 1m16s
CI / contract (pull_request) Successful in 1m28s

Pull request closed

Sign in to join this conversation.