Slice 84: Claude SDK Harness Events
Scope#
Replace the thin Claude CLI JSON run path with a Claude Agent SDK client that
streams provider messages into CodeAlmanac HarnessEvent records.
This is an integration slice. It should not change workflow ownership, page-run lifecycle semantics, run storage, or CLI command behavior.
Why now#
docs/python-port-live-agreement.md says the inspectable transcript surface is
the normalized CodeAlmanac harness event stream and that Claude should use the
richer SDK/event harness, not only the one-shot CLI print path.
Cosmic Python chapter 4 frames the service layer as the place that defines
"the use cases of our system." For this slice, the use case stays
HarnessesService.run(request). The SDK is only a provider detail.
Cosmic Python chapter 13 argues for explicit dependencies and says "Explicit is better than implicit." The Claude query function should therefore be injected into the SDK client so tests can feed typed SDK messages without monkeypatching global imports.
Shape#
ClaudeSdkHarnessAdapter.run(request)
before = git_status_snapshot(...)
result = ClaudeSdkClient(query=claude_agent_sdk.query).run(request)
after = git_status_snapshot(...)
return result.with_changed_files(...)
ClaudeSdkClient.run(request)
return asyncio.run(self._run(request))
ClaudeSdkClient._run(request)
options = ClaudeAgentOptions(...)
async for message in query(prompt=request.prompt, options=options):
state.note_session(message)
events.extend(map_claude_message(message, state))
state.record_result(message)
events.append(done_event(state))
return result_from_state(state, events)Design decisions#
- Add
claude-agent-sdkas an internal runtime dependency. - Keep
HarnessAdapteras the service-owned port. - Keep readiness probing through
claude auth statusfor now, but reportANTHROPIC_API_KEYas ready when it exists. - Use SDK dataclass types with
isinstance; do not parse final text for lifecycle state. - Isolate Claude user/project/local settings with
setting_sources=[]andstrict_mcp_config=True. - Use
permission_mode="dontAsk"with the same local editing/search tool set the archive allowed. - Emit provider session, text delta, text, tool use, tool result, tool summary, context usage, error, done, and helper-agent trace events where the SDK exposes enough structure.
Out of scope#
- Public Python SDK or MCP surface.
- Hosted/cloud capture.
- Structured final output schemas.
- Real paid Claude model dogfood unless credentials and cost are explicitly accepted.
- Changing the default lifecycle harness away from Codex.
Files#
pyproject.toml,uv.locksrc/codealmanac/integrations/harnesses/claude/src/codealmanac/integrations/harnesses/__init__.pytests/test_claude_adapter.py- new Claude SDK client/event tests if they read clearer than one large test
docs/python-port-live-agreement.mddocs/python-port/next-agent-brief.mddocs/python-port/verification-matrix.mddocs/python-port/worklog.md
Verification#
uv run pytest tests/test_claude_adapter.pyuv run pytest tests/test_harnesses_service.py tests/test_ingest_workflow.py::test_ingest_workflow_records_normalized_harness_eventsuv run ruff check src/codealmanac/integrations/harnesses/claude tests/test_claude_adapter.pyuv run pytestuv run ruff check .git diff --check