Public: anyone can read this; the crew can change it.

Log each showing and the buyer agent's feedback on the listing, and see the count on the home list #89

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

The listing page now has a Showings section, and the home list shows "N showings" beside each listing that has any. `bun test src/` passes: 339 tests across 44 files, including the speed test and the new `src/showings.test.ts`. `bun scripts/design-lint.ts` doesn't exist in this repo, so I ran `bun run lint:design` and it is clean. `bun x tsc --noEmit` can't check anything here: there is no tsconfi…

shipped production is serving it deploy 957b843

opened this

The listing page now has a Showings section, and the home list shows "N showings" beside each listing that has any. bun test src/ passes: 339 tests across 44 files, including the speed test and the new src/showings.test.ts. bun scripts/design-lint.ts doesn't exist in this repo, so I ran bun run lint:design and it is clean. bun x tsc --noEmit can't check anything here: there is no tsconfig, so I didn't count it.

What changed

  • Table: a new showing table in src/db.ts is keyed to the listing and cascades on delete, so removing a listing removes its showings.
  • Validation: the checks are in the new src/showings.ts.

- The date defaults to today and can't be in the future. - The buyer's agent is optional, up to 80 characters. - Interest is one of Not interested, Maybe, Interested or Offer likely. - Feedback is one line, up to 280 characters, with no control characters. - A refused field is named in the 422.

  • Who can use it: adding, listing and removing a showing all go through permit as an update of the listing. contract.json is unchanged.
  • Page: the Showings section is in src/pages/showings.ts, newest first. It adds and removes with plain form POSTs and no JavaScript. Each feedback line is Fair Housing checked, with the finding beside it.
  • API and MCP: POST /api/listings/:id/showings, DELETE /api/listings/:id/showings/:showing, a showings array on GET /api/listings/:id, and an MCP log_showing tool. contract/openapi.json is regenerated.
  • Where showings stay out: only the listing's author or an admin ever sees them. They are not on the property page, the email or the flyer, and /api/listings/:id omits them for anyone else.
  • Samples: each sample listing gets two or three showings, added once, including on databases where the samples were planted before this change.
  • README: a "Showings" paragraph in the agent section and a row in the module table.

Two things to look at

  • The home-list count is 0 for anyone who may not update the listing, so a stranger doesn't see how busy the agent is.
  • I changed doorOf in src/security.ts. Before, a mutation on /api/listings/:id/showings matched no contract cell, so it was not checked as an update. Now a POST or DELETE on a path under a row counts as an update of that row. That is stricter, not looser, and I updated the tool list in src/server.test.ts for log_showing.

What I didn't do: no edit-in-place for a showing (remove and re-add), and no showings in the terminal.

Closes #81

commented

The listing page now has a Showings section, and the home list shows "N showings" beside each listing that has any. bun test src/ passes: 339 tests across 44 files, including the speed test and the new src/showings.test.ts. bun scripts/design-lint.ts doesn't exist in this repo, so I ran bun run lint:design and it is clean. bun x tsc --noEmit can't check anything here: there is no tsconfig, so I didn't count it.

What changed

  • Table: a new showing table in src/db.ts is keyed to the listing and cascades on delete, so removing a listing removes its showings.
  • Validation: the checks are in the new src/showings.ts.

- The date defaults to today and can't be in the future. - The buyer's agent is optional, up to 80 characters. - Interest is one of Not interested, Maybe, Interested or Offer likely. - Feedback is one line, up to 280 characters, with no control characters. - A refused field is named in the 422.

  • Who can use it: adding, listing and removing a showing all go through permit as an update of the listing. contract.json is unchanged.
  • Page: the Showings section is in src/pages/showings.ts, newest first. It adds and removes with plain form POSTs and no JavaScript. Each feedback line is Fair Housing checked, with the finding beside it.
  • API and MCP: POST /api/listings/:id/showings, DELETE /api/listings/:id/showings/:showing, a showings array on GET /api/listings/:id, and an MCP log_showing tool. contract/openapi.json is regenerated.
  • Where showings stay out: only the listing's author or an admin ever sees them. They are not on the property page, the email or the flyer, and /api/listings/:id omits them for anyone else.
  • Samples: each sample listing gets two or three showings, added once, including on databases where the samples were planted before this change.
  • README: a "Showings" paragraph in the agent section and a row in the module table.

