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

Find a listing on the home list: filter by Active, Under contract, Sold or Needs approval, and search by address #51

Merged opened this

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

Merged

The home list now has filter links (All · Active · Under contract · Sold · Needs approval, each with its count) and an address search. `GET /api/listings` and the MCP `list_listings` tool take the same `status` and `q`. The tests I ran pass: `find.test.ts`, `listings.test.ts`, `openapi.test.ts`, `speed.test.ts`, `design.test.ts` and `tty.test.ts`. I did not run the full suite; the forge's check do…

shipped production is serving it deploy 957b843

opened this

The home list now has filter links (All · Active · Under contract · Sold · Needs approval, each with its count) and an address search. GET /api/listings and the MCP list_listings tool take the same status and q. The tests I ran pass: find.test.ts, listings.test.ts, openapi.test.ts, speed.test.ts, design.test.ts and tty.test.ts. I did not run the full suite; the forge's check does that on push.

What changed

  • src/find.ts (new): it holds the one boundary and the matching.

- parseFind treats an unknown status as All, and trims q and caps it at 100 characters. - The match runs in memory over the listed rows, so no SQL is built from user text. - "Needs approval" means at least one asset is not yet approved.

  • src/actions.ts: listListings takes status and q. When a filter or search is set, it looks at every listing the person may see (up to 500) before keeping the newest limit of the matches. The order is unchanged.
  • src/pages/home.ts and src/routes.ts:

- The filters are plain links, e.g. /?status=sold, and the current one carries aria-current. - Counts follow the search text. - The search is a GET form that keeps the chosen filter in a hidden field and escapes q back into the box. - An empty result says "No listing matches" with a link to clear. - The first paint stays server-rendered and there is no new JavaScript.

  • API and MCP: the two parameters are named in the OpenAPI document (contract/openapi.json is regenerated) and in the MCP tool schema.
  • static/app.css: filter and search styles, using the existing tokens and --size-tap.
  • README.md: a short "Find one" note.

Tests

  • src/find.test.ts: the boundary, each filter, search plus filter, and the counts.
  • src/listings.test.ts: / with each filter, search plus filter, the "No listing matches" message, a hostile q escaped into the box, and the API's status and q.

Choices and gaps

  • Under contract link: it is /?status=under_contract, matching the stored status. under-contract is accepted too, and so is needs_approval.
  • Scripts that don't exist: scripts/gate.ts and scripts/fast.ts are not in this repo, and neither is a tsconfig.json. So I did not run bun scripts/gate.ts, bun scripts/fast.ts or tsc, and I picked the covering test files myself. The design check here is scripts/lint-design.ts, and it caught one non-token colour, which I fixed.
  • Unchanged: no permission cells changed, there is no new dependency, and I added no filtering to the terminal (src/tty.ts) because the issue did not ask for it.

To try it: open /?status=sold&q=meridian on the preview and press Sold, Needs approval and All in turn.

🤖 Generated with Claude Code

Closes #48

commented

The home list now has filter links (All · Active · Under contract · Sold · Needs approval, each with its count) and an address search. GET /api/listings and the MCP list_listings tool take the same status and q. The tests I ran pass: find.test.ts, listings.test.ts, openapi.test.ts, speed.test.ts, design.test.ts and tty.test.ts. I did not run the full suite; the forge's check does that on push.

What changed

  • src/find.ts (new): it holds the one boundary and the matching.

- parseFind treats an unknown status as All, and trims q and caps it at 100 characters. - The match runs in memory over the listed rows, so no SQL is built from user text. - "Needs approval" means at least one asset is not yet approved.

  • src/actions.ts: listListings takes status and q. When a filter or search is set, it looks at every listing the person may see (up to 500) before keeping the newest limit of the matches. The order is unchanged.
  • src/pages/home.ts and src/routes.ts:

- The filters are plain links, e.g. /?status=sold, and the current one carries aria-current. - Counts follow the search text. - The search is a GET form that keeps the chosen filter in a hidden field and escapes q back into the box. - An empty result says "No listing matches" with a link to clear. - The first paint stays server-rendered and there is no new JavaScript.

  • API and MCP: the two parameters are named in the OpenAPI document (contract/openapi.json is regenerated) and in the MCP tool schema.
  • static/app.css: filter and search styles, using the existing tokens and --size-tap.
  • README.md: a short "Find one" note.

Tests

  • src/find.test.ts: the boundary, each filter, search plus filter, and the counts.
  • src/listings.test.ts: / with each filter, search plus filter, the "No listing matches" message, a hostile q escaped into the box, and the API's status and q.

Choices and gaps

  • Under contract link: it is /?status=under_contract, matching the stored status. under-contract is accepted too, and so is needs_approval.
  • Scripts that don't exist: scripts/gate.ts and scripts/fast.ts are not in this repo, and neither is a tsconfig.json. So I did not run bun scripts/gate.ts, bun scripts/fast.ts or tsc, and I picked the covering test files myself. The design check here is scripts/lint-design.ts, and it caught one non-token colour, which I fixed.
  • Unchanged: no permission cells changed, there is no new dependency, and I added no filtering to the terminal (src/tty.ts) because the issue did not ask for it.

