code wiki / (root) / nx_market.nx

nx_market.nx source

↩ module page · 137 lines · 7312 B

1// nx_market.nx -- the NISHI MARKET RESEARCHER: sees what the team can CAPITALIZE on (operator). It 2// maps the team's capabilities to OPPORTUNITIES and scores each by VALUE x FEASIBILITY -- but only 3// recommends ones the team can actually DELIVER (its capability is at the grade the opportunity 4// REQUIRES, per the Examiner). High-value opportunities we can't deliver yet are HONESTLY flagged 5// BUILD-FIRST (they feed the Futurist + the backlog), not sold as ready. So the team chases value it 6// can actually ship, and knows exactly what to build to unlock the rest. composes Examiner (grades) 7// + Futurist (timing). RACI: Market Researcher scores; Builder builds the unlock; Scientist publishes 8// the proof a buyer/critic needs. license_tier: ORIGINAL Refs: grade-gated opportunity scoring. 9 10import "nx_sclass_grader.nx" // SG_* tiers as the required/held capability grade 11import "nx_bom.nx" // mk_water_production_cost composes the REAL bill of materials 12 13// ===== UNIT ECONOMICS ================================================================================= 14// Added 2026-07-31. nx_market_test.nx specifies NINE mk_* functions that existed nowhere; this file held 15// only the mkt_* opportunity scorer, so the test could never once have passed. Kept in a named section 16// because scoring an opportunity and pricing a unit are different jobs sharing one import graph. 17// Every constant below is fixed by the test's assertions. 18 19const MK_PCT: i64 = 100 20const MK_MIN_MARGIN_PCT: i64 = 40 // below this the margin cannot absorb support + returns 21const MK_MAX_PAYBACK_DAYS: i64 = 180 // 6 months; past that a buyer stops believing the savings story 22 23// Production cost at volume = a bulk fraction of the hand-built prototype. 24func mk_production_unit_cost(prototype_cents: i64, bulk_pct: i64) -> i64 { 25 return prototype_cents * bulk_pct / MK_PCT 26} 27 28// Wright's law: every DOUBLING of cumulative volume retains retention_pct of the unit cost. Applied one 29// doubling at a time, truncating each step, rather than as a single power -- a real cost-down ladder is 30// renegotiated per volume tier, so the rounding happens at each rung and not once at the end. 31func mk_learning_cost(base_cents: i64, retention_pct: i64, doublings: i64) -> i64 { 32 var c: i64 = base_cents 33 var d: i64 = 0 34 while d < doublings { 35 c = c * retention_pct / MK_PCT 36 d = d + 1 37 } 38 return c 39} 40 41// Price for a target GROSS margin: price = cost / (1 - margin). 42func mk_price_for_margin(unit_cost_cents: i64, target_margin_pct: i64) -> i64 { 43 if target_margin_pct >= MK_PCT { return 0 - 1 } // 100% margin needs an infinite price -- REFUSE 44 if target_margin_pct < 0 { return 0 - 1 } 45 return unit_cost_cents * MK_PCT / (MK_PCT - target_margin_pct) 46} 47 48func mk_gross_margin_pct(price_cents: i64, unit_cost_cents: i64) -> i64 { 49 if price_cents <= 0 { return 0 - 1 } 50 return (price_cents - unit_cost_cents) * MK_PCT / price_cents 51} 52 53// Units that must sell to cover fixed cost. CEILING: a fraction of a unit pays for nothing, so 370.37 54// means you are still short until the 371st sells. 55func mk_break_even_units(fixed_cents: i64, price_cents: i64, unit_cost_cents: i64) -> i64 { 56 let contribution: i64 = price_cents - unit_cost_cents 57 if contribution <= 0 { return 0 - 1 } // sells at or below cost: NEVER breaks even, and -1 says so 58 return (fixed_cents + contribution - 1) / contribution 59} 60 61func mk_daily_savings_cents(liters_per_day: i64, alt_cost_per_l: i64, our_cost_per_l: i64) -> i64 { 62 return liters_per_day * (alt_cost_per_l - our_cost_per_l) 63} 64 65// Payback period, FLOOR -- and the asymmetry with mk_break_even_units above is deliberate, not an 66// oversight. Break-even is a REQUIRED COUNT of a discrete thing, so it rounds UP. Payback is a REPORTED 67// DURATION, quoted the way every payback figure is quoted, so it truncates. WORTH KNOWING: at floor the 68// buyer is not actually whole on day 66 (66 x 340 = 22440 < 22500) -- they are whole on day 67. The test 69// pins 66, so 66 is the contract; a stricter reading would use the same ceiling as break-even. 70func mk_payback_days(price_cents: i64, daily_savings_cents: i64) -> i64 { 71 if daily_savings_cents <= 0 { return 0 - 1 } // no savings: payback never arrives 72 return price_cents / daily_savings_cents 73} 74 75// Go-to-market verdict: healthy margin AND a payback a buyer will believe. Both bars are named constants 76// so "why 40?" has an answer that is not a number buried in a branch (rule 11). 77func mk_viable(margin_pct: i64, payback_days: i64) -> i64 { 78 if payback_days < 0 { return 0 } // never pays back 79 if margin_pct < MK_MIN_MARGIN_PCT { return 0 } 80 if payback_days > MK_MAX_PAYBACK_DAYS { return 0 } 81 return 1 82} 83 84// COMPOSES the real BOM: the prototype cost is not a literal here, it is whatever the catalog sums to, 85// so a part price change flows straight through to the production cost. 86func mk_water_production_cost(bulk_pct: i64) -> i64 { 87 let b: *NxBom = nx_bom_water_assemble() 88 let proto: i64 = b.total_cost_cents 89 sys_munmap(b as *u8, BOM_BOM_BYTES) 90 return mk_production_unit_cost(proto, bulk_pct) 91} 92 93const MKT_CAPITALIZE: i64 = 1 // valuable AND deliverable now -> go 94const MKT_BUILD_FIRST: i64 = 2 // valuable but capability not at the required grade -> build the unlock 95const MKT_SKIP: i64 = 3 // not valuable enough 96 97// can the team DELIVER this opportunity? its held capability grade >= the grade the opportunity needs. 98func mkt_can_deliver(capability_grade: i64, required_grade: i64) -> i64 { 99 if capability_grade >= required_grade { return 1 } 100 return 0 101} 102 103// disposition for one opportunity. 104func mkt_disposition(value: i64, value_floor: i64, capability_grade: i64, required_grade: i64) -> i64 { 105 if value < value_floor { return MKT_SKIP } 106 if mkt_can_deliver(capability_grade, required_grade) == 1 { return MKT_CAPITALIZE } 107 return MKT_BUILD_FIRST 108} 109 110// prioritization score (value x feasibility) -- only meaningful for deliverable opportunities. 111func mkt_score(value: i64, feasibility: i64) -> i64 { return value * feasibility } 112 113// the top DELIVERABLE opportunity (max value x feasibility among those we can ship now); -1 if none. 114func mkt_top_deliverable(n: i64, value: *i64, feasibility: *i64, grade: *i64, required: *i64, value_floor: i64) -> i64 { 115 var best: i64 = 0 - 1; var bestscore: i64 = 0 - 1; var i: i64 = 0 116 while i < n { 117 if mkt_disposition(value[i], value_floor, grade[i], required[i]) == MKT_CAPITALIZE { 118 let s: i64 = mkt_score(value[i], feasibility[i]) 119 if s > bestscore { bestscore = s; best = i } 120 } 121 i = i + 1 122 } 123 return best 124} 125 126// the highest-VALUE opportunity that is currently BLOCKED (build-first) -- names the unlock worth 127// building toward (the bridge from Market Researcher to Builder/Futurist). 128func mkt_top_unlock(n: i64, value: *i64, grade: *i64, required: *i64, value_floor: i64) -> i64 { 129 var best: i64 = 0 - 1; var bestv: i64 = 0 - 1; var i: i64 = 0 130 while i < n { 131 if mkt_disposition(value[i], value_floor, grade[i], required[i]) == MKT_BUILD_FIRST { 132 if value[i] > bestv { bestv = value[i]; best = i } 133 } 134 i = i + 1 135 } 136 return best 137}