feat(cairnline): classify sidecar tool errors by structured code - #834
Merged
Conversation
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).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-readablestructuredContent.error.codethat 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
projectCairnlineSidecarToolErrorIsNotFound(result.Text)— substring match on prose.projectCairnlineSidecarToolErrorIsNotFound(result)— readsresult.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_found→ok=false→ 404; every other code (or read failure) → 502.What changed
projectCairnlineSidecarToolErrorCodeextractor decodesstructuredContentinto{error:{code,message}}, guarding nil/empty/null/malformed JSON (returns"").projectCairnlineSidecarToolErrorIsNotFoundnow takes the full result and prefers the structured code.projectCairnlineSidecarToolErrorTextIsNotFound, clearly commented as a transitional fallback.projects.get,assignments.launch_packet,assignments.context,assistant.proposals.get).not_found→ 404, other codes → 502, empty-structured + prose "not found" fallback → 404, empty + unrelated prose → 502).Notes
structuredContent.error.codecontract added in feat(mcp): emit structured tool-error codes cairnline#79 via a local wire constant (cairnlineToolErrorCodeNotFound = "not_found"), deliberately avoiding a pin bump: bumping cairnline pastalpha.5pulls in the unrelatedAssignment.ExecutionRefstring→struct refactor (cairnline#76) whose Hecate-side adaptation is owned by the in-flight feat(projects)!: structured execution refs, awaiting_approval, and context memory through the Cairnline bridge #832. Follow-up: once feat(agent-adapters): guide Claude Code ACP credential setup #79 is tagged and feat(projects)!: structured execution refs, awaiting_approval, and context memory through the Cairnline bridge #832's ExecutionRef adaptation lands, bump the pin and swap the local constant forcairnline.ErrorCodeNotFound.assistant.applysignals partial-failure via its result body status fields, notstructuredContent.error.code. Hecate's sidecar reads don't useapply, so there is no impact here.Verification
go build ./...— cleango vet ./internal/api/... ./internal/orchestrator/...— cleango test ./internal/api/...— okGOCACHE="$(pwd)/.gocache" go test -race -timeout 10m ./internal/api/... ./internal/orchestrator/...— okGenerated by Claude Code