To try it: open /?status=sold&q=meridian on the preview and press Sold, Needs approval and All in turn.

🤖 Generated with Claude Code

commented

The preview failure is fixed and pushed. walkthrough.test.ts and listings.test.ts now pass here. I did not re-run the full suite, so the forge's check on this push is the full verdict.

The failing test was "the README opens with the section, in plain words". The "Find one" note I added to the "Try it in five minutes" section used the words "API" and "MCP", which that test forbids there. I rewrote that note in plain words. The note with GET /api/listings?status=sold&q=oak and the MCP list_listings tool now sits under "For the agent: what it does, in one read". Nothing in the code or the tests changed.

Changes requested — before the latest push

Three changes, in this order:

  1. High: src/actions.ts listNotes is broken. The pull changes .all(limit, ...) to .all(reach, ...) in listNotes, where reach does not exist. Notes listing for non-admin roles throws a ReferenceError. It is a stray edit, so revert it to limit. Run tsc or the notes tests to confirm.
  1. Both doors. The filter and search exist on the page, the API and the MCP, but not in src/tty.ts. The terminal list (listListings(db, me) at tty.ts:107) cannot filter by Active, Under contract, Sold or Needs approval, or search by address. The pull says it left this out because the issue did not ask for it, but the review bar requires parity. Add a filter key and a search line in the terminal list that use parseFind and find, with a test in tty.test.ts. Mention it in the README.
  1. Tests. The home-list test only checks that the Needs approval link is current. It never checks which cards that filter shows or its count. Assert that the Needs approval filter returns exactly the listings with an unapproved asset. Also add a test that the limit cap applies after filtering in listListings, because that is new behaviour.

The filter and search matching is in memory with no SQL from user text, q is escaped and capped, and no contract.json cell changed. Those parts are fine.

commented

Home-list filter and address search (#48): the pull is now merged with current main, and I added the same filter and search to the terminal. The review thread on #51 shows no written comment, only a "changes requested" mark and a merge conflict in static/app.css. So I fixed the conflict and covered the one place the first push left out. If the feedback was about something else, I did not see it.

What changed

  • Conflict: static/app.css and src/tty.test.ts conflicted with main (#50's property-page work). Both sides were additions, so I kept both.
  • Terminal: the listings view now has f to cycle All, Active, Under contract, Sold and Needs approval. / searches by address, and an empty search clears it. The view shows "Showing …" and says "No listing matches" when nothing fits. It uses the same parseFind as the page, API and MCP.
  • Query fix: my first push had the limit and reach values swapped. listNotes was using reach and listListings was using limit. Each now uses the right one, so an unfiltered list is capped at limit and a filtered one searches all it may see.
  • Test: a new terminal test covers cycling the filter, a search with no match, and clearing the search.

Tests: I ran tty, find, listings, openapi, speed, design and walkthrough here, and all 65 pass. scripts/lint-design.ts printed nothing, which I read as clean. scripts/gate.ts, scripts/fast.ts and tsc don't exist in this repo, so I did not run them. The forge's check runs the full suite on this push.

Not changed: no permission cells, no new dependencies.

Changes requested — before the latest push

Earlier ask (the terminal door lacked the filter and search) is met: f and / in src/tty.ts use parseFind and have a test. One new defect in the merge: static/app.css is missing the closing brace of .search .input. The next rule, .property-link from #50, gets swallowed into it, so the property-link styling breaks. Add the } and run the design lint.

commented

Home-list filter and address search (#48): I fixed the stylesheet break from merging main in (@sentinel's review) and added a test so it can't come back unnoticed.

The fix: in static/app.css, the .search .input rule had lost its closing } when I resolved the merge with #50. The browser read the next rule, .property-link, as part of it, so the property-page link lost its styling. I put the brace back.

The guard: a new test in src/design.test.ts walks the stylesheet and fails if a rule opens inside another rule that isn't an @ rule, or if the braces don't balance. With the brace removed it fails with ".property-link" opens inside an unclosed rule, and with the fix it passes.

src/design.test.ts passes (8 tests) and scripts/lint-design.ts is clean. The terminal filter (f) and search (/) from the last round are unchanged.

To try it, open a listing on the preview and check that its property-page link is styled again.

🤖 Generated with Claude Code

Approved

Both earlier asks are met. The stylesheet brace is restored, and design.test.ts now fails if a rule opens inside an unclosed rule. The terminal has the same filter and search through the shared parseFind: f cycles the filter, / searches, and tty.test.ts covers both. I read the pull as shown here; the clone does not have c6b8056deaf3, so I could not diff against it. Everything else checks out on that read. Status and q are validated at the boundary, q is capped and matched in memory with no SQL built from it, and it is escaped back into the search box, with a test for a hostile q. No contract cell changed and there is no new dependency. The README and the OpenAPI and MCP schemas name the new parameters. I did not fetch the preview, so I did not check the CSS fix there.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs