fix(api): settle the fourteen API inconsistencies the route docs pass found #280
No reviewers
Labels
No labels
bug
enhancement
needs-triage
ready-for-agent
ready-for-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Cloonar/deckle!280
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "afk/273"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #273
Fixes all fourteen items #273 lists. Each one calls the rule's existing owner (CLAUDE.md "Every rule has exactly one owner") and has a test.
docs/api.mdnow describes the corrected behaviour for every route touched.Changes
GET /api/link/resolvecheckslangthroughopsRootbefore handling any kind. An unknownlangis now404 editor.unknownLangforexternalandmediatoo.POST /api/element/duplicatenow callselemops.SlotElementsMsg, so it returns422 element.slotRefuses, the same code as insert.GET /api/page/refson the content root's own page returns400 page.rootRefs, a new code. It uses the new exportedpageops.IsContentRootPage.GET /api/media/librarylists legacy folders whose names are not slugs, usingmediaops.CleanFolder(the existing shape check, now exported). The slug rule only applies when an upload would create a new folder. The English and German text oflibrary.folderInvalidis reworded to say what the check actually tests.folderthe same way, throughmediaops.NormalizeFolder, which lives in the sharedplanFolderDelete. Running the delete therefore gives the same result as the dry-run plan. The server'strimFolderSlashesand the copy inmedia.SaveToLibraryare gone.POST /api/media/uploadcheckstargetandfolderbefore it decodes the file.pageops.requireTitle.enabledis read withelement.ReadEnabled. Any value other than true, false or absent is afield.boolfield error (elemops.NewItemnow returns that error).handleSavePageusesdecodeBodyandrequirePost.parent. The placement checks moved out ofpageops.Createintopageops.CheckCreatePlacement, and create and the form both call it, so they return the same refusals with the same codes and statuses.elementL10nreports nothing as synced when the source page has no element with the same uid and name. The editor now reads the form description'ssyncedlist and no longer rebuilds it fromtranslation.synced[name]. Without this second change, the UI would have kept showing those fields as read-only.create(insert, duplicate),delete,move, andenable/disable.snapshotOpsandundo_ops.jsonare unchanged.l10n.beforeSlot,beforeParentandbeforePagecodes can never be returned, soUnplacedErrornow carries a single message and the three codes are deleted from Go and from the client catalogs.GET /api/publish/statuschecks the commit. A value that is not a 40-character hex sha returns400 request.invalidParam(new code), and a well-formed sha the repository does not contain returns404 publish.unknownCommit(new code). A commit the server never polled still returnsstatic, but only if the repository has it. Without a repository the route returns409 publish.noRepo, the same answer the other publish routes give. New helpers:gitrepo.IsCommitIDandRepo.HasCommit.Verification
go test ./...,go vet ./...and gofmt pass, including the drift tests for routes, snapshot ops, docs and apimsg.cd web && npx vitest runpasses (124 files, 3213 tests).tsc --noEmitandnpm run buildsucceed.link/resolve?lang=zzreturns 404.page/refs?path=_index.mdreturns 400page.rootRefs.media/library?folder=My Photosreturns 200. The plan and the delete agree on a folder written with backslashes.publish/statusreturns 400 forzzz, 404 for an unknown sha, andstaticfor HEAD.Follow-ups (not in this PR)
l10n.beforePageParentFirstlooks unreachable too, since a top-level element is always placed. I kept it because no test proves that yet.save. Transfer is not one of the routes the issue lists.pageops.isContentRootPageis now a thin wrapper aroundIsContentRootPage, so the rule is still evaluated only once. Renaming its callers is a mechanical cleanup.scaffold/site/AGENTS.mdand the scaffolded skills are untouched, because nothing they document has changed.🤖 Generated with Claude Code
Each fix calls the rule's existing owner instead of a sibling copy: - link/resolve validates lang (opsRoot) before answering any kind - element/duplicate checks the slot allowlist (elemops.SlotElementsMsg) - page/refs refuses the content root's own page: 400 page.rootRefs - media/library lists legacy non-slug folders (mediaops.CleanFolder); the slug rule stays where a folder comes into being - both folder-delete routes spell folder through mediaops.NormalizeFolder - media/upload checks target and folder before decoding the file - create, rename and translation rename trim the title in one place (pageops.requireTitle) - an item row's non-boolean enabled is a field.bool field error (element.ReadEnabled) - page/save decodes through decodeBody - a create form ref's parent is checked as create checks it (pageops.CheckCreatePlacement) - no same-uid, same-name source element means nothing is synced; the editor takes the form description's synced verdict - structural element writes snapshot as create/delete/move/enable/disable - the plain l10n.before{Slot,Parent,Page} codes were unreachable and are removed - publish/status refuses a malformed commit (400 request.invalidParam) and an unknown one (404 publish.unknownCommit) docs/api.md describes the corrected behaviour for every touched route. Closes #273 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>[autoland] verdict: pass
PASS. Signal: CI aggregate success (ci / build-and-test: npm build + vitest, go vet, go test ./... incl. route/docs/apimsg/snapshot-ops drift tests); gofmt -l on changed Go files clean locally. Conventions: Conventional Commits title, Closes #273 present, base
a0c4ce7= main (no conflict). Scope matches the 14 items of #273; each calls the named owner (opsRoot, SlotElementsMsg, mediaops.CleanFolder/NormalizeFolder in planFolderDelete, pageops.requireTitle, element.ReadEnabled, decodeBody, elementL10n, CheckCreatePlacement shared with Create). Removed l10n.before{Slot,Parent,Page} codes have no remaining references; new codes in apimsg and both catalogs; docs/api.md updated per route. The web change for item 11 (ElementForm reads the description's synced verdict) is necessary for the fix to reach the UI and follows CLAUDE.md's 'client does not re-derive' rule.