Skip to content

fix(list): include projects in deadlines - #220

Merged
ryanlewis merged 1 commit into
mainfrom
fix/issue-213-deadlines-projects
Sep 10, 2026
Merged

ryanlewis merged 1 commit into
mainfrom
fix/issue-213-deadlines-projects

Conversation

@ryanlewis

Copy link
Copy Markdown
Owner

Closes #213

What changed

deadlines now lists projects alongside to-dos. A project takes a deadline exactly as a to-do does, things projects -j has reported it since #204, and agents.md advertises this view as the way to sweep what is due — so an agent following the docs missed every project deadline. The view was pinned to t.type = 0, so those rows were excluded by construction, not by data.

The view now selects t.type IN (0, 1) through the existing todoOrProject constant, the same rule #205, #209 and #216 applied to the other views. Its ORDER BY t.deadline ASC is unchanged, so project rows fall in among the to-dos by date rather than forming a block of their own. --on, --from and --to already filter t.deadline on this view and now filter project rows by their own deadline.

inbox is now the only named view that stays to-do only, which is a property of the Inbox rather than a limitation: an inbox item has not been filed anywhere yet, so it is never a project. A bare -p/-a/-t filter with no view named is the other exception, and the docs now say so explicitly — it routes to the internal catch-all view, which is still pinned to to-dos.

Verification: negative only, and worth being plain about

There is nothing on this database that exercises the change, and I would rather say so than imply a check I did not make.

The app has no Deadlines list to compare against, so the fallback is things projects -j, which reports no project deadlines at all. Reading the live database with sqlite3 -readonly shows why. Seven projects carry a deadline, and every one is already excluded by the status = 0 AND trashed = 0 clause this PR does not touch:

why excluded count
completed or cancelled 4
trashed 3
would list 0

So things deadlines returns the same 13 to-do rows before and after this change, and the SQL the new filter generates selects those same 13. What I have verified is that no project is wrongly admitted and none is wrongly held back for a reason this PR introduces. What I have not verified is a project deadline appearing in the app and then in the CLI, because no such project exists here.

The tests below cover what the live data cannot.

Tests

Six tests in internal/db/tasks_test.go over a new seedDeadlines helper. The fixture interleaves a project between two to-dos by deadline while running "index" against that order, so an ordering that fell back to "index" or grouped by type would fail:

  • projects with a deadline are listed and carry model.TypeProject
  • the project sorts between the two to-dos, by deadline
  • a project with no deadline, a completed one, a trashed one and a heading all stay out
  • --on matches a project row by its own deadline
  • --area finds a project through its own area; --project cannot match one
  • a repeating project template with a deadline, and the to-do inside it, stay out — the one exclusion path that comes from viewsIncludingTemplates rather than the view filter

Docs

internal/skill/SKILL.md, docs/content/commands.md, docs/content/agents.md and README.md: the view lists now say every named view except inbox carries projects, and name the bare-filter exception so an agent knows to name a view when project rows matter. The README gains the general rule it never stated, so its deadlines table row no longer reads as though that view is the only one carrying projects. internal/output/output.go had a comment enumerating the old set of views.

Closes #213

A project takes a deadline exactly as a to-do does, `things projects -j`
has reported it since #204, and agents.md advertises `things deadlines`
as the way to sweep what is due — but the view pinned t.type = 0, so an
agent following the docs missed every project deadline.

The view now selects t.type IN (0, 1) through the existing todoOrProject
constant, the same rule #205, #209 and #216 applied to the other views.
ORDER BY t.deadline ASC is unchanged, so project rows fall in among the
to-dos by date rather than forming a block of their own, and --on/--from/
--to filter a project row by its own deadline.

Verification here is negative only, and the PR says so. Nothing on this
database exercises the change: all seven projects carrying a deadline are
already excluded by the status and trashed clause this commit does not
touch, four closed and three trashed, so the view returns the same 13
to-do rows before and after. What is verified is that no project is
wrongly admitted and none wrongly held back for a new reason.

Docs now say every named view except inbox carries projects, and name the
bare --project/--area/--tag filter as the other exception: it routes to
the internal catch-all view, which is still to-do only.
@ryanlewis
ryanlewis merged commit b688e55 into main Sep 10, 2026
10 checks passed
@ryanlewis
ryanlewis deleted the fix/issue-213-deadlines-projects branch September 10, 2026 11:10
@ryanlewis

Copy link
Copy Markdown
Owner Author

Follow-up: this now has the positive verification the PR body said it lacked.

When PR #228 needed a probe to settle #211, the user approved giving that throwaway project a deadline so it could check this view too. A project PROBE-someday-20260910-122504 was created in Things through the app, put in Someday, and given a deadline of 2026-09-24.

things deadlines -j returned 14 rows where it had returned 13, the extra one being that project:

uuid EgGWfLr644EueTZDebY76h  type project  deadline 2026-09-24

So a project deadline created in the app does reach this view, ordered by its date among the to-dos. Before this change the same query returned 13 rows and no project.

The probe was deleted through the app afterwards and things deadlines is back to 13 rows. The seven pre-existing projects carrying a deadline are still all excluded, four closed and three trashed, exactly as described above.

ryanlewis added a commit that referenced this pull request Sep 10, 2026
Closes #222

## What changed

The internal catch-all view — the one a bare
`--project`/`--area`/`--tag` filter routes to when no view is named —
was still pinned to `t.type = 0` while every named view except `inbox`
had been widened to `todoOrProject` (#205, #209, #216, #220). It now
uses the same set.

`--project` is unaffected in practice: a project has no parent project
of its own, so `p.uuid` never matches a project row and the filter still
returns a project's contents. The visible change is for `--area` and
`--tag`.

## Why

An agent sweeping an area with `things --area Work -j` got none of that
area's projects, which is exactly the miss #222 describes. Parity with
what Things.app shows in the matching list is the project's stated goal.

## How verified

Measured by uuid against Things.app on 10 Sep 2026. For one area the app
reports four projects; the CLI returned zero project rows before this
change and returns the same four uuids after. `things --project <uuid>
-j` still returns zero project rows.

Tests added in `internal/db/tasks_test.go` cover the widened set, the
`--area`/`--tag`/`--project` split, and the exclusions that must not
widen with it — headings, trashed projects, repeating project templates
and the to-dos inside them. Four existing tests asserted the old
to-do-only behaviour and now assert the project rows: two area filters,
the catch-all grouping order, and the CLI-level default-view test.

`make test` and `make lint` are clean.

## Docs

The paragraph naming the bare-filter exception was rewritten once, in
`internal/skill/SKILL.md`, `docs/content/commands.md`,
`docs/content/agents.md` and the README. It now states the `--project`
case rather than a whole-view exception.
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.

bug: things deadlines omits projects that have a deadline

1 participant