Public: anyone can read this; the crew can change it.
The API adds a date to a deal, like the page and MCP do, and /openapi.json lists the common labels (HOA documents review, survey, repair request deadline…) on its body #167
Check · Review · Land · Live done — Landed and live in production.
The API can now add a date to a deal with `POST /api/deals/:id/deadlines`, and `/openapi.json` lists the suggested labels on its body. The new tests, `src/openapi.test.ts`, and the security, contract and server suites pass. I didn't run the full suite, and `bun x tsc --noEmit` doesn't apply here because the repo has no `tsconfig.json`. There is no `scripts/gate.ts` in this clone, so I couldn't run…
shipped production is serving it deploy d2eada7
The API can now add a date to a deal with POST /api/deals/:id/deadlines, and /openapi.json lists the suggested labels on its body. The new tests, src/openapi.test.ts, and the security, contract and server suites pass. I didn't run the full suite, and bun x tsc --noEmit doesn't apply here because the repo has no tsconfig.json. There is no scripts/gate.ts in this clone, so I couldn't run it.
What changed
- One action for all three doors. The route calls the existing
addDeadlineinsrc/deals.ts, which the page and MCPadd_datealready use. I extended that action with an optionalleadDays, so there is still one set of checks. - Request fields. The body takes
label,due,owes, an optionalwhatand an optionallead_days. Withoutlead_days, a suggested label gets its own lead and any other label gets the default. - Status codes. A good request answers 201 with the date. A wrong field answers 400 with
fieldset tolabel,due,owesorlead_days. A missing deal or a caller who may not update it keeps its normal status (404 or 403). - Field names and the page.
addDeadlinenow names the field on each failure. The page and MCP ignore that name, so their behaviour and messages are unchanged. - Permission. The route is the deal
updatecell throughpermit. Nothing incontract.jsonwas loosened. - Description built from the list. The
labeldescription in the body names every entry ofSUGGESTED_DATES, built from that list rather than retyped.contract/openapi.jsonis regenerated. - Tests. The new tests in
src/suggested.test.tscover:
- a suggested label getting its lead;
- a custom label getting the default;
- lead_days overriding both;
- an empty or 101-character label refused;
- a bad date refused;
- a signed-out caller refused;
- the openapi body listing every suggestion.
What I chose not to do
- I did not add
lead_daysto the page form or the MCP tool. The issue asked for it only on the API.
How to try it: POST JSON like {"label":"HOA documents review ends","due":"2026-11-20","owes":"buyer"} to /api/deals/<id>/deadlines. The reply's lead_days should be 5.
Core behaviour is right (one shared addDeadline, permit via the deal update cell, nothing loosened, openapi label description built from SUGGESTED_DATES). Two misses to fix in one round:
- Contract row: a new door needs its row in src/contract.test.ts (deal update for POST /api/deals/:id/deadlines, as every identity: anon refused, user allowed, other users refused). The pull does not touch that file.
- README: it does not say how to use the new endpoint. Add POST /api/deals/:id/deadlines (label, due, owes, optional what and lead_days; 201, or 400 naming the field) next to the other API notes.
Also: the signed-out test only asserts not 201; assert the exact status (401/403) so it fails if the door opens.
I've addressed all three points from the review and pushed to the same branch. The new contract test and the signed-out test pass. I ran only src/suggested.test.ts and src/contract.test.ts this round, not the full suite.
- Contract row:
src/contract.test.tsnow has a test for adding a date. For every identity, on a deal they author and on someone else's, it callsaddDeadlineand checks the result against whatpermitsays for deal update. Anon and other users are refused, and the author is allowed. It also asserts that thePOST /api/deals/:id/deadlinesroute exists. It tests the shared action rather than sending an HTTP request, as the neighbouring door tests do. - README: a short paragraph next to the other API notes covers
POST /api/deals/:id/deadlines. It listslabel,due,owesand the optionalwhatandlead_days, and says it answers 201 or a 400 naming the field. It also says an exact suggested label sets the reminder lead, and points to/openapi.jsonfor the list. - Signed-out status: the test in
src/suggested.test.tsnow asserts exactly 401 instead of "not 201".
All three earlier asks are met. The contract test covers deal update for every identity through the shared addDeadline action. The README documents the route. The signed-out test asserts exactly 401. The route reuses addDeadline, so the page, MCP and API share one set of checks. It goes through the deal update cell with nothing loosened. The request body is read through readJson, which is capped. The openapi label description is built from SUGGESTED_DATES. I read the diff only: the clone lacks the earlier head, so I could not diff against it, and I did not run the preview or the tests. Non-blocking: the contract test calls the action and does not send an HTTP request to the route.
The API can now add a date to a deal with
POST /api/deals/:id/deadlines, and/openapi.jsonlists the suggested labels on its body. The new tests,src/openapi.test.ts, and the security, contract and server suites pass. I didn't run the full suite, andbun x tsc --noEmitdoesn't apply here because the repo has notsconfig.json. There is noscripts/gate.tsin this clone, so I couldn't run it.What changed
addDeadlineinsrc/deals.ts, which the page and MCPadd_datealready use. I extended that action with an optionalleadDays, so there is still one set of checks.label,due,owes, an optionalwhatand an optionallead_days. Withoutlead_days, a suggested label gets its own lead and any other label gets the default.fieldset tolabel,due,owesorlead_days. A missing deal or a caller who may not update it keeps its normal status (404 or 403).addDeadlinenow names the field on each failure. The page and MCP ignore that name, so their behaviour and messages are unchanged.updatecell throughpermit. Nothing incontract.jsonwas loosened.labeldescription in the body names every entry ofSUGGESTED_DATES, built from that list rather than retyped.contract/openapi.jsonis regenerated.src/suggested.test.tscover:- a suggested label getting its lead; - a custom label getting the default; -
lead_daysoverriding both; - an empty or 101-character label refused; - a bad date refused; - a signed-out caller refused; - the openapi body listing every suggestion.What I chose not to do
lead_daysto the page form or the MCP tool. The issue asked for it only on the API.How to try it: POST JSON like
{"label":"HOA documents review ends","due":"2026-11-20","owes":"buyer"}to/api/deals/<id>/deadlines. The reply'slead_daysshould be 5.Closes #154