Skip to content

fix(list): match the app's anytime and upcoming arrangement - #236

Merged
ryanlewis merged 1 commit into
mainfrom
fix/issue-217-mixed-row-ordering
Sep 10, 2026
Merged

ryanlewis merged 1 commit into
mainfrom
fix/issue-217-mixed-row-ordering

Conversation

@ryanlewis

Copy link
Copy Markdown
Owner

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.

anytime no 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. 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: 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 bare t."index" order before, which scattered each project's to-dos and made the rendered group header repeat.

upcoming reads by date, then by the within-day todayIndex the app also keys Today on. Bare t."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:

  1. Sorting on the area index alone put the unfiled items last. Things writes its indexes negative, so the COALESCE default of 0 that stands for "not filed here" outranks every real area.
  2. Sorting on the project index alone put each area's project to-dos before its own loose ones, for the same reason. The app does the opposite.

Each key is therefore a CASE rather than a plain index.

How verified

By uuid against the app on live data:

view rows order vs the app
anytime 105 shared identical for all 105
upcoming 30 identical for all 30, no membership difference either way

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

today and the catch-all --project/--area/--tag ordering 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 and today is already at parity.

Two ordering gaps are recorded in the comments rather than guessed at, both raised by /code-review:

  • 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 this data, so the app's answer could not be measured.
  • upcoming orders on a nullable todayIndex, and SQLite sorts NULL first. No upcoming row carries a NULL, and the today ordering 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 upcoming now 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.md and the README: every named view except inbox and anytime. Each says why anytime is the exception and points at things projects for sweeping projects themselves.

make test and make lint are clean.

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
@ryanlewis
ryanlewis merged commit 7dc0582 into main Sep 10, 2026
10 checks passed
@ryanlewis
ryanlewis deleted the fix/issue-217-mixed-row-ordering branch September 10, 2026 13:15
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.
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.

feat(list): decide how project rows sort among to-dos in views that now include both

1 participant