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

Mark a listing under contract or sold, and write the 'Just sold' post and email from it #45

Merged opened this

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

Merged

A listing can now be marked Active, Under contract or Sold (with an optional sold price). Under contract and Sold add a status post and a status email. I pushed the change, and `bun test` passes (171 tests). I could not run `tsc --noEmit` here because the repo has no `tsconfig.json`, so types are unchecked.

shipped production is serving it deploy 957b843

opened this

A listing can now be marked Active, Under contract or Sold (with an optional sold price). Under contract and Sold add a status post and a status email. I pushed the change, and bun test passes (171 tests). I could not run tsc --noEmit here because the repo has no tsconfig.json, so types are unchecked.

What changed

  • Status and price: a listing has a status and an optional sold price. A migration adds both columns, and validateStatus in src/listing.ts checks them. A sold price is accepted only with Sold, in whole dollars from $1,000.
  • One action, four doors: setStatus in src/actions.ts asks permit for the listing's update cell, so only the author or an admin may change it. It is audited, and no cell in contract.json changed. It is reached from:

- the listing page: a status box and price field with a "Set status" button, - the API: PATCH /api/listings/:id with {status, sold_price}, - the MCP: a new set_status tool, - the terminal: the s key, typing sold 752000, under contract or active.

  • New assets: there are two new kinds, social_status (a Social tab post) and email_status (an Email tab email, subject "Just sold — 412 Alder Lane"). They are written in the listing's tone, Fair Housing checked, and each needs its own approval.
  • Approval counts: the total is counted from the stored assets, so it reads "0 of 8 approved" once a listing is announced and "of 6" when active.
  • Existing assets untouched: setting the status writes only the two status assets, so your edits and approvals on the other six stay. Saying what is already true changes nothing. Changing the sold price rewrites the announcement and clears its approval. A retone or "Edit facts" rewrites everything, status assets included.
  • Pill and band: the home list and listing head show a status pill beside the approval pill when the status isn't Active. The flyer gets a "Sold at $…" or "Under contract" band, and its badge reads "Just sold" or "Under contract" instead of "Just listed". The new CSS classes are named in docs/design/areas/status.md.
  • Sample: 88 Meridian Street (the condo) is seeded as Sold at $752,000.
  • Tests and docs: src/status.test.ts covers validation, the words in every tone, the approvals and counts, who may change it, the band, the sample, and the MCP, terminal, API and page doors. The walkthrough has a new "Mark it sold" step with a matching README step. The README, contract/openapi.json and the existing tests that assumed six kinds are updated.

Choices to check

  • I made the condo the Sold sample rather than 412 Alder Lane (the issue's example subject), because Alder Lane's open-house sample and its walkthrough steps depend on it staying live.
  • The two status assets are labelled "Status post" and "Status email" in the editor, whatever the status.
  • I did not add status to the launch form: a listing always launches Active.
  • Open houses are not mentioned in the status assets.

To try it Open any listing you own, pick Sold with a price, press "Set status", and approve the two new assets. Or run bun test src/status.test.ts src/walkthrough.test.ts.

Closes #36

commented

A listing can now be marked Active, Under contract or Sold (with an optional sold price). Under contract and Sold add a status post and a status email. I pushed the change, and bun test passes (171 tests). I could not run tsc --noEmit here because the repo has no tsconfig.json, so types are unchecked.

What changed

  • Status and price: a listing has a status and an optional sold price. A migration adds both columns, and validateStatus in src/listing.ts checks them. A sold price is accepted only with Sold, in whole dollars from $1,000.
  • One action, four doors: setStatus in src/actions.ts asks permit for the listing's update cell, so only the author or an admin may change it. It is audited, and no cell in contract.json changed. It is reached from:

- the listing page: a status box and price field with a "Set status" button, - the API: PATCH /api/listings/:id with {status, sold_price}, - the MCP: a new set_status tool, - the terminal: the s key, typing sold 752000, under contract or active.

  • New assets: there are two new kinds, social_status (a Social tab post) and email_status (an Email tab email, subject "Just sold — 412 Alder Lane"). They are written in the listing's tone, Fair Housing checked, and each needs its own approval.
  • Approval counts: the total is counted from the stored assets, so it reads "0 of 8 approved" once a listing is announced and "of 6" when active.
  • Existing assets untouched: setting the status writes only the two status assets, so your edits and approvals on the other six stay. Saying what is already true changes nothing. Changing the sold price rewrites the announcement and clears its approval. A retone or "Edit facts" rewrites everything, status assets included.
  • Pill and band: the home list and listing head show a status pill beside the approval pill when the status isn't Active. The flyer gets a "Sold at $…" or "Under contract" band, and its badge reads "Just sold" or "Under contract" instead of "Just listed". The new CSS classes are named in docs/design/areas/status.md.
  • Sample: 88 Meridian Street (the condo) is seeded as Sold at $752,000.
  • Tests and docs: src/status.test.ts covers validation, the words in every tone, the approvals and counts, who may change it, the band, the sample, and the MCP, terminal, API and page doors. The walkthrough has a new "Mark it sold" step with a matching README step. The README, contract/openapi.json and the existing tests that assumed six kinds are updated.

Choices to check

  • I made the condo the Sold sample rather than 412 Alder Lane (the issue's example subject), because Alder Lane's open-house sample and its walkthrough steps depend on it staying live.
  • The two status assets are labelled "Status post" and "Status email" in the editor, whatever the status.
  • I did not add status to the launch form: a listing always launches Active.
  • Open houses are not mentioned in the status assets.

To try it Open any listing you own, pick Sold with a price, press "Set status", and approve the two new assets. Or run bun test src/status.test.ts src/walkthrough.test.ts.

Changes requested — before the latest push

Change requested, one thing to fix first: the sold sample does not show in the preview (or on any existing database).

  1. README step 8 says '88 Meridian Street is already sold', and the sample test passes on a fresh database. But seed() only runs markStatus when the seeded row is newly inserted. On this pull's preview, and on production once it lands, the sample listings were seeded long ago, so / shows Meridian as '0 of 6 approved' with no status pill and no 'Just sold' assets. The meeting would not see the feature the README points to. Fix: mark the sample sold on databases that already have it. For example, in seed, find the sample by address and call markStatus when it is still active and was authored by sample. Add a test that seeds, resets that listing to active (or seeds an older shape), re-seeds, and expects it sold.
  1. src/tty.ts: the new statusToLine/statusFromLine block was inserted between the doc comment '/** In the one-line edit box, \n typed as two characters is a line break. */' and toLine/fromLine, which orphans that comment. Move the status helpers above it or below fromLine.

Checked and fine: setStatus goes through listingFor and permit for the update cell; no contract.json cell is loosened; the page, API, MCP and terminal all reach the one action; the status form is shown to the agent only; the migration is additive and CHECK-constrained; tests cover validation, tones, Fair Housing, approvals kept, the four doors and the sample. I could only read the diff up to the middle of src/tty.ts; I did not review the rest of that file.

commented

Both review points are fixed and pushed (660e4c1). bun test passes (171 tests); tsc --noEmit still can't run here because the repo has no tsconfig.json.

  • Sold sample on existing databases: seed() now marks 88 Meridian Street sold on databases that already had it. It does this once, using a sample-condo-sold row in the seeded table. It only touches a listing that is authored by sample, matches the Meridian address, and is still active. I made it once-only because marking it on every boot would undo an admin who sets the sample back to active on purpose. Fresh databases are covered by the same pass.
  • New test: in src/status.test.ts I reset Meridian to the pre-feature shape (active, no status assets, marker row removed) and re-seeded. It comes back Sold at $752,000 with 8 assets. After an admin sets it to active, a further seed() leaves it active.
  • Orphaned comment: in src/tty.ts the \n line-break comment is back directly above toLine/fromLine, and the status helpers sit above it.
Approved

Both earlier asks are met: the sold sample is now applied once to existing databases (seed marker plus a test that resets and re-seeds), and the tty helper comment is back above toLine/fromLine. setStatus goes through listingFor/permit and is audited, and the body is validated at the boundary. No contract cell is loosened. All four doors (page, API, MCP, tty) reach it, and each has a test. The openapi and README are updated. Non-blocking: the diff I was given was cut off inside src/tty.ts, so I read the tty status path through status.test.ts rather than line by line. This clone does not have the head commit, so I could not diff against ba85fe7.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs