Skip to content

feat(cairnline): classify sidecar tool errors by structured code - #834

Merged
chicoxyzzy merged 1 commit into
masterfrom
feat/cairnline-sidecar-error-codes
Jul 9, 2026
Merged

feat(cairnline): classify sidecar tool errors by structured code#834
chicoxyzzy merged 1 commit into
masterfrom
feat/cairnline-sidecar-error-codes

Conversation

@chicoxyzzy

Copy link
Copy Markdown
Member

Summary

The Hecate Cairnline sidecar client previously detected a not-found tool-level error by substring-matching Cairnline's prose ("not found"). This change makes it read the machine-readable structuredContent.error.code that Cairnline now emits on a failed MCP tool call, and demotes the prose match to a transitional fallback used only when the sidecar returns no structured code (pre-contract builds).

Before / After

  • Before: projectCairnlineSidecarToolErrorIsNotFound(result.Text) — substring match on prose.
  • After: projectCairnlineSidecarToolErrorIsNotFound(result) — reads result.Result.StructuredContent{error:{code,message}} as the primary signal (code == not_found), falling back to the legacy prose match only when no structured code is present.

HTTP mappings unchanged: not_foundok=false → 404; every other code (or read failure) → 502.

What changed

  • New projectCairnlineSidecarToolErrorCode extractor decodes structuredContent into {error:{code,message}}, guarding nil/empty/null/malformed JSON (returns "").
  • projectCairnlineSidecarToolErrorIsNotFound now takes the full result and prefers the structured code.
  • Legacy substring match kept as projectCairnlineSidecarToolErrorTextIsNotFound, clearly commented as a transitional fallback.
  • All 4 call sites converted (projects.get, assignments.launch_packet, assignments.context, assistant.proposals.get).
  • Added table-driven regression tests for the extractor and the classifier (structured not_found → 404, other codes → 502, empty-structured + prose "not found" fallback → 404, empty + unrelated prose → 502).

Notes

Verification

  • go build ./... — clean
  • go vet ./internal/api/... ./internal/orchestrator/... — clean
  • go test ./internal/api/... — ok
  • GOCACHE="$(pwd)/.gocache" go test -race -timeout 10m ./internal/api/... ./internal/orchestrator/... — ok

Generated by Claude Code

Before, the Cairnline sidecar client detected a not-found tool-level
error by substring-matching Cairnline's prose ("not found"). Now it reads
the machine-readable structuredContent.error.code that Cairnline emits on
a failed MCP tool call, and only falls back to the legacy text match when
the sidecar returns no structured code (pre-contract builds).

This is standalone and does not bump the cairnline module pin: the wire
error code is read via a local wire constant rather than the exported
cairnline.ErrorCodeNotFound, deliberately avoiding a pin bump because
moving cairnline past alpha.5 pulls in the unrelated Assignment.ExecutionRef
string-to-struct refactor whose Hecate-side adaptation is owned by an
in-flight PR.

HTTP mappings are unchanged: not_found resolves to ok=false (404), every
other code or read failure resolves to the read-failure path (502).
@chicoxyzzy
chicoxyzzy marked this pull request as ready for review July 9, 2026 12:33
@chicoxyzzy
chicoxyzzy merged commit 1489df5 into master Jul 9, 2026
13 checks passed
@chicoxyzzy
chicoxyzzy deleted the feat/cairnline-sidecar-error-codes branch July 9, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant