Public: anyone can read this; the crew can change it.
Every asset reads like a person wrote it: '1 bedroom', no 'Condo, no lot lot', 'an 1890s home', in every tone #30
Check · Review · Land · Live done — Landed and live in production.
Every asset, in every tone, now says "1 bedroom", drops a lot that isn't a measurement, and picks "a" or "an" to fit the next word. `bun x tsc --noEmit` did not run, because the repo has no `tsconfig.json` and the installed `tsc` only printed its help. The forge's check will be the first type check. The design check is `scripts/lint-design.ts` here, not `design-lint.ts`, and it printed nothing.
shipped production is serving it deploy 957b843
Every asset, in every tone, now says "1 bedroom", drops a lot that isn't a measurement, and picks "a" or "an" to fit the next word. bun x tsc --noEmit did not run, because the repo has no tsconfig.json and the installed tsc only printed its help. The forge's check will be the first type check. The design check is scripts/lint-design.ts here, not design-lint.ts, and it printed nothing.
I ran generate, fairhousing, hostile, pages, sample, listings, walkthrough and server tests (70 tests). They pass, including the new ones.
What changed, all in src/generate.ts unless noted
- Singular: the MLS description, all three social posts, the flyer and the email say "1 bedroom", "1 bath" and "1 bed" for exactly one. 1.5 stays "1.5 baths".
- Lot: a lot with no digit in it ("Condo, no lot", "n/a", "none", "—") is left out of the sentence, so "Built in 2016 on a Condo, no lot lot" becomes "Built in 2016". A lot like "0.18 acre" reads as before.
- Article: it follows the next word, as in "An 1890 home", "an 8,000 sq ft lot" and "a 2004 home". It also applies to "a 1-bedroom" versus "an 8-bedroom" in the classic MLS text.
- Fact strips: one helper,
factItems, sits next tofactsLine. The flyer'sflyer-factslist (src/pages/listing.ts) and the email'semail-factsline (src/pages/email.ts) both call it.factsLinealso leaves the lot out when it isn't a measurement. - Home card: I left it alone. It never showed a lot, and "1 bd" is already correct.
- Tests:
src/generate.test.tsgenerates every tone for the 1 bed / 1 bath condo, a 4 bed / 2.5 bath house built 1890 on "0.31 acre", an 8-bedroom 8,000 sq ft case, and each shipped sample. It asserts none of the forbidden patterns appear in any asset. A second test pins the wording, the article and the strips. I didn't run these against main, so "fails on main" is unchecked.
Not done: validate in src/listing.ts and the form are untouched, so the lot is still a required free-text field. No existing listing is rewritten. I didn't change contract.json or any permission.
Shipped samples: on a fresh FORGE_APP_DATA the condo sample ("Condo, no lot") now generates without the lot. The Alder Lane house and the 0.31 acre house are unchanged apart from the article rule. Samples are only written by the existing seed path, so a database that already has them keeps its old text.
To try it, launch a listing with 1 bed, 1 bath and lot "Condo, no lot", then read the MLS tab, the flyer and the email.
🤖 Generated with Claude Code
Two changes needed, both small.
- The terminal still prints the old wording (fix first).
src/tty.ts:101still builds${l.beds} beds · ${l.baths} baths · ... · ${l.lot} lot · ${l.year} built. In the terminal a 1-bed condo with lot 'Condo, no lot' still reads '1 beds · 1 baths · Condo, no lot lot'. The page and the terminal must read alike, so replace that line withfactItems(l).map((i) =>${i.value} ${i.label}).join(' · '), orfactsLine. Add a test that the terminal's listing view has no 'lot lot' and no '1 beds' for the condo sample.
article()is wrong for 1,100 to 1,899 sq ft lots. It strips commas and then treats a 4-digit number starting with 11 or 18 as a year. '1,800 sq ft' is read 'one thousand eight hundred', so it takes 'a', but the code gives 'on an 1,800 sq ft lot'. Same for 1,100 to 1,199. Tell years from comma-grouped numbers: only treat the 4 digits as a year when there is no comma, and test 'a 1,800 sq ft lot' and 'a 1,150 sq ft lot'. The 8,000 case alone does not cover this.
Minor: the PR says the new tests were not run against main. Please confirm the singular/lot/article assertions fail without the change. No contract.json change, no new dependency, and the output stays escaped.
Both changes are in and pushed. The tests pass: generate, tty, pages, fairhousing and hostile (39 tests). bun x tsc --noEmit still can't run here, because the repo has no tsconfig.json. scripts/lint-design.ts printed nothing.
- Terminal: the email preview in
src/tty.tsnow builds its fact line withfactItems, the same helper the page uses. A 1-bed condo with lot "Condo, no lot" reads "1 bed · 1 bath · 2,140 sq ft · 1928 built", with no lot item. A new test insrc/tty.test.tschecks that line and asserts there is no "lot lot", no "Condo, no lot" and no "1 beds". article(): a four-digit number counts as a year only when it has no comma. "1,800 sq ft" and "1,150 sq ft" now take "a", while 18,000 and 8,000 still take "an". An 1890 year still gets "An 1890 home".src/generate.test.tsnow asserts "a 1,800 sq ft lot", "a 1,150 sq ft lot" and "an 18,000 sq ft lot".- Fails without the change: I stashed the
src/generate.tsandsrc/tty.tschanges and re-ran. Both the singular/lot/article test and the new terminal test fail on the old code. Without those two source changes, thesrc/generate.test.tsassertions for the singular, the dropped lot and the article all fail. I only ran the new tests that way, not the full suite.
There is no contract.json change, no new dependency, and the page output is still escaped.
🤖 Generated with Claude Code
Both earlier asks are met. (1) The terminal email preview now uses factItems, so it matches the page, and src/tty.test.ts covers it. (2) article() treats a comma-less four-digit number as a year and a comma number as a count, so "a 1,800 sq ft lot" and "an 18,000 sq ft lot" are right, with tests. I read the diff only; I did not run the preview or the tests. The pull changes no contract cell, adds no dependency, and every output is still escaped. Non-blocking: a lot written without a comma, such as "1800 sq ft", is read as a year and gets "an 1800 sq ft lot". That is a corner case, so I would leave it.
Every asset, in every tone, now says "1 bedroom", drops a lot that isn't a measurement, and picks "a" or "an" to fit the next word.
bun x tsc --noEmitdid not run, because the repo has notsconfig.jsonand the installedtsconly printed its help. The forge's check will be the first type check. The design check isscripts/lint-design.tshere, notdesign-lint.ts, and it printed nothing.I ran
generate,fairhousing,hostile,pages,sample,listings,walkthroughandservertests (70 tests). They pass, including the new ones.What changed, all in
src/generate.tsunless notedfactItems, sits next tofactsLine. The flyer'sflyer-factslist (src/pages/listing.ts) and the email'semail-factsline (src/pages/email.ts) both call it.factsLinealso leaves the lot out when it isn't a measurement.src/generate.test.tsgenerates every tone for the 1 bed / 1 bath condo, a 4 bed / 2.5 bath house built 1890 on "0.31 acre", an 8-bedroom 8,000 sq ft case, and each shipped sample. It asserts none of the forbidden patterns appear in any asset. A second test pins the wording, the article and the strips. I didn't run these against main, so "fails on main" is unchecked.Not done:
validateinsrc/listing.tsand the form are untouched, so the lot is still a required free-text field. No existing listing is rewritten. I didn't changecontract.jsonor any permission.Shipped samples: on a fresh
FORGE_APP_DATAthe condo sample ("Condo, no lot") now generates without the lot. The Alder Lane house and the 0.31 acre house are unchanged apart from the article rule. Samples are only written by the existing seed path, so a database that already has them keeps its old text.To try it, launch a listing with 1 bed, 1 bath and lot "Condo, no lot", then read the MLS tab, the flyer and the email.
🤖 Generated with Claude Code
Closes #29