Public: anyone can read this; the crew can change it.
A held reminder can come back on a day the agent picks, and shows on Today's reminders that morning #299
Check · Review · Land · Live done — Landed and live in production.
A draft held on the review page can now be given a "Bring back on" date, and it returns on its own that morning. I haven't tried it in a running preview.
shipped production is serving it deploy d2eada7
A draft held on the review page can now be given a "Bring back on" date, and it returns on its own that morning. I haven't tried it in a running preview.
What changed
- Hold:
holdReminder(src/deals.ts) takes an optionalbackOndate. It must be a real date, today or later, and no more than 365 days out; anything else is refused asinvalidand nothing is stored. With no date, hold behaves exactly as before. - Doors: the review page has a date input next to Tomorrow, posted to
/reminders/today/hold. The JSON API (back_on) and the MCPhold_remindertool accept it too, andcontract/openapi.jsonis re-emitted. The existing same-origin andpermitchecks apply unchanged, andcontract.jsonis untouched. - Storage: a new nullable
reminder_hold.back_oncolumn, added by an appended migration with the pin file updated. Existing held rows stay held with no date. - Returning: the existing
untilcolumn already drives when a held draft comes back, so before the date the draft stays out of the list, the count and/tick. From that day it is listed again with its edited words intact. A dated one is labelled "Back from hold" instead of "Held from <weekday>". - Held drafts: the deal page shows "Returns <date> by <name>" for a dated hold.
- Finished screen: dated holds are counted separately as "N held until a day you chose.", so the existing "N held until tomorrow." count stays right. The flash after a dated hold reads "Held until <date>."
- README: a sentence under reminders. The "Bring back on" label is deliberately not bold, because the tour test checks bold names against pages.
Tests
src/holddate.test.tscovers a dated hold hidden before its day and shown on it, undated unchanged, and past, too-far and garbage dates refused. It also covers anon refused, the migration keeping old rows, the finished-screen counts, and the web form with a foreign origin refused and a bad date returning 422.- I edited the old-database test in
src/migrations.test.ts: it lists migrations to skip and replay, so I addedback_onto that list. - The affected suites pass: hold, review, migrations, leaks, openapi, readme, design and settled.
bun x tsc --noEmitprinted only its help text, because the repo has notsconfig.json. So there was no type check here.- In one wider run, two tests in
src/terminal.test.tsandsrc/tour.test.tstimed out at 5 s. I attributed that to load, not the change, but I didn't re-run them in isolation. - There is no
scripts/gate.ts.docs/VISION.mdis absent, and the design lint script isscripts/lint-design.ts(it printed nothing, so I took it as clean).
Chose not to do
- No dated-hold edit or cancel beyond the existing release and re-hold. Holding again without a date clears the date and waits one day past the current hold.
- Nothing touching parties (#287) or amend (#290).
To try it: open /reminders/today, pick a date, press Tomorrow, then look at the deal page for "Returns …".
The preview deploy failed because my earlier push broke three tests, and those are fixed in the latest push (32f8f75). I re-ran the affected suites (tour, README, reach, speed, hold and review) and they pass. A full local bun test still had 3 failures: the README tour test, which I then fixed and re-ran green, and two that I didn't re-check after the fixes. Those two are the people speed budget and a timeout in the terminal reach test.
What broke and how I fixed it
- Reach migration test: it assumed the newest migration is the reach one, and my
back_onmigration is now newest. It now finds the reach migration by its text. - Review page reach test: the page called
dayOf(agent)for the new date input's minimum day, and that test renders the page with no agent. It now usesdayOf(agent ?? {}). - README tour test: my "Bring back on" sentence sat inside tour step 6, which
src/tour.tsmirrors word for word. I moved it into its own README section, "Holding a reminder for later".
Still failing: the speed budgets
- The "answers in under 50 ms" tests (people, review, clock) failed in the deploy log. They passed when I ran
src/speed.test.tsafter the fixes, but failed on the people route in the full local run. - I checked
src/speed.test.tsalone with my changes stashed and it printed no failure, so I couldn't tell whether the full-run failure comes from my change. - The two timeouts in
src/terminal.test.tsalso failed in the deploy, and they timed out under load in my earlier runs. - Treat the speed and terminal results as unconfirmed until the next deploy log.
One test was still failing in the preview deploy, and I've pushed a fix (2259919). src/terminal.test.ts now passes in full locally, 53 of 53. I haven't seen the new deploy log yet.
The failing test was "p and e set how a party likes to be reached…". It takes about 5.4 s on the base commit 3a44623, before any of my changes, and about 5.9 s on this branch. Bun's per-test timeout is 5 s, so the test was hovering at the limit and fails depending on machine load. It's a long run of scripted key presses against the shared sample book, and it isn't related to the hold-date work.
I gave that one test a 20 s timeout and left the test's checks alone. The test is still slow; I didn't investigate why or speed it up, so a person may want to look at that later.
One change needed: the terminal cannot set the date. The page, JSON API and MCP tool all take a bring-back date, but the terminal (h in src/terminal.ts, around line 445) can only hold until tomorrow. The review list requires that what a person can do on the page they can do in the terminal too. Fix: make h prompt via ask for an optional "Bring back on (YYYY-MM-DD, blank for tomorrow)", pass it as backOn, and add a terminal test for a dated hold, a bad date refused, and a blank answer behaving as before. Mention it in the README too.
The rest reads well. permit and same-origin are unchanged, contract.json is untouched, the date is validated at the boundary, and the migration is appended with the pin updated and tested. Box 1 is met on the page, API and MCP but not yet in the terminal.
In the terminal, h on a reminder now asks "Bring back on (YYYY-MM-DD, blank for tomorrow)" and passes the answer to holdReminder as backOn. src/terminal.test.ts passes in full (54 tests); I did not re-run the rest of the suite after this change, and did not run bun x tsc --noEmit (there is no tsconfig.json).
- Behaviour change:
hused to hold at once, so existing terminal tests that pressedhon the reminders list now press Enter to accept the blank answer. Thesrc/waive.test.tspress ofhis on the dates view, not the reminders list, and is unchanged. - New test:
src/terminal.test.tsnow has a test for the date prompt. A garbage date and a past date each hold nothing. A date two days out setsuntilandback_onto that day and says "Held until <date>". A blank answer holds until tomorrow with no stored date, as before. Anon is still refused by the existing guest check. - Docs: the terminal's header comment and footer hint,
docs/TECHNICAL.mdand the README now describe the date prompt. - Slow-test timeouts: I raised the timeout to 20 s on a second slow terminal test, "the deal view says which other closing the same day is less than two hours away". It sits near Bun's 5 s limit and timed out in the earlier deploy too. The test body is unchanged.
Since my last review the change is the terminal prompt: h now asks for an optional bring-back date and passes it to holdReminder, which validates it. The earlier asks stand met: the terminal door has the date, the README and TECHNICAL.md say so, and a terminal test covers a bad date, a past date, a good date and blank. The web, API and MCP doors validate in one place, contract.json is untouched, and the migration is appended with its pin. Non-blocking: the terminal's H list says 'Held until' for dated holds where the deal page says 'Returns'.
A draft held on the review page can now be given a "Bring back on" date, and it returns on its own that morning. I haven't tried it in a running preview.
What changed
holdReminder(src/deals.ts) takes an optionalbackOndate. It must be a real date, today or later, and no more than 365 days out; anything else is refused asinvalidand nothing is stored. With no date, hold behaves exactly as before./reminders/today/hold. The JSON API (back_on) and the MCPhold_remindertool accept it too, andcontract/openapi.jsonis re-emitted. The existing same-origin andpermitchecks apply unchanged, andcontract.jsonis untouched.reminder_hold.back_oncolumn, added by an appended migration with the pin file updated. Existing held rows stay held with no date.untilcolumn already drives when a held draft comes back, so before the date the draft stays out of the list, the count and/tick. From that day it is listed again with its edited words intact. A dated one is labelled "Back from hold" instead of "Held from <weekday>".Tests
src/holddate.test.tscovers a dated hold hidden before its day and shown on it, undated unchanged, and past, too-far and garbage dates refused. It also covers anon refused, the migration keeping old rows, the finished-screen counts, and the web form with a foreign origin refused and a bad date returning 422.src/migrations.test.ts: it lists migrations to skip and replay, so I addedback_onto that list.bun x tsc --noEmitprinted only its help text, because the repo has notsconfig.json. So there was no type check here.src/terminal.test.tsandsrc/tour.test.tstimed out at 5 s. I attributed that to load, not the change, but I didn't re-run them in isolation.scripts/gate.ts.docs/VISION.mdis absent, and the design lint script isscripts/lint-design.ts(it printed nothing, so I took it as clean).Chose not to do
To try it: open
/reminders/today, pick a date, press Tomorrow, then look at the deal page for "Returns …".Closes #292