Skip to content

fix: update commands honor the positional resource ID#89

Merged
yorkable merged 1 commit into
mainfrom
fix/update-positional-id
Jun 4, 2026
Merged

fix: update commands honor the positional resource ID#89
yorkable merged 1 commit into
mainfrom
fix/update-positional-id

Conversation

@yorkable

@yorkable yorkable commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

The bug was worse than the issue title

#68 reported --dry-run printing a PUT URL without the resource ID. Root cause: the API's update endpoints are collection-style (PUT /api/v1/agents/) and route by the identifier inside the body — and the six body-routed update commands accepted a positional ID without ever reading it. syllable agents update 574 --file body.json updated whatever id the body contained; 574 was decoration. It worked daily only because the canonical get → jq → update pipe carries the right id in the body.

The fix

A shared guard, ensureBodyIdentifier (cmd/root.go), applied to all six body-routed update commands:

Command Positional → body key
agents update id (numeric)
prompts update id (numeric)
custom-messages update id (numeric)
language-groups update id (numeric)
users update email
tools update name (body still routes by its numeric id; the name is validated/injected)
  • Body lacks the key → positional is injected (as a JSON number where the schema says integer).
  • Body carries a different value → hard error naming both; never silently prefer either:
    Error: positional argument "574" conflicts with id=575 in the request body — make them match or drop id from the body
    
  • --dry-run previews now show the effective identifier in the body (the URL stays collection-style — that is genuinely what the API expects):
    { "body": { "description": "noop", "id": 574 }, "dry_run": true, "method": "PUT", "url": ".../api/v1/agents/" }

Commands whose updates already embed the ID in the URL (outbound, insights, directory, channels) were audited and are unaffected; body-only update commands (roles, services, data-sources, voice-groups, incidents) keep their existing contract.

Tests

  • TestEnsureBodyIdentifier — inject/match/conflict/non-numeric/string-key/non-object cases
  • TestAgentsUpdateInjectsPositionalID — stub server receives id: 574 merged into the body
  • TestAgentsUpdateConflictingBodyID — conflict errors before any request is sent
  • TestUsersUpdateInjectsPositionalEmail, TestToolsUpdateValidatesPositionalName

go test ./... green locally.

Closes #68

🤖 Generated with Claude Code

The platform's collection-style PUT endpoints (agents, prompts,
custom-messages, language-groups, users, tools) route by an identifier
inside the request body, not the URL. The update commands accepted a
positional ID but never read it — the body's value silently decided
which resource got updated, and --dry-run previews showed neither.

A shared guard (ensureBodyIdentifier) now reconciles the two on all six
commands: the positional is injected into the body when absent (id for
agents/prompts/custom-messages/language-groups, email for users, name
for tools), and a conflicting body value refuses the update instead of
guessing. Dry-run previews now show the effective identifier.

Closes #68

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@yorkable
yorkable merged commit 713b1d8 into main Jun 4, 2026
2 checks passed
@yorkable
yorkable deleted the fix/update-positional-id branch June 4, 2026 06:22
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.

--dry-run on PUT updates omits the resource ID from the URL

1 participant