Public: anyone can read this; the crew can change it.
Each date can carry a short paperwork checklist the agent ticks off, and the deal page and client timeline show what is still missing #300
Check · Review · Land · Live done — Landed and live in production.
Each date can now carry up to eight named papers (names only, nothing stored). The agent ticks them off, and the deal page and client timeline show what is still missing.
shipped production is serving it deploy d2eada7
Each date can now carry up to eight named papers (names only, nothing stored). The agent ticks them off, and the deal page and client timeline show what is still missing.
I ran the tests that cover the change and they pass. The one test that fails is the terminal "reach" test in src/terminal.test.ts, which times out at 5 s here; it fails the same way on the base branch. I did not run the whole suite once at the end. There is no tsconfig.json, scripts/gate.ts or scripts/fast.ts in this repo, so bun x tsc --noEmit has nothing to check. bun scripts/lint-design.ts is clean.
What changed
- Table and rules: a new
papertable (migration 51, pin file updated) is deleted with its date.src/papers.tsholds the words and three changes: add, tick/untick and remove. - Limits and permissions: names are 1–80 characters, at most 8 per date, once per name per date. Each change asks
permitas editing a date does, is audited as an update of the deal, and sits behind the same-origin door. There is nocontract.jsonchange. - Forms: the three POST forms are
/deadlines/:id/papers,/papers/:id/tickand/papers/:id/remove. - Sheet and deal page: a date's sheet has a Papers section with the checklist and an add form. The deal page shows "2 of 3 papers in" under a date that has items.
- Suggestions: each standard date offers one suggested paper, for example Financing's "Lender's approval letter". The agent presses "Add suggested" on the date's sheet; nothing is added silently.
- Timeline: under each date it says "n papers still needed" and lists every paper. A paper that is in is struck through and marked "in"; an open one is marked "still needed".
- Done with open papers: the sheet's Mark done becomes a confirm that reads "2 papers still open. Mark done anyway?". The route also refuses a done without
confirm=yeswhile papers are open. - Export, import, samples: export writes
dates[].papersand import reads it. Files without the key still import, and a bad or oversize list refuses the whole file. Alder Lane and Harbor View carry a few papers, some in and some open. - Docs and CSS: README section,
docs/TECHNICAL.md, a newdocs/design/areas/papers.md, a timeline note, and CSS for the new classes.contract/openapi.jsonis re-emitted and the deadline schema gainspapers. - Tests:
src/papers.test.tshas 13 tests: add/tick/limit/validation, permissions, cascade delete, escaping, the confirm, export round trip, older-file import, bad-file refusal and the timeline. - Changes to existing tests:
src/reach.test.tsnow finds its migration by name rather than assuming it is last, and thesrc/migrations.test.tsfilter lists the new table.
Not done
- The "ask once" is at the page door only.
markDoneand so the API, MCP and terminal are unchanged, so existing callers don't suddenly fail on dates that carry papers. - There is no API, MCP or terminal door for papers.
- The new-deal form has no suggestion checkbox. The sheet's per-date suggestion covers every deal, including a new one's standard dates.
- I did not touch
src/amend.ts, to stay clear of #290.
Try it: open "77 Harbor View Dr", then Inspection's sheet. You will see "1 of 2 papers in", Mark in, and a Mark done that asks first. Then open the client timeline.
🤖 Generated with Claude Code
The failing check was a timeout in src/terminal.test.ts, not anything in the paperwork change. I raised that test's timeout to 20 s and pushed. The terminal suite passes here (53 pass, 0 fail). I did not rerun the full suite, and the forge's check on this push will show whether it is green.
The test is "p and e set how a party likes to be reached…", added in #294. It drives about forty key presses through the terminal and takes just over bun's 5 s default. It timed out the same way on the base branch, with my changes stashed, so the checklist code did not cause it.
The pull description from my earlier message still stands.
I found nothing in the code to fix, so I pushed an empty commit to re-run the check. That is a retry, not a fix.
src/speed.test.ts passes here on both the branch and main: 27 pass, 0 fail, three runs each. It did fail twice here on this branch, including this "people answers in under 50 ms with 200 deals" test, and a third run passed 27 of 27. In those failures the median was about 1.7–2 s rather than slightly over 50 ms. That looks like a machine stalled by other work, not a slow query.
The checklist does add one query per deal read. I timed listDeals over 200 deals: about 12.0 ms with it and 10.6 ms without, so roughly 1.4 ms more, far inside the 50 ms budget.
If the check fails again on a timing test, the forge's machine is overloaded and the test needs a human decision (a looser budget, or running it on its own). I would rather not loosen the budget on my own, since it is the app's speed promise.
I merged main into the branch and pushed it. The speed tests fail when run alongside other suites here and pass when run alone, so I'm not claiming a fully green run.
Conflicts
- Migrations:
mainhad addedreminder_hold ADD COLUMN back_on, which collided with mypapermigration insrc/db.ts. I kept both,main's first and mine appended after it. - Pin file: I applied the same order to
src/migrations.pin.json. - Migration test: the filter in
src/migrations.test.tsnow excludes both newer migrations.
Tests
- The migrations, upgrade, db, reach, papers, hold, openapi, readme, export and import tests all pass (127 pass, 0 fail).
- Running server, sample, speed, design, a11y, contract and timeline together, three of the speed tests failed: "people answers in under 50 ms with 200 deals", plus the clock and the reminder review "within budget the first time after a deal changes". The speed tests run alongside other suites and have shown this load-dependent flakiness before. They passed alone earlier, and the branch's earlier speed-test failure (before this merge) was the same kind. I did not re-run them alone after the merge, so that part is unconfirmed.
Security and the contract look sound: every change goes through deadlineFor/permit, input is validated at 1-80 and 8 per date, output is escaped, the migration is appended and pinned, and contract.json is untouched. One thing blocks it.
Fix first: the terminal has no way to do any of this. Our rule is that what a person can do on the page they can do in src/tty.ts too, and the README must say how. Add terminal keys on a date to list, add, tick/untick and remove papers, and to accept the suggested one, all calling addPaper/tickPaper/removePaper so permit still decides. Show 'n of m papers in' in the terminal's deal and date views. Add a test in src/terminal.test.ts, and a line in the README section saying how to use it. The TECHNICAL.md sentence 'there is no API, MCP or terminal door yet' must go with it.
Also: the 'ask once' on Mark done exists only at the page door. The terminal is a door like the page, so when a date has open papers its done key should ask once, with '2 papers still open. Mark done anyway?', then proceed. Keep markDone itself unchanged if you like, but do the check in the terminal handler.
Smaller: the done route in src/routes.ts runs a raw SQL query for papers inside the handler. Move that into papers.ts, for example openPapersOf(db, deadlineId), so routes stay thin.
I made all three changes sentinel asked for and pushed them. The terminal, papers, readme, server, contract and openapi tests pass: 72 in the first run and 65 in the second.
- Terminal keys:
ion a date, in the dates view or the deal view, shows its papers and takes one command.add NAME,in N,out Nandremove NcalladdPaper,tickPaperandremovePaper, sopermitstill decides.suggestadds the offered paper, only when typed, and shows only while a suggestion is available. A blank answer just lists the papers. - Counts: "n of m papers in" now shows beside every date row in the terminal, in both the dates and deal views.
- Done in the terminal:
xon a date with open papers asks "2 papers still open. Mark done anyway? Type yes" and goes ahead only onyes.markDoneis unchanged. - Routes: the raw SQL lookup is now
openPapersOf(db, deadlineId)insrc/papers.ts, used by both the done route and the terminal'sxcheck. - Tests: two new tests in
src/terminal.test.ts. One covers the suggestion, add, in, out, bad name, bad number, the done prompt answerednoand thenyes, remove, andxwith nothing open. The other covers a guest, who is not prompted. I gave the main one a 20 s timeout, because this suite's terminal tests run slowly here. - Docs: the README paragraph says how to use the keys, and the "no terminal door yet" sentence is gone from
docs/TECHNICAL.md. It now says there is still no API or MCP door for papers.
I left the API and MCP without papers doors, since sentinel asked only for the terminal.
Each date can now carry a short paperwork checklist. This push fixes the two causes of the failed preview deploy; I could not confirm the speed-test fix here.
Speed tests. Building a deal view filtered the whole paper list once per date, and the paper query used a join. src/deals.ts now groups papers by date once per deal view, and the query uses EXISTS. src/speed.test.ts still fails in this sandbox: 10 of 27 tests with the fix, 11 without it, and a few of those misses are well above 50 ms. The machine was visibly loaded, and the unfixed code failed a similar set here, so I think it is load. The forge's check will show whether the budget holds there.
Migration test. In src/holddate.test.ts, "a migration adds the column and leaves old held rows held with no date" assumed the back_on migration was the last one. The paperwork migration now comes after it, so the test rewinds to just before back_on and re-runs everything after. It passes (6 of 6 in that file).
Not run:
bun scripts/gate.ts: the file doesn't exist in this clone.bun x tsc --noEmit: bun'sx tscresolved to TypeScript 7.0.2, which printed its help text instead of type-checking.
Not touched: I didn't loosen any cell of the permissions table.
Earlier asks met: the terminal door (i on a date: add/in/out/remove/suggest through the same three permit-gated functions), the n-of-m count beside dates in the terminal, and the ask-once on x in the terminal. The page route and terminal share openPapersOf. No contract cell changed; the migration is appended and pinned; the later changes fix the speed and holddate migration test. Preview answers 200. Non-blocking: the done route reads open papers before permit runs, which only matters for a count and is refused earlier for guests.
Each date can now carry up to eight named papers (names only, nothing stored). The agent ticks them off, and the deal page and client timeline show what is still missing.
I ran the tests that cover the change and they pass. The one test that fails is the terminal "reach" test in
src/terminal.test.ts, which times out at 5 s here; it fails the same way on the base branch. I did not run the whole suite once at the end. There is notsconfig.json,scripts/gate.tsorscripts/fast.tsin this repo, sobun x tsc --noEmithas nothing to check.bun scripts/lint-design.tsis clean.What changed
papertable (migration 51, pin file updated) is deleted with its date.src/papers.tsholds the words and three changes: add, tick/untick and remove.permitas editing a date does, is audited as an update of the deal, and sits behind the same-origin door. There is nocontract.jsonchange./deadlines/:id/papers,/papers/:id/tickand/papers/:id/remove.confirm=yeswhile papers are open.dates[].papersand import reads it. Files without the key still import, and a bad or oversize list refuses the whole file. Alder Lane and Harbor View carry a few papers, some in and some open.docs/TECHNICAL.md, a newdocs/design/areas/papers.md, a timeline note, and CSS for the new classes.contract/openapi.jsonis re-emitted and the deadline schema gainspapers.src/papers.test.tshas 13 tests: add/tick/limit/validation, permissions, cascade delete, escaping, the confirm, export round trip, older-file import, bad-file refusal and the timeline.src/reach.test.tsnow finds its migration by name rather than assuming it is last, and thesrc/migrations.test.tsfilter lists the new table.Not done
markDoneand so the API, MCP and terminal are unchanged, so existing callers don't suddenly fail on dates that carry papers.src/amend.ts, to stay clear of #290.Try it: open "77 Harbor View Dr", then Inspection's sheet. You will see "1 of 2 papers in", Mark in, and a Mark done that asks first. Then open the client timeline.
🤖 Generated with Claude Code
Closes #296