code wiki / (root) / nx_commons_price.nx

nx_commons_price.nx source

↩ module page · 107 lines · 6113 B

1// nx_commons_price.nx -- COMMENSURATION: how do you compare an hour of shovelling to a borrowed GPU? 2// 3// KNOWN GOOD MATCHED, and it is in this estate already: nx_meal_effort. Its three honesty invariants 4// are the whole spine of this problem and are adopted verbatim in spirit: 5// 1. AN UNASSESSED SKILL IS NOT AN EXPERT SKILL -- return UNKNOWN, never default to a flattering value. 6// 2. NO INVENTED WAGE -- "picking a plausible-looking hourly rate would make the ranking AN OPINION 7// WEARING ARITHMETIC'S CLOTHES". If the commons has not DECLARED a rate, there is no rate. 8// 3. ONLY FULLY-KNOWN OPTIONS MAY BE RANKED -- a partial sum must never be presented as a total. 9// Real-world known good for the base unit: TIME BANKING (Cahn 1980; hOurworld / TimeBanks USA, decades 10// of field deployment). Its defining move is that it REFUSES to price skill: one hour is one hour. 11// 12// WHY TIME AND NOT MARKET PRICE -- this is the whole design and it is testable. 13// Any commensuration that prices contributions at market rate RE-IMPORTS THE EXACT INEQUALITY THE 14// COMMONS EXISTS TO ESCAPE. Price a lawyer's hour at 10 janitor-hours and the commons faithfully 15// reproduces the strip mine, in its own currency, with a clean conscience. The market rate is not a 16// neutral measuring stick; it is the thing under dispute. So the base unit is the one unit every 17// person has in equal supply: an hour. 18// 19// THE IMPROVEMENT OVER NAIVE TIME BANKING (its known failure, addressed rather than ignored): 20// flat 1:1 gives no signal for scarce, hazardous or unsocial work -- if one neighbour can fix the 21// boiler at 2am in the rain, 1:1 says that hour is worth a pleasant hour of weeding, and the boiler 22// does not get fixed. So multipliers exist -- but they are DECLARED BY THE COMMONS, recorded as data, 23// and BOUNDED: 24// * CP_MULT_FLOOR_PERMIL = 1000 -- NOBODY'S HOUR IS EVER WORTH LESS THAN AN HOUR. There is no 25// downward adjustment at all, because that is the lever every hierarchy reaches for first. 26// * CP_MULT_CAP_PERMIL = 3000 -- and no hour may be worth more than three. 27// ★ THE SPREAD BETWEEN THE MOST AND LEAST VALUED HOUR IS THEREFORE BOUNDED AT 3:1 BY CONSTRUCTION. 28// For scale: a hedge-fund hour against a cleaner's hour is on the order of 1000:1. A commons cannot 29// vote itself into that shape here, because the arithmetic will not represent it. 30// 31// THE THIRD STATE IS MANDATORY. cp_* returns CP_UNPRICED for anything the commons has not declared a 32// rate for, and any total containing an unpriced part is itself UNPRICED. A pricer that can only ever 33// return a number WILL fabricate one -- the same lesson nx_gonogo paid for when a two-state verdict 34// had to grow a third. 35// 36// Integer permille throughout, no floats. Rates are DATA (law 11), never literals in this file. 37// license_tier: ORIGINAL No hw writes (Rule 26). 38import "nx_syscalls.nx" 39 40const CP_UNPRICED: i64 = 0 - 1 // the third state -- absent evidence, not zero value 41const CP_SEC_PER_HOUR: i64 = 3600 42const CP_PERMIL: i64 = 1000 43const CP_MULT_FLOOR_PERMIL: i64 = 1000 // no hour is worth LESS than an hour. No exceptions. 44const CP_MULT_CAP_PERMIL: i64 = 3000 // and none is worth more than three. 45 46// A declared multiplier, clamped into the bounded band. A commons may declare 8x; it gets 3x. 47// Clamping rather than refusing is deliberate: a refusal here would let one bad row disable pricing 48// for a whole kind, and the bound is the protection, not the rejection. 49func cp_clamp_mult(declared_permil: i64) -> i64 { 50 if declared_permil < CP_MULT_FLOOR_PERMIL { return CP_MULT_FLOOR_PERMIL } 51 if declared_permil > CP_MULT_CAP_PERMIL { return CP_MULT_CAP_PERMIL } 52 return declared_permil 53} 54 55// TIME -> commons units. One hour at 1.0x is exactly CP_PERMIL units, so the unit IS the hour. 56// mult_permil must come from a declared row; pass CP_UNPRICED and you get UNPRICED back. 57func cp_price_time(seconds: i64, mult_permil: i64) -> i64 { 58 if seconds < 0 { return CP_UNPRICED } 59 if mult_permil == CP_UNPRICED { return CP_UNPRICED } 60 let m: i64 = cp_clamp_mult(mult_permil) 61 return (seconds * m) / CP_SEC_PER_HOUR 62} 63 64// NON-TIME kinds (bytes, kWh, items, GPU-seconds). rate_permil = declared commons-units per unit of 65// this kind. There is NO default and there will never be one: an undeclared kind is UNPRICED. 66// This is invariant 2 -- no invented wage -- generalised past wages. 67func cp_price_kind(qty: i64, rate_permil: i64) -> i64 { 68 if qty < 0 { return CP_UNPRICED } 69 if rate_permil == CP_UNPRICED { return CP_UNPRICED } 70 if rate_permil < 0 { return CP_UNPRICED } 71 return (qty * rate_permil) / CP_PERMIL 72} 73 74// A total is UNPRICED if ANY component is. Invariant 3: a partial sum presented as a total is the 75// coverage lie, and it always flatters whoever contributed the priced part. 76func cp_total(parts: *i64, n: i64) -> i64 { 77 var sum: i64 = 0 78 var i: i64 = 0 79 while i < n { 80 if parts[i] == CP_UNPRICED { return CP_UNPRICED } 81 sum = sum + parts[i] 82 i = i + 1 83 } 84 return sum 85} 86 87// Is a whole contribution priceable, or must it be reported rather than ranked? 88func cp_is_priced(v: i64) -> i64 { 89 if v == CP_UNPRICED { return 0 } 90 return 1 91} 92 93// The bounded spread, as a measurable property rather than a claim in a comment. 94func cp_spread_permil() -> i64 { 95 return (CP_MULT_CAP_PERMIL * CP_PERMIL) / CP_MULT_FLOOR_PERMIL 96} 97 98// ---- NEGATIVE CONTROL: the market-rate pricer ------------------------------------------------- 99// Kept in the library ON PURPOSE, exactly as nx_connect_match_lib keeps the arithmetic mean beside 100// the harmonic mean and nx_commons_lib keeps the pairwise rule beside the network rule. The gate 101// needs it to prove that the CHOICE of base unit is what does the work -- otherwise "time banking is 102// fairer" is an assertion. Never call this to price a real contribution. 103func cp_market_price(seconds: i64, hourly_cents: i64) -> i64 { 104 if seconds < 0 { return CP_UNPRICED } 105 if hourly_cents < 0 { return CP_UNPRICED } 106 return (seconds * hourly_cents) / CP_SEC_PER_HOUR 107}