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}