Skip to content

M0: expose segment attraction and scale dining time by courses - #9

Open
HSpector1 wants to merge 5 commits into
rewrite/m0-menu-responsive-demandfrom
fix/m0-human-gate-readiness
Open

M0: expose segment attraction and scale dining time by courses#9
HSpector1 wants to merge 5 commits into
rewrite/m0-menu-responsive-demandfrom
fix/m0-human-gate-readiness

Conversation

@HSpector1

Copy link
Copy Markdown
Owner

What this is

The two authorized M0 finishing corrections from the PR #7 adjudication (H2 legibility + M1 economics). Focused, in-scope; does not reopen DemandModel.Capture. Targets the PR #7 branch for a clean diff.

Do not merge. Review surface + human-gate decision. No human tests. No M1.

H-LEG-1 — attraction legibility

The menu-responsive demand model is now visible. SegmentAttraction (pool / consideration% / mix% / parties) is on ForecastSnapshot and ServiceResult:

  • Pre-service: a "Who this plan attracts" table + one plain-language note (largest-remainder reconciled to the market).
  • Post-service: an ATTRACTION funnel, so forecast vs actual arrival mix can be compared.
  • New causal categories (disjoint-PState attribution, no double-count): weak-attraction (too few), wrong-mix (a composition opportunity cost — under-attracted the market's higher-spending crowd while the operation ran fine), purchase-conversion (arrived but didn't order), alongside seating/kitchen. Example: a value chassis in the enthusiast market now reads "wrong mix — under-attracted food enthusiast, the market's higher-spending crowd (~$204 missed), while filling the room with a lower-value crowd."

MED-ECON-1 — course-scaled seat duration

Flat EatMin=12 → shared Tuning.SeatEatMin(coursesX10) = clamp(6 + 6·courses, 12, 30) → 1/2/3-course = 12 / 18 / 24. The sim scales a party's eat window by DistinctCourseCount; the forecast scales seatCoverCap by the captured-mix expected courses (same primitive). Only eat time changes — AvgTicketTimeMin (kitchen) is untouched.

Economic effect (empirical): a premium 3-course service turns 2.96 covers/seat (~61-min dwell) vs a value 1-course 3.67 covers/seat (~33-min dwell) — premium ties up a table ~1.85× longer per seat-hour, so per-seat-minute leverage falls from ~4× toward ~2.5–3×. No over-correction: premium still wins enthusiast, value still wins lunch, mixed viable in social, KNOWN_RESIDUAL holds. Favorable side effect: best-generalist regret rose 6%→17% on base 271828182 (exactly the mechanism the adjudication predicted), not gate-chasing.

Technical

  • 146 tests pass (Core 54 + Determinism 31 + Scenario 61, incl. 7 new ReadinessTests), 0 fail, stable across two runs; Release build clean.
  • Determinism/accounting/RNG/forecast-immutability/scope preserved. Goldens deliberately re-baselined (duration changes seat occupancy; attraction legibility does not affect the checksum) — see D-032.

Gate recommendation

Verdict: Pass-with-notes
Action:  Continue   (request a focused independent re-review of these two corrections)

Continue does not authorize human tests, M1, or merging.

Where to look

  • reports/m0/M0-HUMAN-GATE-READINESS-REPORT.md — §A–J incl. exact reviewer experiments (§J)
  • docs/design/M0-HUMAN-GATE-READINESS-CONTRACT.md — the pre-code design lock
  • docs/DECISION-LOG.md — D-031 / D-032
  • Reproduce: --report (attraction table + causal categories), dotnet test

🤖 Generated with Claude Code