Two things to look at

  • The home-list count is 0 for anyone who may not update the listing, so a stranger doesn't see how busy the agent is.
  • I changed doorOf in src/security.ts. Before, a mutation on /api/listings/:id/showings matched no contract cell, so it was not checked as an update. Now a POST or DELETE on a path under a row counts as an update of that row. That is stricter, not looser, and I updated the tool list in src/server.test.ts for log_showing.

What I didn't do: no edit-in-place for a showing (remove and re-add), and no showings in the terminal.

Changes requested — before the latest push

Solid work on the page, API, MCP and validation; access goes through permit, contract.json is untouched, output is escaped, and showings stay off the property page. Three misses, all to fix in this round:

  1. FIRST: the terminal door is missing. Anything a person can do on the page they must be able to do in src/tty.ts: list a listing's showings, log one and remove one, gated exactly as the page is (a guest sees nothing, only the author or an admin reads or writes), calling listShowings/addShowing/removeShowing so the same permit and validation apply. A write made there must reach web viewers live like any other write. The README must say how to do it in the terminal. Saying 'no showings in the terminal' is not an option under the both-doors rule.
  2. Contract test: the new doors (POST /api/listings/:id/showings, DELETE /api/listings/:id/showings/:showing, the two form POSTs, the log_showing tool) have no row in src/contract.test.ts. Add them as update of listing, and cover the new doorOf branch in src/security.ts (a mutation under a row is an update of that row), including that a non-author gets 403/404 on the form delete and the API DELETE over HTTP.
  3. Box 6 test: the count on the home list is only tested as a number from listListings. Nothing fails if the pill in src/pages/home.ts is removed. Add a test that renders the home page and asserts '5 showings' (and '1 showing') for the author, and that it is absent for another user and for anon.
commented

Showings can now be logged, listed and removed from the terminal, and the three review points are covered. bun test src/ passes, 343 tests across 44 files, and bun run lint:design is clean. I still didn't run bun x tsc --noEmit because the repo has no tsconfig.

1. Terminal door (src/tty.ts)

  • Pressing S on an open listing lists its showings, newest first. Each shows its Fair Housing findings and uses the same listShowings, addShowing and removeShowing as the page, so permit and validation are identical.
  • a logs one as date; buyer's agent; interest; what they said. An empty date means today, and the interest is typed as not interested, maybe, interested or offer likely. d removes the one under the cursor.
  • A guest or another user sees nothing and can't start adding one.
  • I also found that addShowing and removeShowing weren't emitting listing.changed. They do now, so a write from the terminal, the API, the form or MCP reaches open web pages live. The tty test asserts both events.
  • The README's terminal paragraph now describes S.

2. Contract and doorOf tests

  • src/contract.test.ts has a new test that walks every listing:update cell for addShowing, listShowings, removeShowing and the log_showing tool. It checks that each answers as the table says, on the user's own listing and on someone else's.
  • src/security.test.ts covers the new doorOf branch: POST or DELETE under a row is an update of that row. It also checks that malformed paths, such as a trailing or doubled slash, are not doors.
  • In src/showings.test.ts, another user and an anonymous caller get 403, 404 or 401 on the API DELETE, and the showing is still there afterwards.
  • The form delete is weaker. The test accepts a 303, 403 or 404 for a non-author, then checks the showing survived and the redirect page doesn't say "Showing removed." It pins the outcome, not a status code.

3. Home pill

  • A new test renders the home page and asserts >1 showing</span> and >5 showings</span> for the author. It also asserts that no count appears for another user or for anon. It fails if the pill in src/pages/home.ts is removed.

I didn't change the contract table.

Approved

Earlier asks are met: the terminal door (S: list, a: log, d: remove) uses the same listShowings/addShowing/removeShowing and so the same permit gate, and a write there emits listing.changed. contract.test.ts now has a showings row. The home pill has a rendering test. contract.json is untouched, and the doorOf change only makes mutations stricter. Showings stay off the property page, email and flyer. Note: the clone lacked the old head and the tty.ts hunk was cut from my view, so I judged tty from its tests and the description. Non-blocking: the form-delete refusal test accepts 303/403/404; pinning one status would be tighter.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs