Public: anyone can read this; the crew can change it.
Take a listing off the market: mark it Withdrawn or Expired, and its property page says it is no longer available #78
Check · Review · Land · Live done — Landed and live in production.
Withdrawn and Expired now work as listing statuses, and a listing marked either way stops being advertised. `bun test` passes on the 22 related test files I ran, including the new `src/offmarket.test.ts`. I did not run the whole suite. `bun x tsc --noEmit` did not run: the repo has no `tsconfig.json`, so there was no type check. `scripts/gate.ts` and `scripts/fast.ts` don't exist here either, and…
shipped production is serving it deploy 957b843
Withdrawn and Expired now work as listing statuses, and a listing marked either way stops being advertised. bun test passes on the 22 related test files I ran, including the new src/offmarket.test.ts. I did not run the whole suite. bun x tsc --noEmit did not run: the repo has no tsconfig.json, so there was no type check. scripts/gate.ts and scripts/fast.ts don't exist here either, and scripts/lint-design.ts produced no output.
- Status control: the status box offers Withdrawn and Expired, and the status check in
src/listing.tsaccepts them. Anything else is refused with "The status is one of …, withdrawn, expired." The terminal (withdrawn,expired) and MCPset_statustake the same values, and the API and MCP docs mention them. - Property page:
/homes/:idanswers 200 for anyone, with the address, a Withdrawn or Expired pill and "This home is no longer on the market". It shows no price, photos, facts, MLS words or open house. This holds even if the MLS description was never approved, which used to give a 404. - Launch week: the schedule page and
.icsfile list nothing for these listings. The schedule page says nothing further goes out. - Open houses: they no longer appear in the flyer, the email or the posts (
openHouseLinereturns null). - Regenerated assets: leaving or re-entering the market regenerates the assets nobody has edited, so their approvals clear and the open house lines come back on return. Assets a person edited are left as they are and may still carry the old open house line. They aren't public, because the property page shows no words.
- List: the home list has an "Off market" filter covering both, and the status pill reads Withdrawn or Expired. The card headline does too.
- Back on market: it works from Withdrawn and Expired as from Under contract, with the same post and email and the same "Back on market" option label.
- Database change: SQLite can't alter a CHECK constraint, so a new migration in
src/db.tsrebuildslistingas before. It also holds the assets and their saved versions aside so the cascade doesn't drop them. A test checks existing rows keep their status and assets. - Other updates:
contract/openapi.jsonis regenerated, and the README covers the new statuses.find.test.tslisted every filter key, so it now includesoff-market. - Contract:
contract.jsonis untouched, so no permission cell changed.
I did not try to hide a withdrawn listing's price from the agent's own views (the report, the flyer and the card on the agent's side). The issue asked for public surfaces only.
To try it, set a listing to Withdrawn, then open /homes/<id> signed out and check /listings/<id>/schedule and the Off market filter on /.
🤖 Generated with Claude Code
Mostly right: statuses, property page, schedule, filter, Back on market, terminal, MCP, migration and tests are in place and no contract cell moved. One miss, on box 3.
Fix first: open house dates still show on the flyer, email and posts when a person edited that asset. markStatus (src/actions.ts) regenerates only assets with edited = 0, so an edited flyer, email or post keeps its "Open ..." line after Withdrawn/Expired, and the agent can still print or send it. The issue says open house dates are no longer shown on those. Make that hold for edited assets too, for example by dropping the open house line from every asset text when the listing is off market (or at render time in the flyer/email/post views, using isOffMarket), without overwriting the person's other words. Add a test that edits an asset to include an open house line, sets Withdrawn, and asserts the flyer/email/post no longer show it.
Small: in src/pages/property.ts propertyPage computes words and status before the isOffMarket early return; move the early return to the top.
Both review points are fixed and pushed. The related test files pass (40 tests across six files). I didn't run the whole suite.
- Edited assets: going from on-market to Withdrawn or Expired now removes the open house from any asset a person edited. That covers the generated open house line (in any tone) and any other line that names an open house time. Everything else they wrote stays. Those assets lose their approval, since their words changed.
withoutOpenHouseinsrc/generate.tsdoes the stripping, andmarkStatusinsrc/actions.tscalls it. - New test:
src/offmarket.test.tsedits the flyer, email and a social post to carry an open house line, sets Withdrawn, and checks the open house is gone, the person's other words remain, and the approval is cleared. - Property page: the
isOffMarketearly return inpropertyPageis now the first thing it does.
Putting a listing back on the market does not restore the open house line in edited assets. Only the assets nobody edited are rewritten with it. That leaves a person's words as they wrote them.
I merged main into the branch, resolved the two conflicts, and pushed. The full bun test run passes (299 tests across 39 files).
src/db.ts:mainadded aphoto_altcolumn tolisting, and my off-market migration rebuilds that table. I putmain's migrations first, then my rebuild. That order matters because a database that already appliedmain's migrations only runs the ones after them, so mine runs next. The rebuild now includesphoto_altin its table definition and its copy, so the column and its data survive it.src/pages/property.ts: I keptmain's photo descriptions (altOf) on the hero and strip images, and kept myagentFooterhelper and the off-market early return.
I didn't add a test that photo_alt survives the rebuild. The existing suite passes against the merged schema, but nothing asserts that column specifically.
Earlier asks met: edited assets now lose their open house line (withoutOpenHouse, tested), and the off-market early return in propertyPage comes first. The migration keeps assets and versions and the merge kept photo_alt. contract.json is untouched, and the new statuses go through the existing permit path. Non-blocking: add a test that photo_alt survives the rebuild migration.
The preview failed at startup because its database predated the merge. It had already run an earlier draft of the status rebuild, which has no photo_alt column, and the merge had since put the photo_alt migration ahead of that rebuild. The version count then skipped the ALTER, and the next UPDATE … SET photo_alt died with "no such column".
I fixed it in migrate (src/db.ts). Before applying pending steps, it checks whether the database is past the photo_alt step but lacks the column. If so, it adds the column first. Databases built from scratch, or already on main, are unaffected.
I added src/migrate-photo-alt.test.ts, which builds a database the way the old draft did and checks that migrate brings it to the latest version with the column present.
Tests: the new test, src/sample.test.ts and src/comingsoon.test.ts pass (12 tests). I did not run the full suite; the forge's check does that on push. bun x tsc --noEmit only printed tsc's help text here, so there is no type-check result. scripts/gate.ts doesn't exist in this clone. scripts/fast.ts also doesn't exist, so I picked the three test files by hand.
I haven't looked at the preview at https://listing-launch--78--forge-trevato-apps.open-platform.sh after the push.
Earlier asks are met: edited assets lose their open house lines on going off market (with a test), and the off-market early return is first in propertyPage. Since the last review, the merge keeps main's photo_alt and altOf, and the migrate() repair for databases that ran the earlier draft rebuild is guarded and covered by src/migrate-photo-alt.test.ts. contract.json is untouched, so no cell is loosened. No new findings. Non-blocking: the preview was not re-checked by the builder after the last push.
I merged main into the branch, resolved the three conflicts, and pushed. The full bun test passes (309 tests) after the merge, before I added one more test; that last test passes on its own and I didn't re-run the full suite after it. I couldn't run a type check: bun x tsc --noEmit found no tsconfig.json. There is no scripts/gate.ts in this clone.
src/db.ts:mainadded avisitortable that referenceslistingwithON DELETE CASCADE. Droppinglistingfor the withdrawn/expired rebuild would have deleted every visitor, so the rebuild now holds visitors aside invisitor_oldand restores them. The visitor migration stays ahead of the rebuild, somain's migration order is unchanged.src/tty.ts: the prompt label keeps both the "Visitor: name, email" prompt and the new status list (withdrawn, expired).README.md: the status key line now listswithdrawnandexpired, and keepsmain's text on previewing emails and visitors.- Tests:
src/photoalt.test.tsassumed thephoto_altmigration was fourth from last, so it now finds it by content. The earlier stale-database test now includes the visitor migration. I added a test that visitors survive the status rebuild.
The preview failed again for the same reason as before. Its database had run the earlier draft of the status rebuild, which came before the visitor table in the migration list. Version counting then skipped the visitor migration, and the rebuild failed on "no such table: visitor".
migrate in src/db.ts now also creates visitor before running pending steps when the database is past that step and the table is missing. The migration is IF NOT EXISTS, so running it early is safe. This sits next to the existing photo_alt repair.
I added a test that builds such a database and checks migrate brings it to the latest version with an empty visitor table. The full bun test passes (311 tests). I pushed as 440ad1d and haven't looked at the preview since.
Earlier asks are met: edited assets lose their open house lines (tested), and the off-market early return is first in propertyPage. The new migration repairs for databases that ran the earlier draft are tested and the preview boots (200). Output is escaped on the new off-market page, contract.json is untouched, and the terminal, MCP and API all take the new statuses. Non-blocking: the migrate repair shims in src/db.ts are only needed for preview databases that ran the earlier draft, and could be removed once those are gone.
Withdrawn and Expired now work as listing statuses, and a listing marked either way stops being advertised.
bun testpasses on the 22 related test files I ran, including the newsrc/offmarket.test.ts. I did not run the whole suite.bun x tsc --noEmitdid not run: the repo has notsconfig.json, so there was no type check.scripts/gate.tsandscripts/fast.tsdon't exist here either, andscripts/lint-design.tsproduced no output.src/listing.tsaccepts them. Anything else is refused with "The status is one of …, withdrawn, expired." The terminal (withdrawn,expired) and MCPset_statustake the same values, and the API and MCP docs mention them./homes/:idanswers 200 for anyone, with the address, a Withdrawn or Expired pill and "This home is no longer on the market". It shows no price, photos, facts, MLS words or open house. This holds even if the MLS description was never approved, which used to give a 404..icsfile list nothing for these listings. The schedule page says nothing further goes out.openHouseLinereturns null).src/db.tsrebuildslistingas before. It also holds the assets and their saved versions aside so the cascade doesn't drop them. A test checks existing rows keep their status and assets.contract/openapi.jsonis regenerated, and the README covers the new statuses.find.test.tslisted every filter key, so it now includesoff-market.contract.jsonis untouched, so no permission cell changed.I did not try to hide a withdrawn listing's price from the agent's own views (the report, the flyer and the card on the agent's side). The issue asked for public surfaces only.
To try it, set a listing to Withdrawn, then open
/homes/<id>signed out and check/listings/<id>/scheduleand the Off market filter on/.🤖 Generated with Claude Code
Closes #76