claude added 5 commits July 30, 2026 13:50
Two authorized corrections only: H-LEG-1 (attraction legibility — expose per-segment
consideration/captured mix in forecast+autopsy, add weak-attraction/wrong-mix causal
categories) and MED-ECON-1 (course-scaled seat duration — shared SeatEatMin primitive,
Model A base+per-distinct-course, sim + forecast). Locked before coding per §6.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MED-ECON-1: replace flat EatMin=12 with a shared Tuning.SeatEatMin(coursesX10)
(base 6 + 6 per distinct course, clamped [12,30]); the sim scales a party's eat
window by its DistinctCourseCount and the forecast scales seat-cover capacity by the
captured mix's expected course count, so a premium multi-course meal turns fewer tables
(seat-minute leverage ~4x -> ~3x). Only eat time changes; ticket/cook path untouched.

H-LEG-1 (core): thread CapturedDemand into Finalize; build realized per-segment
attraction (pool/consideration/arrival-mix); add weak-attraction, wrong-mix, and
purchase-conversion causal categories with disjoint-PState attribution (wrong-mix is a
composition opportunity cost, never double-counted). (Note: Simulator.cs/Forecast.cs
carry both corrections; presentation is in the next commit.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tation)

Add SegmentAttraction (pool/consideration/composition/parties) to ForecastSnapshot and
ServiceResult; render a "Who this plan attracts" table pre-service with a plain-language
attraction note (largest-remainder reconciliation), and a per-segment ATTRACTION funnel
post-service, so the menu-responsive demand model is visible and forecast-vs-actual mix
can be compared. Harness --report diagnostic to eyeball forecast+autopsy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…and re-baseline goldens

New ReadinessTests: SeatEatMin monotonic/bounded, ticket-time unchanged, per-segment
attraction reconciles pre/post-service, wrong-mix reachable (and never for a good premium
plan), weak-attraction and seating remain distinguishable. Rename the weak-demand
attribution assertion to weak-attraction. Re-baseline the three goldens (course-scaled
duration changes seat occupancy; attraction legibility does not affect the checksum).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Final report (reports/m0/M0-HUMAN-GATE-READINESS-REPORT.md, §A-J), DECISION-LOG D-031
(the two corrections) and D-032 (goldens re-baselined). Regenerated balance/determinism/
forecast evidence; the example autopsy now renders the pre-service attraction table and
the post-service ATTRACTION funnel.

Gate: Verdict Pass-with-notes / Action Continue (request focused independent re-review).
Do not merge; no human tests; no M1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@HSpector1

Copy link
Copy Markdown
Owner Author

