fix(project edit): refuse a to-do with the same structured error as edit - #193
Merged
Merged
Conversation
`things project edit <to-do>` returned a bare "not a project: <title>", so under --json it landed as the generic `error` token with no kind, uuid or title. An agent branching on tokens handled a project sent to `edit` but not a to-do sent to `project edit`. Both guards now return the same error type, which carries the token it should render as: `not a task` one way, `not a project` the other, with kind, query, uuid and title in each. The plain-text message mirrors its opposite — `"Post letter" is a to-do; use things edit`. Closes #191
Follow-up from review: the site's token list predates `not a task` and did not gain `not a project` either, so an agent author reading the reference docs would meet a token the CLI emits but the list does not mention. Also fall back to the generic `error` token if a wrongKindError is ever built without one, rather than shipping an empty string a consumer cannot match.
ryanlewis
added a commit
that referenced
this pull request
Sep 10, 2026
Closes #219 The `--json` error payload spelled one concept two ways. `kind` was `"task"` everywhere except the `not a project` failure, where it was `"to-do"`, while `type` on a task row has said `"task"` since #214. One word wins, and that word is `task`. ## What changed `ProjectEditCmd.Run` now builds its `wrongKindError` with `Kind: "task"`. That field feeds both the JSON `kind` and the rendered message, so the message moves with it: `"Post letter" is a task; use things edit`. Every other producer already said `task`, `project`, `area` or `tag`. The docs that print or explain the payload follow: the error-payload example in the bundled skill, the token paragraph on the agents page, and the README paragraph explaining what `kind` carries. The skill gains a one-line note in its `--json` section saying a to-do is a `task` in every JSON value the CLI emits, naming `import` payloads as the one exception. `commands.md` and `configuration.md` are untouched. Neither documents the error payload, and no command or flag changed. ## Not a breaking change `kind: "to-do"` was introduced in #193, which is not an ancestor of `v0.7.0`. The value has never appeared in a release, so no published contract moves and there is nothing for the 0.8.0 notes to warn about. That is the same reasoning #223 applied to the `type` rename. The `error` token is untouched either way: it stays `not a project`. ## Out of scope, deliberately `import` payloads still spell a to-do `"to-do"`. That is Things' own JSON URL scheme format and it stays documented as the exception. Three other `kind` variables hold `"to-do"` and were left alone, because none of them reaches JSON. They only compose English prose: the repeating-item refusal in `verify.go`, the per-item refusal line in `importcheck.go`, and the agent brief's opening sentence in `internal/output/agent.go`. The issue scopes itself to JSON values, and renaming these would rewrite prose and its tests well outside that. The new comment on the `Kind` field says so explicitly, so the next reader does not take the rule as wider than it is. ## How tested `make test` and `make lint` both green. `TestRunProjectEditRefusesTodoReference` asserts the new `kind` and message and carries a comment saying why. The built binary was run against the live Things database to confirm the payload it prints.
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.
#190 gave
things edit <project>a stablenot a tasktoken under--json. The opposite mistake did not get the same treatment:things project edit <to-do>returned a barenot a project: <title>, which lands under--jsonas the generic{"error": "error"}with nokind,uuidortitle. An agent branching on tokens handled one direction and had to string-match the other.Both guards now return the same error type, carrying the token it renders as —
not a taskone way,not a projectthe other — withkind,query,uuidandtitlein each. The plain-text message mirrors its opposite:The guard sits before
checkRepeating, tag verification and any URL open, so nothing is written. A test mirrors the one from #190: a to-do reference throughproject edit, nothing opened, the whole payload asserted.Docs: the new token in
internal/skill/SKILL.md, the README error section and the site commands page, next to thenot a taskentries. The site's agents page listed tokens too and had never gainednot a task— it now names both.One thing found and left alone: title resolution has no type preference, so with both a project and a to-do called "Chores",
things project edit Choresresolves the to-do and points atthings edit, which is not the item the user meant. That is inresolveTask/GetTaskand predates this change — worth its own issue.Closes #191