Create kanban columns (#6) #13

Merged
ethan merged 2 commits from feat/create-column into feat/query-tasks-by-tag 2026-08-09 20:38:36 -05:00
Member

Closes #6.

Stacked on #12 (→ #11 → #10). Merge in order and each retargets cleanly.

This was the only one of the nine with no known write endpoint. Found it by probing a throwaway project.

The endpoint

POST column, with the same {"add": [...], "update": [], "delete": []} envelope every other batch endpoint uses, returning the standard id2etag/id2error.

The near-misses are recorded in DESIGN.md §14 so nobody has to repeat the search:

Attempt Result
POST batch/column 404
POST column/project/{id} 405 — GET only, that's the read path
POST column with a bare object 200, empty envelope, nothing created
POST column with a JSON array 500
POST column with the batch envelope ✅

The bare-object case is the nasty one — it looks like success and silently does nothing.

Also worth knowing: columns do not require the project to be in kanban view. A project with viewMode: null accepts them fine, so there's no view-mode dance before creating one.

Duplicate names — the guard is load-bearing

The issue asked for duplicates to be "rejected or return the existing one". I verified why it matters: TickTick does create the duplicate. A raw POST of a second column named Doing produced:

columns now ['Doing', 'Doing']

That splits one logical section in two — the exact failure the acceptance list anticipated. So create_column is idempotent by name, compared case-insensitively, and returns the existing column instead. A create-if-missing reconcile can call it every run.

This is the same shape as _require_group / _require_column in #11: the v2 API validates nothing, so our guard is the only one there is.

Acceptance

  • Create a column with a name and optional position ✅
  • Returns the new column id, usable without a second lookup ✅ — a live test creates a column, then immediately creates a note into it by that id
  • Duplicate name rejected or returns existing ✅ — returns existing, case-insensitively

Testing

186 passed, plus 8 live tests. The live test asserts one column still exists after three create_column("Doing"/"DOING") calls.

🤖 Generated with Claude Code

https://claude.ai/code/session_012VU4wBhFAAosZLQcWqvHsf

Closes #6. > **Stacked on #12** (→ #11 → #10). Merge in order and each retargets cleanly. This was the only one of the nine with no known write endpoint. Found it by probing a throwaway project. ## The endpoint `POST column`, with the same `{"add": [...], "update": [], "delete": []}` envelope every other batch endpoint uses, returning the standard `id2etag`/`id2error`. The near-misses are recorded in DESIGN.md §14 so nobody has to repeat the search: | Attempt | Result | |---|---| | `POST batch/column` | 404 | | `POST column/project/{id}` | 405 — GET only, that's the read path | | `POST column` with a bare object | 200, empty envelope, **nothing created** | | `POST column` with a JSON array | 500 | | **`POST column` with the batch envelope** | ✅ | The bare-object case is the nasty one — it looks like success and silently does nothing. Also worth knowing: columns do **not** require the project to be in kanban view. A project with `viewMode: null` accepts them fine, so there's no view-mode dance before creating one. ## Duplicate names — the guard is load-bearing The issue asked for duplicates to be "rejected or return the existing one". I verified why it matters: **TickTick does create the duplicate.** A raw POST of a second column named `Doing` produced: ``` columns now ['Doing', 'Doing'] ``` That splits one logical section in two — the exact failure the acceptance list anticipated. So `create_column` is idempotent by name, compared case-insensitively, and returns the existing column instead. A create-if-missing reconcile can call it every run. This is the same shape as `_require_group` / `_require_column` in #11: the v2 API validates nothing, so our guard is the only one there is. ## Acceptance - **Create a column with a name and optional position** ✅ - **Returns the new column id, usable without a second lookup** ✅ — a live test creates a column, then immediately creates a note into it by that id - **Duplicate name rejected or returns existing** ✅ — returns existing, case-insensitively ## Testing `186 passed`, plus 8 live tests. The live test asserts one column still exists after three `create_column("Doing"/"DOING")` calls. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_012VU4wBhFAAosZLQcWqvHsf
Closes #6.

Endpoint found by probing: POST column with the same
{"add": [...], "update": [], "delete": []} envelope the other batch endpoints
use. The near-misses are recorded in DESIGN.md §14 so nobody repeats the search
— batch/column is a 404, POST column/project/{id} is a 405, POST column with a
bare object returns an empty envelope and silently creates nothing, and a JSON
array body is a 500. Only the bare `column` path with a batch body works.
Columns do not require the project to be in kanban view.

create_column is idempotent by name, case-insensitively: an existing column of
that name is returned rather than duplicated. That guard is load-bearing rather
than defensive — verified that TickTick DOES create a second column with the
same name when asked, producing ["Doing", "Doing"] and splitting one logical
section in two. Same shape as the group/column guards in §12: the API has no
validation, so ours is the only one.

The returned Column carries the id, so a caller can place cards into a
brand-new column in the same run without a second lookup. A live test asserts
that end to end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012VU4wBhFAAosZLQcWqvHsf
Covers the nine-issue batch: wire timestamps and folder groups (#1, #8, #3),
project placement, sort order, column guards and note append (#2, #9, #7, #4),
tag query (#5), and column creation (#6).

Sits at the top of the PR stack so main is release-ready the moment the stack
lands — tagging v0.5.0 then triggers the Woodpecker image publish, which is what
actually puts the new tools on the deployed gateway.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012VU4wBhFAAosZLQcWqvHsf
ethan merged commit 65763c6e5f into feat/query-tasks-by-tag 2026-08-09 20:38:36 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
halfst/ticktick-mcp!13
No description provided.