Final Focused Independent Re-Review — M0 Human-Gate Readiness (PR #9)

Independent technical readiness review of the two authorized corrections (H-LEG-1 attraction
legibility, MED-ECON-1 course-scaled seat duration) before the five-player M0 human gate.
Read-only. No branch modified, no commit, no merge, no human tests run.

A. Review identity

Repository HSpector1/Restaurant
PR #9M0: expose segment attraction and scale dining time by courses
Base e5baa54 (PR #7 branch rewrite/m0-menu-responsive-demand)
Head reviewed c360ebcc72ce1e0a59394e4368977feb29eca1ca (all 7 agents confirmed this exact SHA)
Date 2026-07-30
Environment macOS (Darwin arm64), .NET SDK 8.0.423, Release config; fresh clone at /tmp/rev9
Method Lead inline verification + 6 focused read-only sub-agents (A forecast-attraction, B funnel/causality, C course/seat timing, D forecast-timing, E strategy/economics, F docs/scope/tests), each on its own held-out seeds, then 1 adversarial cross-check of the one Blocker.
Verification loops used 0 of the 2 optional bounded loops (§30) — evidence was unambiguous.
Commands dotnet build/test -c Release (x2); Harness --report/--probe/--attr; the interactive CLI; independent scratch drivers referencing the built RestaurantSim.Core.dll (public API) on held-out seed bases 424242000 / 511–558M / 522M / 533M / 544M / 555M.

Baseline confirmed: the diff (git diff e5baa54..HEAD) is 17 files / +662 −124 — 6 production .cs (Tuning, Simulator, Forecast, Result, TextReport + Harness/Program.cs diagnostics), 3 test .cs (AttributionTests, ScenarioTests modified; ReadinessTests new), and docs/reports. No binaries/assets, no M1 systems, Demand.cs/PriceModel.cs untouched, PR #7 base and main unchanged. Contract M0-HUMAN-GATE-READINESS-CONTRACT.md was committed before the code (commit 771e2b8 precedes the feat commits).


B. Executive verdict

Technical verdict:        Pass-with-notes
Action:                   Continue  (authorize the five-player M0 human gate)
Human-test authorization: Authorized with stated limitations
PR #9 merge:              Do not merge yet (final human-gate candidate; hold until the gate)
Foundation merge:         Do not merge yet (only after the human gate)
Main merge:               Do not merge yet (only after the human gate; M1 remains separate)
M1 status:                UNAUTHORIZED

Both authorized corrections are implemented, in scope, deterministic, and independently verified. No Blocker or High defect survives scrutiny. The single Blocker raised by a sub-agent (attraction table "attracted > pool") was refuted on cross-check and re-adjudicated by the lead to a Medium legibility note: it is internally consistent with the simulator, matches the authorizing contract's exact formula, and does not teach a false strategic rule. The remaining items are legibility/wording edge cases and one economic-rationale disclosure — none makes attraction invisible or corrupts the funnel the gate relies on. This maps to §31 "Authorized with stated limitations."

Load-bearing claims reproduced independently (held-out seeds):

Claim Verdict Evidence
SeatEatMin 1/2/3-course = 12/18/24, floor 12, ceil 30, integer-only, no name/premium branch Confirmed Tuning.cs:35-36; direct calls (0/5/-5→12; 40/100/1000→30; 14→14). Int128 math, no float in the timing path.
Course scaling actually lengthens table occupancy in the authoritative sim Confirmed Seat-bound social-dinner, held-out seeds: 3-course serves fewer parties & loses more to capacity than mains-only, every seed (e.g. served 55→47, lostCap 20→40).
Only the eat window changes; AvgTicketTimeMin (kitchen) untouched Confirmed Ticket time recorded at delivery (Simulator.cs:204) before Eating; ReadinessTests locks it.
Funnel disjointness gen == served+lostCap+lostWait+lostMenu Confirmed 0 violations across 56,700+ held-out runs (Agent B) + 1,500 (lead). Four terminal PStates partition every party; tail sweep at Simulator.cs:267.
Forecast attraction table reconciles (pool→ExpectedArrivals, attracted→attempts) exactly Confirmed 15 scenario×strategy + 13 synthetic edge cases; sums exact in all.
Attraction notes derived from real values, no market/segment/strategy-name branch Confirmed Forecast.cs:213-231 branches on ConsiderationBp/CompositionBp/PoolParties; names only in display interpolation.
Causal categories disjoint, ranked by max not sum; premium never mislabeled wrong-mix Confirmed DiagnoseBottleneck (Simulator.cs:656-664); premium-in-enthusiast wrong-mix = 0/200 (lead) & 0/40 (test); broad+overload → kitchen, not weak-attraction.
H-LEG-1 code is economically inert (attraction visibility does not move the checksum) Confirmed Agent E rebuilt pre-fix core + patched only the EatUntil line → reproduces post-fix byte-for-byte.
No dominant strategy; generalist is a fragile compromise, not safe near-best everywhere Confirmed 2/3 distinct market winners; best-generalist worst-market regret 14/18/22% on held-out searchbases (all >10%).
Docs honest: no-merge / no-human-test / no-M1 stated; cross-OS determinism marked UNVERIFIED Confirmed Contract, report, decision-log D-031, DETERMINISM-CONTRACT.md.
146 tests pass ×2, Release, 0 warnings; modified tests re-baselined/renamed, not weakened Confirmed Core 54 + Determinism 31 + Scenario 61; goldens changed for MED-ECON-1 (documented D-032); weak-demandweak-attraction is a cause-string rename.

C. Attraction legibility (H-LEG-1)

Forecast table (TextReport.Forecast): renders segment | pool | appeal% | mix% | attracted with a total row. Pool sums exactly to ExpectedArrivals and attracted sums exactly to attempts (= round(ExpectedArrivals × conversion)), via a remainder-to-last-segment allocation. The mix column correctly has no printed total (so a 100.01% rounding case never renders a visible contradiction). The forecast is fully immutable/deterministic (byte-identical across 20 recomputes).

Actual funnel (autopsy): a DEMAND FUNNEL block (attempted parties / served parties+covers / lost-to-seating / walked-waiting / no-acceptable-dish, correctly labeled parties vs covers) plus an ATTRACTION table (pool/appeal/mix/arrived) and a FORECAST vs ACTUAL line. A player can compare expected mix vs actual arrival mix directly (e.g. forecast 7/78/13 vs actual 6/78/14). Seeded variance is framed as "an analytic approximation… gaps are expected and informative," not as a forecast error.

Player questions (§26) — answerable from the CLI alone:

  1. Which segments are in this market? → Yes (pool column, all 3 segments, pre- and post-service).
  2. Which segments do I attract? → Yes (appeal% + mix% + attracted counts).
  3. Which am I failing to attract? → Yes (low appeal/mix + the "barely registers with X" note).
  4. Is my problem attraction or service? → Yes (causal summary separates weak-attraction/wrong-mix from seating/kitchen; funnel shows where losses fall).
  5. Did the actual mix resemble the forecast? → Yes (forecast table vs autopsy ATTRACTION table + FORECAST vs ACTUAL).
  6. What should I change next? → Mostly (primary causal recommendation + most/least-useful dish + attraction note), with edge-case roughness (A2/A3/A4 below).

Terminology issues: "appeal" for consideration and "mix" for composition read cleanly. The one confusing point is the pool vs attracted relationship (finding A1).


D. Causal attribution

The autopsy's causal summary distinguishes five disjoint channels and reports the single largest by measured lost contribution (ranked by Math.Max, never summed), with the busiest station as a strictly secondary "condition, not necessarily the primary loss." Rendered examples (Harness --report):

  • weak-attraction"weak attraction — only N parties considered a visit versus roughly 150 the market could yield."
  • wrong-mix"the plan under-attracted food enthusiast, the market's higher-spending crowd (~$204.12 of missed contribution), while filling the room with a lower-value crowd. The operation ran fine…" (business-consequence line + clean-operation qualifier).
  • purchase-conversion — seated but nothing acceptable to order.
  • seating / kitchen — the operational channels.

Disjoint-state audit (§11): every generated party ends in exactly one terminal PState (Paid / WalkedSeat / WalkedFood / NoOrder); demandGenerated == served + lostCap + lostWait + lostMenu held with 0 violations across 56,700+ held-out runs (Agent B) and 1,500 (lead). weak-attraction reads ungenerated parties; wrong-mix is a composition opportunity cost (not a party bucket); so no contribution is double-counted. The earlier "utilization flip" defect is not recreated — in tiny-room wrong-mix cases the missed-opportunity cents genuinely exceed the small realized seat loss.

Controlled fixtures (lead, 200 held-out seeds each):

Plan Market PrimaryLossCause distribution
healthy value (52 seats) lunch kitchen 181 / seating 18 / wrong-mix 1
healthy value (60 seats) lunch kitchen 200
value-chassis enthusiast kitchen 150 / wrong-mix 42 / weak-attraction 8
premium enthusiast kitchen 197 / seating 3 / wrong-mix 0
repellent menu social weak-attraction 200
tiny room lunch seating 199 / kitchen 1
broad + grill overload social kitchen 200 (not mislabeled weak-attraction)

wrong-mix is reachable but never fires for a well-matched premium plan (0/200), and only 0.5% for a healthy value plan (not a systematic false positive). Because M0 is throughput-dominated (D-012), "kitchen" is the primary cause in most services; the demand-side causes are narrow-regime by design, but the attraction table is always shown, so legibility does not depend on which cause fires.

Priority (§15): strong-attraction + weak-op → operational primary; weak-attraction + excellent-op → attraction primary with no add-capacity advice; weak + weak → both stages reported and ordered. Verified.


E. Timing correction (MED-ECON-1)

  • SeatEatMin(coursesX10) = clamp(6 + 6·courses, 12, 30): 1/2/3-course = 12/18/24 exactly; 0/invalid → 12; ≥40 → 30 ceiling (never reached by the sim, whose max is 3 courses). Integer-only, name-agnostic, no premium penalty.
  • Course-count determination: DistinctCourseCount counts distinct courses among ordered dishes (deduped, size-independent). A party always has a main before any starter/dessert is attempted (OrderDishes gating), so distinct ∈ {1,2,3}; a dishless party becomes NoOrder and never reaches the Eating state. Failed/comped courses still count toward the eat window (economically reasonable: the diner occupies the seat while a comped course is served).
  • Total seated duration: measured in seat-bound 24-seat rooms — value 1-course ≈ 32.5 min dwell, premium 3-course ≈ 65 min dwell; a 3-course meal ties up a table ~ longer. Multi-course parties demonstrably serve fewer parties and lose more to capacity.
  • Forecast reuse: the forecast's seatCoverCap uses the same SeatEatMin (dwell = ForecastNonEatDwellMin(20) + SeatEatMin(expectedCoursesX10)), from the captured-mix expected course count. It cannot be reproduced by the old flat 12 (3-course dwell 38 / cap 217 vs old 32 / 258); a 1-course mix is byte-identical to legacy. Higher course count → longer dwell → fewer seat turns, monotonically.
  • Table release / service close (§19): deterministic; occupied-at-close parties flushed to lost-wait (Simulator.cs:267); accounting non-negative and conserved on every held-out run; identical checksum on repeat.

F. Economic effects & premium-leverage ruling

Per-seat/per-station decomposition (base 555000000, n=200), premium retains every qualitative property: check $58.59 vs value $20.07; ingredient/cover $14.92 vs $4.53; ticket 24.2 min vs 11.8; effective dwell 65 vs 33 min; station util 63-64% vs 43-46%; contribution/cover $34.28 vs $9.94. Premium still wins the enthusiast evening; value still wins lunch; mixed viable in social. No over-correction; no correction that is economically inert.

Covers/seat clarification (§22): the builder's 3.67 (value) / 2.96 (premium) are theoretical seat-turn figures (= ServiceMinutes ÷ dwell) measured in controlled 24-seat seat-bound rooms — a basis the builder's report explicitly discloses (reproduced independently: 3.69 / 2.77). They are not realized covers/seat, which for the named strategies in their native markets are ≈ 3.21 / 2.51. The gate summary should carry this label so the figure is not over-generalized.

Premium-leverage ruling (§23): Credible with tuning notes.
The correction mechanically does what it should: each premium multi-course cover now ties up its seat 2× longer, so its per-cover-per-eat-minute leverage falls (≈3.4× → ≈2.2×). However, Agent E's isolation experiment (rebuild pre-fix core, patch only the EatUntil line) proved the more decision-relevant, undisclosed result: at the whole-service level in the enthusiast market — where premium is demand/kitchen-bound, not seat-bound — the longer dwell reduced wait-walkouts (LostToWait 10.8 → 4.4), so premium's enthusiast contribution rose +35–38% ($2120 → ~$2920) across 5 held-out bases, and whole-service premium/value leverage rose 1.29× → 1.80×. So the "premium now pays for its seat time / leverage reduced 4×→3×" framing is metric-dependent: it holds per-cover but inverts at the service level in premium's home market. The intended cross-market effect did land where it matters — the premium-anchored generalist's edge shrank (regret 6% → 14–22%). This is a rationale/disclosure gap, not a broken correction: strategy choice remains valid. The owners should be told the net effect on premium in enthusiast is a modest increase, not a taming.


G. Strategy regression

  • Winners: lunch = Focused Value; social = Premium Craft; enthusiast = Premium Craft → 2/3 distinct, no single dominant strategy (distribution.md, base 555000000, 200 seeds; p10s reproduced).
  • No cross-market dominator: best-generalist worst-market regret 14% / 18% / 22% on held-out searchbases 556/557/558M, all in enthusiast-evening, all >10%. Higher than the builder's stated ~6–17% band, which strengthens the no-dominator conclusion.
  • Generalist ruling (§25): PASS. The generalist is a fragile, seat-time-consuming compromise, not the safe near-best everywhere. MED-ECON-1 nudged it less dominant (favorable). The underlying demand-rewrite residual (D-028/D-029) is a pre-existing PR M0 menu-responsive demand rewrite (Conditional-Pass; owner decision on residual) #7 owner decision, out of PR M0: expose segment attraction and scale dining time by courses #9's scope; PR M0: expose segment attraction and scale dining time by courses #9 does not worsen it. Per the mandate, I do not fail on a single sub-10% regret — and here every measured regret is above 10% anyway.

H. Technical regression & scope

  • Tests: 146 pass twice in Release (Core 54, Determinism 31, Scenario 61 incl. 7 new ReadinessTests), 0 fail / 0 skip / 0 warnings. Debug and Release both green.
  • Test integrity: modified tests were re-baselined/renamed, not weakened — 3 golden checksums changed because course-scaled seat occupancy changes covers/revenue (documented D-032; H-LEG-1 does not affect the checksum), and the weak-demandweak-attraction rename keeps the same fixture and assertion. New ReadinessTests are non-trivial (SeatEatMin 12/18/24, ticket-time isolation, exact attraction reconciliation, causal fixtures) and reproduce on independent held-out seeds.
  • Determinism / accounting / RNG / forecast immutability / numeric guards: all hold; forecast has no seed and is byte-stable; no float in authoritative timing.
  • Scope (§28): grep confirms no executable reputation / repeat-visit / customer-memory / reviews / marketing / reservations / bussing / Godot / pathfinding / saves / managers / delegation / campaign / audio / assets. Clean.
  • Documentation (§27): contract, readiness report, and decision-log all state PR M0: expose segment attraction and scale dining time by courses #9 awaits independent re-review and forbid merge / human-tests / M1; cross-OS determinism is correctly documented as UNVERIFIED/OPEN; the shipped example-forecast-vs-actual.txt reproduces byte-for-byte (checksum 05E65C6E8AD6AF07). One stale code comment (F-DOC-1 below).

I. Findings

(Severity: Blocker/High/Medium/Low/Note · Status = whether the tested claim held)

A1 — Forecast "attracted" can exceed a segment's "pool"

  • Severity: Medium · Status: Confirmed (phenomenon) / the "no segment exceeds its pool" reconciliation check is not satisfied
  • Category: legibility / labeling
  • Claim tested: mandate §8 reconciliation — "No segment exceeds its potential pool."
  • Evidence: attracted[s] = pool[s] × considerRaw[s]/Bp where raw consideration may exceed 100% (Demand.cs:109-127; Forecast.cs:200-203). On the default preset+market (Balanced Competent / social-dinner) the CLI renders Social Dinner pool 57 / attracted 59 (forecast) and pool 57 / arrived 59 (autopsy, seed 424242123); Broad Menu shows 57 → 74. Columns still reconcile exactly to ExpectedArrivals (95) and attempts, and it is consistent between forecast and sim. Matches the authorizing contract §2a formula exactly. ReadinessTests never asserts Parties ≤ PoolParties.
  • Why it matters: on a legibility-focused gate, "attracted 74 from a pool of 57" reads as an arithmetic contradiction and the forecast table (unlike the autopsy table) carries no explanatory caption. It does not teach a false strategic rule — the lesson ("this menu is a social-diner magnet") is correct — and the aggregate funnel the gate relies on is sound.
  • Required action: add a one-line caption to the forecast attraction table (as the autopsy table has) clarifying that a strongly-attracted segment can draw a share above its neutral market baseline, and/or relabel "pool" as "market baseline"; add a Parties-vs-Pool intent test. Owner decision, low-risk.
  • Gate effect: Does not block. Stated limitation. (Sub-agent A rated Blocker citing the mandate checklist; adversarial cross-check refuted to Low citing the authorizing contract; lead adjudicates Medium — the mandate check genuinely fails, but against an assumption the authorized model does not share.)

A2 — Over-attraction note is silent in the unstaffed-station case

  • Severity: Medium · Status: Confirmed · Category: notes / coverage gap
  • Evidence: the overload note guard is kitchenCoverCap > 0 && attractedCovers > cap·1.2 (Forecast.cs:228). When a menu lists a dish whose station has no cook, kitchenCoverCap == 0, so the guard is false: ~107 parties attracted, ExpectedCovers 0, contribution ≈ −$1802, yet the only note is "mostly value lunch." The single most severe over-attraction case emits no overload warning.
  • Why it matters: this is exactly the situation the note exists to catch. Mitigant: ExpectedCovers 0 and a large negative contribution are still shown (a loud separate signal).
  • Required action: also fire (with distinct "kitchen can serve ~nobody" wording) when kitchenCoverCap == 0 && attractedCovers > 0.
  • Gate effect: does not block; recommend polishing before or during the gate.

A3 — Self-contradictory note for a menu with no main course

  • Severity: Medium · Status: Confirmed · Category: notes / contradiction
  • Evidence: with no mains, Capture returns consideration=floor for all segments and composition falls back to raw pool mix; the ignored-branch then prints "barely registers with social dinner: only 1% considered it, so they are just 60% of arrivals despite being 60% of this market" — calling the largest arrival share "barely registers," with a "despite" between two equal percentages.
  • Required action: guard the degenerate all-at-floor / no-mains case (e.g. "this menu has no main course; almost no one will choose you"); avoid "just N% … despite N%" when equal.
  • Gate effect: does not block; reachable by a player who forgets a main.

A4 — Note fixates on a micro-segment for premium plans · Low · Confirmed

  • For premium/enthusiast plans the prose note calls out value-lunch (4% of that market) instead of the enthusiast pull. The table still answers "who's most attracted"; only the one-line note is sub-optimal. Reorder note priority so a tiny-pool segment does not preempt the dominant/top-pull note.

A5 — "largest-remainder" label is really "remainder-to-last" · Note · Confirmed

  • Forecast.cs:200-201 rounds the first N−1 and gives the exact remainder to the last segment. Safe for the current 3-segment fixtures; a future tiny trailing segment could underflow. Relabel the comment or implement true largest-remainder; add a ≥0 guard.

B-2 — Loss-making seating verdict prints "~$0.00 of lost contribution" yet advises adding seats · Low · Confirmed

  • When a service is loss-making, cpcFloored == 0 so seatLoss == 0, but the count-based fallback still picks "seating" and appends "more seats would recover the most." Internally inconsistent for a non-expert. Suppress the cents figure / recover-by-expanding advice when the service is loss-making.

B-3 — purchase-conversion is a thin residual channel · Note · Refined

  • Under menu-responsive demand, most would-be menu-rejecters are converted to non-arrivals scored as weak-attraction, so purchase-conversion is rarely PRIMARY (never mislabeled as seating/staffing). A design-intent clarification for the owners, not a defect.

B-4 — wrong-mix cents is a modeled opportunity estimate · Note · Confirmed

  • wrongMixLoss = underCovers × spendGap × 0.60 is an estimate compared against realized cpc losses; disjointness/max-not-sum still hold. The line already says "missed contribution," which is fair.

D-3 — Forecast ExpectedCovers can point the wrong direction vs the sim on seat/course changes · Low · Refuted (as a timing defect)

  • Forecast cover-direction disagrees with the sim in 5/9 paired tests, but every case traces to the documented kitchen-only realization keying (Forecast.cs:81-85, from PR M0 menu-responsive demand rewrite (Conditional-Pass; owner decision on residual) #7 / D-021) plus the separate economic model — not the timing primitive, whose seatCap/dwell math is directionally correct in all cases. The forecast is explicitly a low-confidence analytic estimate; the decision-relevant contribution direction agrees in the seat-tradeoff cases. Recommend the gate note that ExpectedCovers is a kitchen-ceiling estimate, not a realized-cover predictor.

D-4 — Seating-risk note is a kitchen-overload warning, not a seat-shortage warning · Note · Refined

  • Forecast.cs:228-229 fires with LostToWait (kitchen cannot feed seated parties), matching its wording, not with LostToCapacity (seat-queue overflow). Internally consistent; optionally clarify in docs.

E-1 — Premium-leverage rationale is metric-dependent and partly inverted · Medium · Refined

  • See §F. The builder's "leverage reduced 4×→3×" holds per-cover but inverts at the whole-service level in the enthusiast market, where premium contribution rose ~35–38% (emergent wait-walkout reduction). Correction is mechanically sound and strategy choice is valid; the disclosure is incomplete. Ask the builder to restate the effect as observed (or show the whole-service basis for ~4×). Not blocking.

E-2 — 3.67/2.96 covers/seat are controlled seat-turns, not realized · Low · Refined

  • Theoretical ServiceMinutes/dwell in 24-seat seat-bound rooms (disclosed by the builder); realized named-strategy covers/seat are ≈3.21/2.51. Label accordingly in the gate summary.

F-DOC-1 — Stale code comment: "~70% / 62%-converting" vs the actual 35% floor · Low · Confirmed

Confirmations that support readiness (Note/Confirmed): B-1 (conservation, 56,700+ runs, 0 violations), B-5 (one-course byte-identity + determinism), C-1..C-5 (timing primitive, distinct-course logic, seat coupling, table release, exact-2× dwell), D-1/D-2/D-5 (shared primitive, seats-past-kitchen-wall flat, ticket-time isolation), E-3/E-4 (generalist regret >10%, determinism/test integrity), F-SCOPE-1 (clean scope), F-TEST-1 (no test weakening).


J. Human-test ruling

Authorized with stated limitations.

Both corrections work; the remaining imperfections (A1 label, A2/A3 note edge cases, A4 note priority, E-1 rationale) do not teach false strategic rules; the generalist is a healthy fragile strategy; players can understand and test positioning from the CLI; only minor tuning/wording notes remain. Recommended (not required) before the gate: a small notes-polish pass (A1 caption, A2 unstaffed-kitchen warning, A3 no-mains phrasing) would raise fidelity on the very feature the gate tests — owner's call, since the core table is legible and the degenerate cases already surface loud 0-cover / negative-contribution signals.


K. Final gate

Verdict: Pass-with-notes
Action:  Continue

Continue authorizes the five-player M0 human gate. It does not authorize M1 and does not authorize merging.

Howard and Aaron should run the five-player uncoached M0 human gate using the reviewed PR #9 build (c360ebc).

Keep PR #9, the rewrite/correction chain, and the foundation unmerged until the human gate result is in; M1 remains separately unauthorized.


Independent review complete. Read-only throughout: no branch modified, no commit, no merge, no human tests run. This review reproduced every load-bearing claim on held-out seeds; where it disagreed with a sub-agent (A1 severity, E-1 status) the lead re-adjudicated with cited evidence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants