Two distinct kickoff bugs surfaced during a real-world --container docker run on macOS. They share a common entrypoint surface so I'm filing together; happy to split if preferred.
Bug 1 — macOS Keychain auth gap
What
The container entrypoint (`crosslink/resources/container/entrypoint.sh`) only handles auth via:
```bash
if [ -f /host-auth/.credentials.json ]; then
cp /host-auth/.credentials.json /home/agent/.claude/.credentials.json
```
On macOS, claude CLI stores its OAuth credentials in Keychain, not in `/.claude/.credentials.json`. Result: `/host-auth/` (which is `/.claude` bind-mounted) has no `.credentials.json`, the conditional skips, the agent boots without auth, and claude CLI prints:
```
Claude configuration file not found at: /home/agent/.claude/.claude.json
A backup file exists at: /home/agent/.claude/backups/.claude.json.backup.
[...]
Not logged in · Please run /login
```
The agent then either hangs forever or exits silently depending on flags.
Reproduction
- macOS host with claude CLI authenticated via OAuth (Keychain). `ls ~/.claude/` shows no `.credentials.json`.
- `crosslink kickoff run --container docker --skip-permissions `
- Container starts. `docker logs ` shows the "Claude configuration file not found" loop.
Suggested fix
Have the entrypoint additionally honor `CLAUDE_CODE_OAUTH_TOKEN` (and/or `ANTHROPIC_API_KEY`) as fallbacks, with a prioritized order:
- `/host-auth/.credentials.json` (Linux hosts with claude logged in via that file).
- `/host-auth/.kickoff.env` (or any `/host-auth/*.env`) sourced into the agent's environment, picking up `CLAUDE_CODE_OAUTH_TOKEN`.
- Pass-through environment variable from `crosslink kickoff run` itself (which currently has no `--env` flag — see #N for that piece).
Optionally: emit a clear error message if no auth path resolves, instead of falling through silently.
Workaround in use
A santana-local entrypoint wrapper at `forecast-bio/santana@feature/post-m0-bootstrap:scripts/santana-entrypoint.sh` sources `/host-auth/.kickoff.env` before delegating to the upstream entrypoint:
```bash
KICKOFF_ENV="/host-auth/.kickoff.env"
if [ -f "$KICKOFF_ENV" ]; then
set -a; source "$KICKOFF_ENV"; set +a
fi
exec /usr/local/bin/crosslink-entrypoint.sh "$@"
```
Plus a santana-local Dockerfile that installs the wrapper as ENTRYPOINT. Works but is redundant once this is fixed upstream.
Bug 2 — kickoff agent rewrote the design file passed via `--doc`
What
`crosslink kickoff run --doc <path/to/design.md>` is documented as supplying the canonical design doc as input to the agent. In a real run, the agent edited the design file itself during scaffolding work, dropping ~85 lines of content (including OPEN-question markers) and replacing the title heading.
Reproduction
- `crosslink kickoff run "M0: ..." --doc .design/.md ...`
- Agent runs to completion, commits include the design file as modified. PR is opened.
- Diff shows the agent re-templated the design doc (e.g. dropped a section, restructured headings). The post-merge state on `main` no longer matches the canonical version that was passed in.
Concrete instance
In the santana repo's M0 milestone, the agent rewrote `.design/santana-token-ar.md` from 402 lines to 317 lines, including dropping three `` blocks (Q1, Q2, Q3) that captured genuine open architectural questions. The file's recorded `doc_hash` in `.design/.pipeline.json` (the kickoff infra's own state file) still pointed to the canonical sha256, mismatching the new committed content.
The post-M0 fix-up PR is at forecast-bio/santana#2 — restoring the canonical version.
Suggested fix
Treat the `--doc ` input as read-only in the agent's allowed-tool surface. Concretely:
- Mount the design doc at a stable path in the container (e.g. `/workspaces/repo/.design/.md`) but read-only, OR
- After the agent run completes, validate that `sha256()` matches the `doc_hash` already recorded in `.kickoff-plan.json` / `.pipeline.json`. Fail the kickoff (or at minimum produce a clear warning surfaced to the agent's report) if the hashes diverge.
- Document explicitly in `KICKOFF.md` (the in-context prompt) that the design doc is canonical input, not a working file.
Option 2 is the lighter-weight fix and would have caught this M0 issue at the validation step before the PR went up.
Environment
Cross-refs
Two distinct kickoff bugs surfaced during a real-world
--container dockerrun on macOS. They share a common entrypoint surface so I'm filing together; happy to split if preferred.Bug 1 — macOS Keychain auth gap
What
The container entrypoint (`crosslink/resources/container/entrypoint.sh`) only handles auth via:
```bash
if [ -f /host-auth/.credentials.json ]; then
cp /host-auth/.credentials.json /home/agent/.claude/.credentials.json
```
On macOS, claude CLI stores its OAuth credentials in Keychain, not in `
/.claude/.credentials.json`. Result: `/host-auth/` (which is `/.claude` bind-mounted) has no `.credentials.json`, the conditional skips, the agent boots without auth, and claude CLI prints:```
Claude configuration file not found at: /home/agent/.claude/.claude.json
A backup file exists at: /home/agent/.claude/backups/.claude.json.backup.
[...]
Not logged in · Please run /login
```
The agent then either hangs forever or exits silently depending on flags.
Reproduction
Suggested fix
Have the entrypoint additionally honor `CLAUDE_CODE_OAUTH_TOKEN` (and/or `ANTHROPIC_API_KEY`) as fallbacks, with a prioritized order:
Optionally: emit a clear error message if no auth path resolves, instead of falling through silently.
Workaround in use
A santana-local entrypoint wrapper at `forecast-bio/santana@feature/post-m0-bootstrap:scripts/santana-entrypoint.sh` sources `/host-auth/.kickoff.env` before delegating to the upstream entrypoint:
```bash
KICKOFF_ENV="/host-auth/.kickoff.env"
if [ -f "$KICKOFF_ENV" ]; then
set -a; source "$KICKOFF_ENV"; set +a
fi
exec /usr/local/bin/crosslink-entrypoint.sh "$@"
```
Plus a santana-local Dockerfile that installs the wrapper as ENTRYPOINT. Works but is redundant once this is fixed upstream.
Bug 2 — kickoff agent rewrote the design file passed via `--doc`
What
`crosslink kickoff run --doc <path/to/design.md>` is documented as supplying the canonical design doc as input to the agent. In a real run, the agent edited the design file itself during scaffolding work, dropping ~85 lines of content (including OPEN-question markers) and replacing the title heading.
Reproduction
Concrete instance
In the santana repo's M0 milestone, the agent rewrote `.design/santana-token-ar.md` from 402 lines to 317 lines, including dropping three `` blocks (Q1, Q2, Q3) that captured genuine open architectural questions. The file's recorded `doc_hash` in `.design/.pipeline.json` (the kickoff infra's own state file) still pointed to the canonical sha256, mismatching the new committed content.
The post-M0 fix-up PR is at forecast-bio/santana#2 — restoring the canonical version.
Suggested fix
Treat the `--doc ` input as read-only in the agent's allowed-tool surface. Concretely:
Option 2 is the lighter-weight fix and would have caught this M0 issue at the validation step before the PR went up.
Environment
ghcr.io/forecast-bio/crosslink-agent:latestis not published — add to CI/CD #576 workaround)Cross-refs
ghcr.io/forecast-bio/crosslink-agent:latestis not published — add to CI/CD #576 — ghcr.io image not published; produced the chain that exposed Bug 1.