Added a recipes page at /recipes where a cook pastes a recipe as… #11

Merged
cmoriarty merged 3 commits from osf/75-verification-auto-auto-approve-per-run into compare/verify-final 2026-10-17 21:45:37 -04:00
Owner

What was done

  • Added a recipes page at /recipes where a cook pastes a recipe as plain text, picks the servings and Metric or US, and sees it back scaled and converted one ingredient a line, with each line that did not read reported beside its line number (src/pages.ts).
  • A recipe reads as a Serves N first line then one ingredient a line — quantity, optional unit, name — where a quantity may be a whole number, a decimal, a fraction, or a mixed number and a unit may be left off, so 2 eggs reads (src/recipe.ts).
  • Scaling multiplies every quantity by the servings asked for over the recipe's own, and shows spoon, cup and count amounts to the nearest quarter or third and weight and volume amounts as a whole number from ten up or one decimal below (src/scale.ts).
  • Converting shows each ingredient with a unit in the chosen system, in the unit of its kind that keeps the number readable, and leaves an ingredient without a unit as it is (src/convert.ts).
  • The shopping list holds up to five recipes, adds up the same ingredient in the same unit, lists the same ingredient in different units once per unit, and sorts by name; it travels with the page in hidden form fields rather than the database (src/shopping.ts).
  • Each piece has its own unit tests plus page and route tests and a browser test, the README describes the page and the changelog names the change; after Braid's runs, a person-style check of the served page (fresh, populated, and refused-servings states) passed, noting only a cosmetic button-wrap.
  • Not done: npm test runs the unit tests only; the new browser test in e2e/recipes.spec.ts is a separate command (npm run test:e2e) that needs Postgres, so it is not part of the unit run.
  • Not done: the browser test's expected list (6 eggs, 14 oz flour) was kept as the spec and the product changed to match it, rather than adjusting the test.

How to review

  1. Check out the work branch (osf/75-verification-auto-auto-approve-per-run) and install its dependencies with npm install.
  2. Type-check with npx tsc --noEmit; it should be clean.
  3. Run the unit tests with npm test; the new recipe, scale, convert, shopping, page, and route tests all pass, and it starts an embedded Postgres if DATABASE_URL is not set.
  4. Run the browser tests: install Chromium once with npx playwright install chromium, then npm run test:e2e; the two new tests in e2e/recipes.spec.ts pass (the suite needs DATABASE_URL, as in CI).
  5. See it working: point DATABASE_URL at a reachable Postgres, start the server with npm start, and open /recipes; paste Serves 2, 200 g flour, a pinch of salt, 2 eggs, enter 4, choose US, and press Show recipe to see 14 oz flour, 4 eggs, and Line 3: a pinch of salt (no quantity), then press Add to list to see the shopping list.
  6. Read first: src/server.ts for the /recipes routes, then src/pages.ts for the page.

Code review

Reviewed the recipes change (src/recipe.ts, src/scale.ts, src/convert.ts, src/shopping.ts, the /recipes page and routes, and its unit, server, and e2e tests) against the five spec deltas: every scenario is implemented, user output is escaped in element and attribute context, and the routes sit before the GET / branch as required. One real defect: nearestQuarterOrThird in src/scale.ts walked from 1/4 up in an O(quantity) loop building and sorting ~24*quantity objects, so one valid pasted line with a large count (parseQuantity accepts digit strings of any length) could freeze the single-threaded server for minutes; I replaced it with a constant-time nearest-multiple computation that keeps the tie-to-the-smaller and 1/4-floor behavior, and added a regression test. No generated files, caches, secrets, or scratch artifacts are in the commit (node_modules, test-results, and playwright-report are gitignored). tsc, the six touched test files (61 tests), the Playwright test listing, and the release-notes check all pass.

Left for you

  • minor, src/server.ts:15: readBody has no size cap, so the new POST /recipes route (like the pre-existing POST / route) accepts arbitrarily large form bodies. Left as-is: it is the shared pre-existing pattern, and capping it would change the shortener's routes too, which is outside this change's scope.

Fixed before the commit

  • major, src/scale.ts:60: nearestQuarterOrThird built and sorted 2*(12*quantity+2) candidate objects, so a valid pasted line with a large count (parseQuantity accepts digit strings of any length, e.g. 99999999999999 eggs) would hang the single-threaded server on one form submission. Replaced with an O(1) nearest-multiple of 1/4 and 1/3 (a half rounds to the smaller, the smallest multiple is 1/4, a tie between the two families goes to the smaller value); behavior is unchanged for all spec-pinned amounts, and a regression test was added in test/scale.test.ts.

Tests

All 47 scenarios of scale-recipes-and-shopping-list have a tagged test.

The test lint found, in the run's tests on the last check:

Recorded, not decided

  • e2e/recipes.spec.ts:1 B7: no relative import: this file asserts on things it constructed itself, which is the measured 'asserted on my own mock' failure
  • test/recipes-page.test.ts:1 B7: nothing imported here looks like the module under test ('recipes-page')
  • test/recipes-server.test.ts:1 B7: nothing imported here looks like the module under test ('recipes-server')

Braid run

  • Workflow: Braid's OpenSpec flow, change scale-recipes-and-shopping-list (23 steps, 2 retried)
  • Issue: cmoriarty/braid-combined-test#4
  • Model: vLLM · qwen3.8-27b
  • Time: 2 h 14 m from start to this pull request, 2 h 9 m of it in the model
  • Tokens: 26.0M input, 59k output, 249k reasoning, over 224 model calls
  • Pre-flight estimate: fits, p50 198k / p80 278k tokens against a 394k-token step allowance
  • CI: passed its 6 jobs on f2d06e7d1c
  • Run: run_01M5637X8K3YXQXYEJ48HNT8W5

Opened by Braid.

## What was done - Added a recipes page at `/recipes` where a cook pastes a recipe as plain text, picks the servings and Metric or US, and sees it back scaled and converted one ingredient a line, with each line that did not read reported beside its line number (`src/pages.ts`). - A recipe reads as a `Serves N` first line then one ingredient a line — quantity, optional unit, name — where a quantity may be a whole number, a decimal, a fraction, or a mixed number and a unit may be left off, so `2 eggs` reads (`src/recipe.ts`). - Scaling multiplies every quantity by the servings asked for over the recipe's own, and shows spoon, cup and count amounts to the nearest quarter or third and weight and volume amounts as a whole number from ten up or one decimal below (`src/scale.ts`). - Converting shows each ingredient with a unit in the chosen system, in the unit of its kind that keeps the number readable, and leaves an ingredient without a unit as it is (`src/convert.ts`). - The shopping list holds up to five recipes, adds up the same ingredient in the same unit, lists the same ingredient in different units once per unit, and sorts by name; it travels with the page in hidden form fields rather than the database (`src/shopping.ts`). - Each piece has its own unit tests plus page and route tests and a browser test, the README describes the page and the changelog names the change; after Braid's runs, a person-style check of the served page (fresh, populated, and refused-servings states) passed, noting only a cosmetic button-wrap. - **Not done:** `npm test` runs the unit tests only; the new browser test in `e2e/recipes.spec.ts` is a separate command (`npm run test:e2e`) that needs Postgres, so it is not part of the unit run. - **Not done:** the browser test's expected list (`6 eggs`, `14 oz flour`) was kept as the spec and the product changed to match it, rather than adjusting the test. ## How to review 1. Check out the work branch (`osf/75-verification-auto-auto-approve-per-run`) and install its dependencies with `npm install`. 2. Type-check with `npx tsc --noEmit`; it should be clean. 3. Run the unit tests with `npm test`; the new recipe, scale, convert, shopping, page, and route tests all pass, and it starts an embedded Postgres if `DATABASE_URL` is not set. 4. Run the browser tests: install Chromium once with `npx playwright install chromium`, then `npm run test:e2e`; the two new tests in `e2e/recipes.spec.ts` pass (the suite needs `DATABASE_URL`, as in CI). 5. See it working: point `DATABASE_URL` at a reachable Postgres, start the server with `npm start`, and open `/recipes`; paste `Serves 2`, `200 g flour`, `a pinch of salt`, `2 eggs`, enter 4, choose US, and press Show recipe to see `14 oz flour`, `4 eggs`, and `Line 3: a pinch of salt (no quantity)`, then press Add to list to see the shopping list. 6. Read first: `src/server.ts` for the `/recipes` routes, then `src/pages.ts` for the page. ## Code review Reviewed the recipes change (src/recipe.ts, src/scale.ts, src/convert.ts, src/shopping.ts, the /recipes page and routes, and its unit, server, and e2e tests) against the five spec deltas: every scenario is implemented, user output is escaped in element and attribute context, and the routes sit before the GET /<code> branch as required. One real defect: nearestQuarterOrThird in src/scale.ts walked from 1/4 up in an O(quantity) loop building and sorting ~24*quantity objects, so one valid pasted line with a large count (parseQuantity accepts digit strings of any length) could freeze the single-threaded server for minutes; I replaced it with a constant-time nearest-multiple computation that keeps the tie-to-the-smaller and 1/4-floor behavior, and added a regression test. No generated files, caches, secrets, or scratch artifacts are in the commit (node_modules, test-results, and playwright-report are gitignored). tsc, the six touched test files (61 tests), the Playwright test listing, and the release-notes check all pass. **Left for you** - minor, `src/server.ts:15`: readBody has no size cap, so the new POST /recipes route (like the pre-existing POST / route) accepts arbitrarily large form bodies. Left as-is: it is the shared pre-existing pattern, and capping it would change the shortener's routes too, which is outside this change's scope. **Fixed before the commit** - major, `src/scale.ts:60`: nearestQuarterOrThird built and sorted 2*(12*quantity+2) candidate objects, so a valid pasted line with a large count (parseQuantity accepts digit strings of any length, e.g. 99999999999999 eggs) would hang the single-threaded server on one form submission. Replaced with an O(1) nearest-multiple of 1/4 and 1/3 (a half rounds to the smaller, the smallest multiple is 1/4, a tie between the two families goes to the smaller value); behavior is unchanged for all spec-pinned amounts, and a regression test was added in test/scale.test.ts. ## Tests All 47 scenarios of `scale-recipes-and-shopping-list` have a tagged test. The test lint found, in the run's tests on the last check: **Recorded, not decided** - `e2e/recipes.spec.ts:1` B7: no relative import: this file asserts on things it constructed itself, which is the measured 'asserted on my own mock' failure - `test/recipes-page.test.ts:1` B7: nothing imported here looks like the module under test ('recipes-page') - `test/recipes-server.test.ts:1` B7: nothing imported here looks like the module under test ('recipes-server') ## Braid run - **Workflow:** Braid's OpenSpec flow, change `scale-recipes-and-shopping-list` (23 steps, 2 retried) - **Issue:** cmoriarty/braid-combined-test#4 - **Model:** vLLM · qwen3.8-27b - **Time:** 2 h 14 m from start to this pull request, 2 h 9 m of it in the model - **Tokens:** 26.0M input, 59k output, 249k reasoning, over 224 model calls - **Pre-flight estimate:** fits, p50 198k / p80 278k tokens against a 394k-token step allowance - **CI:** passed its 6 jobs on `f2d06e7d1c` - **Run:** [run_01M5637X8K3YXQXYEJ48HNT8W5](http://openspecflow.internal.underthere.xyz/run/run_01M5637X8K3YXQXYEJ48HNT8W5) _Opened by Braid._
check: the code of fix-loop round 2, for the project's CI
Some checks failed
CI / Release notes (pull_request) Successful in 11s
CI / Unit tests (pull_request) Successful in 1m0s
CI / Unit tests (push) Successful in 54s
CI / Browser tests (pull_request) Failing after 1m3s
CI / Release notes (push) Successful in 8s
CI / Browser tests (push) Failing after 1m26s
ce3e80ddff
cmoriarty force-pushed osf/75-verification-auto-auto-approve-per-run from ce3e80ddff
Some checks failed
CI / Release notes (pull_request) Successful in 11s
CI / Unit tests (pull_request) Successful in 1m0s
CI / Unit tests (push) Successful in 54s
CI / Browser tests (pull_request) Failing after 1m3s
CI / Release notes (push) Successful in 8s
CI / Browser tests (push) Failing after 1m26s
to f2d06e7d1c
All checks were successful
CI / Release notes (push) Successful in 10s
CI / Unit tests (push) Successful in 52s
CI / Browser tests (push) Successful in 1m6s
CI / Browser tests (pull_request) Successful in 1m10s
CI / Release notes (pull_request) Successful in 12s
CI / Unit tests (pull_request) Successful in 1m19s
2026-10-17 21:27:01 -04:00
Compare
cmoriarty changed title from Scale a recipe, convert its units, and build a shopping list to Added a recipes page at /recipes where a cook pastes a recipe as… 2026-10-17 21:45:09 -04:00
cmoriarty force-pushed osf/75-verification-auto-auto-approve-per-run from f2d06e7d1c
All checks were successful
CI / Release notes (push) Successful in 10s
CI / Unit tests (push) Successful in 52s
CI / Browser tests (push) Successful in 1m6s
CI / Browser tests (pull_request) Successful in 1m10s
CI / Release notes (pull_request) Successful in 12s
CI / Unit tests (pull_request) Successful in 1m19s
to 608369c283
All checks were successful
CI / Release notes (push) Successful in 10s
CI / Browser tests (push) Successful in 1m1s
CI / Unit tests (push) Successful in 1m6s
CI / Unit tests (pull_request) Successful in 1m1s
CI / Release notes (pull_request) Successful in 10s
CI / Browser tests (pull_request) Successful in 1m37s
2026-10-17 21:45:10 -04:00
Compare
cmoriarty merged commit bb20e76c74 into compare/verify-final 2026-10-17 21:45:37 -04:00
cmoriarty deleted branch osf/75-verification-auto-auto-approve-per-run 2026-10-17 21:45:37 -04:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cmoriarty/braid-combined-test!11
No description provided.