fix(list): match the app's anytime and upcoming arrangement - #236
Merged
Merged
Conversation
Three changes to the two views the app arranges differently from the CLI, all measured against it on 10 Sep 2026 by uuid. Anytime no longer lists project rows. Every active project is trivially "anytime", so listing them all would bury the to-dos; the app uses the project as the group header above its own to-dos instead. The app's Anytime held none of the 23 active projects as a row and all 107 of their to-dos, which the CLI already matched exactly. This revises what #205 and #216 widened for this view alone. Today keeps its project rows, and so do upcoming and someday, where a project has actually been put somewhere. Anytime is ordered to match that arrangement: the items filed nowhere lead the list, then areas in area order, and inside an area its own loose to-dos come before those of its projects. Each key is a CASE rather than a plain index because Things writes its indexes negative, so the COALESCE default of 0 that stands for "not filed here" would sort where the app puts it first. The view listed in bare t."index" order before, which scattered each project's to-dos and made the rendered group header repeat. Upcoming reads by date rather than by list position, which is what the app's own Upcoming does: start date, then the within-day todayIndex it also keys Today on. Bare t."index" order interleaved the dates. Both now reproduce the app's order exactly — anytime over all 105 shared rows, upcoming over all 30. Two ordering gaps stay open rather than being guessed at, both recorded in the comments. A to-do inside a project that carries no area falls to the COALESCE default and sorts after every area; there is no such project in the data, so the app's answer could not be measured. Upcoming orders on a nullable todayIndex, where SQLite puts NULL first; no upcoming row carries a NULL, and the today view has the same shape, so changing one and not the other would invent a rule rather than match one. Closes #217
This was referenced Sep 10, 2026
ryanlewis
added a commit
that referenced
this pull request
Sep 10, 2026
Closes #237 ## Before and after, by uuid against the app This changes numbered rows in two views, so here is the position-match count each way: | view | before | after | | --- | --- | --- | | `today --include-completed` | 4 / 27 | **27 / 27** | | `someday` | 0 / 6 | **6 / 6** | | `anytime` (unchanged, as a control) | 105 / 105 | 105 / 105 | `things today` with no flag is that same order with the closed rows removed, 18 of 18. ## What changed The app arranges Today, Anytime and Someday identically: unfiled items first, then areas in area order, and inside an area its own loose to-dos before those of its projects. #236 gave `anytime` those keys; this gives them to the other two from the same constant, renamed from `anytimeGrouping` to `listGrouping` now that three views take it. ## today needed more than the missing grouping key The issue said to keep today's existing within-day key and add only what is missing. Adding only the loose-before-project key took today from 4 of 27 to **9** of 27, so I measured the app's own Today to find the rest. Two of today's within-day keys turned out to be wrong: - **`t.status ASC`** put the closed items `--include-completed` keeps at the end of their group. The app leaves a closed item where it was, struck through — six closed rows were interleaved through three groups in the measured list. - **`t.todayIndexReferenceDate DESC`** reordered whole groups by the day a `todayIndex` was last rewritten. That column stamps which day a `todayIndex` belongs to; the app does not sort on it. With both removed and `todayIndex` alone as the within-group key, today reproduces the app in every position. I confirmed the proposed keys directly against the app's list before touching the code, so the removal is measured rather than inferred. Flagging it because it is more than the issue asked for. The alternative was leaving today at 9 of 27, which would not close the issue. ## Tests Three added: today grouping loose to-dos before project to-dos across two areas; today keeping a closed row in place among open ones under `--include-completed`; and someday leading with unfiled items then areas in area order. Each seeds indexes running against the expected order so the key under test is the one doing the work. No existing test covered the two removed keys. ## Unverified, recorded in the comment A Someday project row and a loose Someday to-do in the same area both fall through to `t."index"`, comparing a project's index with a to-do's — different spaces, the same concern the `repeating` view's ordering calls out. It is no worse than the bare index ordering it replaces, and there is no Someday project in the data to measure the app's answer against. ## Docs `docs/content/commands.md` now describes the arrangement once for the three views that share it, notes that today orders within a group by the position Things keeps for the day, and says that `someday` reaches only the unfiled-then-areas half, since its filter excludes every to-do with a parent project. `make test` and `make lint` are clean.
ryanlewis
added a commit
that referenced
this pull request
Sep 10, 2026
Closes #245 Two changes the 10 Sep refactor review decided together (sections F and G). ## One statement of which views carry projects The sentence existed six times with six different tails, and three PRs conflicted on it in a day. **One copy had already gone stale**: the `--json` bullet in `SKILL.md` still listed `anytime` among the views carrying project rows after #236 removed them — and that ships in the binary, so agents were being told something untrue. | where | before | after | | --- | --- | --- | | `internal/skill/SKILL.md` | two copies | the one statement, labelled as such | | `docs/content/commands.md` | 19 lines | a line naming which filters match a project | | `docs/content/agents.md` | 20 lines | what an agent acts on: each row carries `"type"` | | `README.md` | 10 lines | a sentence | | `todoOrProject` comment | 14 lines | 5, pointing at SKILL.md | ## `task` as the word in prose The JSON `type` and the error `kind` already say `task`, so a reader who hit "ambiguous task" then read a hint about handing a **to-do** to an agent. The agent brief, the listing hint, the repeating refusal, the import refusal, the someday rejection and the `--todos` help all move to `task`. The `import` payload keeps `"to-do"` — that is Things' own format, not the CLI's word — and `errors.go` documents it as the exception. The payload fixtures in `importcheck_test.go` are untouched for the same reason; only the message assertions changed. ## What `/code-review --fix` caught, and it is the part worth reading **Six passages of documentation quote CLI output verbatim**, and the rename left every one of them wrong: the brief header, its closing line, the hint line, the `## Tasks` heading, and the repeating refusal in both `SKILL.md` and the README. An agent matching on the documented text would have matched nothing. All are corrected and checked against real output. It also found that I had created a redirect with no destination — the README said "see the commands page for the detail" while the commands page deferred to `things skill show` — and that a sentence I wrote gave the wrong cause for `anytime` having no project rows, since `today` and `someday` use the same group-header arrangement and do carry them. Both fixed. ## Deliberately not done - **Per-view rationale in `tasks.go`.** #240 is moving it next to the view each argument belongs to, which is where someone changing a filter will actually read it; collecting it in one comment is what made the old block a conflict magnet. `codecs-views` confirmed my hunk applies cleanly over #240 and has already reworded its cross-references. - **The ~100 remaining uses of the English word** in site prose about Things' own concepts. A blanket replace was explicitly not wanted. This does leave a seam: in `agents.md` the paragraph I rewrote says "tasks" and the next paragraph still says "a to-do inside a project". Worth a decision on whether the site prose follows. ## Follow-up noticed `internal/output/output_test.go:391` has a comment saying "today/upcoming/anytime list a scheduled project as a row", wrong since #217 narrowed `anytime`. Outside this diff. `make test` and `make lint` are clean.
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.
Closes #217
What changed
Three changes to the two views the app arranges differently from the CLI, all measured against it on 10 Sep 2026 by uuid.
anytimeno longer lists project rows. This is a revision of what #205 and #216 widened, for this view alone. Every active project is trivially "anytime", so listing them all buries the to-dos; the app uses the project as the group header above its own to-dos instead. Measured: the app's Anytime held none of the 23 active projects as a row, and all 107 of their to-dos, which the CLI already matched exactly.todaykeeps its project rows, and so doupcomingandsomeday, where a project has actually been put somewhere.anytimeis ordered to match that arrangement: items filed nowhere lead, then areas in area order, and inside an area its own loose to-dos before those of its projects. It listed in baret."index"order before, which scattered each project's to-dos and made the rendered group header repeat.upcomingreads by date, then by the within-daytodayIndexthe app also keys Today on. Baret."index"order interleaved the dates.Getting the keys right took two corrections
Worth recording, because both were wrong in ways that still produced plausible-looking output:
Each key is therefore a CASE rather than a plain index.
How verified
By uuid against the app on live data:
Plain output now renders the app's shape: unfiled items, then each area, then each project name once as a group header above its to-dos.
Deliberately left alone
todayand the catch-all--project/--area/--tagordering are byte-identical to before. An earlier revision of this branch had factored their ORDER BY into shared constants; I backed that out, because neither was asked to change andtodayis already at parity.Two ordering gaps are recorded in the comments rather than guessed at, both raised by
/code-review:upcomingorders on a nullabletodayIndex, and SQLite sorts NULL first. No upcoming row carries a NULL, and thetodayordering has the same shape, so changing one and not the other would invent a rule rather than match one.One consequence worth a decision, not fixed here: because
upcomingnow orders by date, a project with to-dos on several days gets its group header re-printed per day, since the CLI has no day header to group under the way the app does.Docs
The "which views carry projects" paragraph was rewritten once in
internal/skill/SKILL.md,docs/content/commands.md,docs/content/agents.mdand the README: every named view exceptinboxandanytime. Each says whyanytimeis the exception and points atthings projectsfor sweeping projects themselves.make testandmake lintare clean.