code wiki / _hdl_build / nx_meal_effort.nx
nx_meal_effort.nx
buildroot/runtime/_hdl_build/nx_meal_effort.nx
about
nx_meal_effort.nx -- LIB: OPPORTUNITY COST. "Is cooking this actually the right use of my evening?"
The planner already knows what a dish COSTS in ingredients. That is only half a decision. The other half is
TIME -- and time is not a property of the recipe, it is a property of the PERSON COOKING IT. A cook who has
never dressed an onion takes two and a half times as long as one who has, so the same dish is a cheap
weeknight for one household and a bad trade for another. This engine makes that explicit, and it is the seam
where the skill coach changes the answer: get better at the knife and cooking-at-home genuinely wins more
often. Skill is not decoration here, it is a term in the arithmetic.
Options compared: SCRATCH (raw ingredients + your time), PREPPED (pre-cut fresh, less time, more money),
FROZEN (least time, least money, a quality call the engine does NOT pretend to make), RESTAURANT (no time,
most money). Alternatives are DATA rows, so adding "meal kit" is a row, not a recompile (rule 11).
THREE HONESTY INVARIANTS, each gate-enforced, because each one silently flatters cooking at home:
1. AN UNASSESSED SKILL IS NOT AN EXPERT SKILL. If we have never measured someone chopping, ef_step_time
returns -1 and the step is counted as UNKNOWN. Defaulting an unmeasured cook to "fast" is how a planner
tells a beginner a two-hour braise is a thirty-minute dish.
2. NO INVENTED WAGE. If a household has not said what an hour of theirs is worth, the comparison reports
MONEY ONLY and says so. Picking a plausible-looking hourly rate would make the ranking an opinion wearing
arithmetic's clothes.
3. ONLY FULLY-KNOWN OPTIONS MAY BE RANKED. An option missing either its money or its time is reported, never
ranked -- the same coverage rule that stops a shop carrying one ingredient winning the basket.
Integer cents and whole seconds throughout; no floats anywhere.
Schema (prefix passed in, e.g. knowledge/store/meal-):
meal:skill:<sid> -> name <t> parent-sid <t> what it is (the broad -> specific skill tree)
meal:skills -> TAB list of skill ids
meal:steps:<rid> -> step count
meal:step:<rid>:<n> -> label <t> skill-sid <t> base_seconds (base = a competent cook's pace)
meal:level:<unit>:<sid> -> assessed level 0..4 (absent = NEVER ASSESSED, not zero)
meal:tmul:<level> -> time multiplier in permille (data-driven, never hardcoded)
meal:timevalue:<unit> -> cents per hour (absent = not valued, not free)
meal:alt:<rid>:<kind> -> price_cents <t> minutes <t> source <t> date
license_tier: ORIGINAL No hw writes (Rule 26).
dependencies 4 imports · 3 importers
imports: nx_meal_plan.nxnx_food_science.nxnx_seg_store.nxnx_syscalls.nx
imported by: nx_meal_effort_gate.nxnx_meal_skills_page.nxnx_skills_emit.nx
structs
| none |
consts
| 44 | const EF_TAB: i64 = 9 |
| 45 | const EF_KEYCAP: i64 = 200 |
| 46 | const EF_VALCAP: i64 = 2048 |
| 47 | const EF_PERMILLE: i64 = 1000 |
| 48 | const EF_SECS_PER_HOUR: i64 = 3600 |
| 49 | const EF_MAXSTEPS: i64 = 128 |
| 50 | const EF_MAXDEPTH: i64 = 8 |
| 53 | const EF_S_NAME: i64 = 0 |
| 54 | const EF_S_PARENT: i64 = 1 |
| 55 | const EF_S_ABOUT: i64 = 2 |
| 58 | const EF_P_LABEL: i64 = 0 |
| 59 | const EF_P_SKILL: i64 = 1 |
| 60 | const EF_P_SECONDS: i64 = 2 |
| 63 | const EF_A_PRICE: i64 = 0 |
| 64 | const EF_A_MINUTES: i64 = 1 |
| 65 | const EF_A_SOURCE: i64 = 2 |
| 66 | const EF_A_DATE: i64 = 3 |
| 69 | const EF_O_MONEY: i64 = 0 |
| 70 | const EF_O_SECONDS: i64 = 1 |
| 71 | const EF_O_TIMECOST: i64 = 2 |
| 72 | const EF_O_TOTAL: i64 = 3 |
| 73 | const EF_O_KNOWN: i64 = 4 |
| 74 | const EF_O_KIND: i64 = 5 |
| 75 | const EF_O_SLOTS: i64 = 6 |
| 78 | const EF_UNASSESSED: i64 = 0 - 1 |
functions
| 83 | func ef_keybuf() -> *u8 { if EF_KB == 0 { EF_KB = sys_mmap(EF_KEYCAP) as i64 } return EF_KB as *u8 } called by 7: ef_skill_fieldef_step_countef_step_fieldef_levelef_tmulef_timevalue+1 calls 1: sys_mmap |
| 84 | func ef_pq() -> *i64 { if EF_PQ == 0 { EF_PQ = sys_mmap(16) as i64 } return EF_PQ as *i64 } |
| 85 | func ef_lq() -> *i64 { if EF_LQ == 0 { EF_LQ = sys_mmap(16) as i64 } return EF_LQ as *i64 } |
| 87 | func ef_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 92 | func ef_empty() -> *u8 |
| 99 | func ef_num_key(out: *u8, o: i64, v: i64) -> i64 |
| 112 | func ef_field_at(prefix: *u8, key: *u8, f: i64, out: *u8) -> i64 called by 8: ef_skill_fieldef_add_stepef_step_countef_step_fieldef_levelef_tmul+2 calls 4: ef_pqef_lqss_getfd_field |
| 121 | func ef_skill_key(sid: *u8, out: *u8) -> i64 |
| 129 | func ef_add_skill(prefix: *u8, sid: *u8, name: *u8, parent: *u8, about: *u8) -> i64 |
| 143 | func ef_skill_field(prefix: *u8, sid: *u8, f: i64, out: *u8) -> i64 |
| 151 | func ef_skill_path(prefix: *u8, sid: *u8, out: *u8) -> i64 |
| 189 | func ef_step_key(rid: *u8, idx: i64, out: *u8) -> i64 |
| 197 | func ef_steps_key(rid: *u8, out: *u8) -> i64 |
| 205 | func ef_add_step(prefix: *u8, rid: *u8, idx: i64, label: *u8, skill: *u8, base_seconds: i64) -> i64 |
| 230 | func ef_step_count(prefix: *u8, rid: *u8) -> i64 |
| 238 | func ef_step_field(prefix: *u8, rid: *u8, idx: i64, f: i64, out: *u8) -> i64 |
| 244 | func ef_step_base(prefix: *u8, rid: *u8, idx: i64) -> i64 |
| 252 | func ef_level_key(unit: *u8, sid: *u8, out: *u8) -> i64 |
| 261 | func ef_set_level(prefix: *u8, unit: *u8, sid: *u8, level: i64) -> i64 |
| 272 | func ef_level(prefix: *u8, unit: *u8, sid: *u8) -> i64 |
| 280 | func ef_set_tmul(prefix: *u8, level: i64, permille: i64) -> i64 |
| 291 | func ef_tmul(prefix: *u8, level: i64) -> i64 |
| 301 | func ef_set_timevalue(prefix: *u8, unit: *u8, cents_per_hour: i64) -> i64 |
| 313 | func ef_timevalue(prefix: *u8, unit: *u8) -> i64 called by 2: ef_compareef_time_is_valued calls 6: ef_keybufas_appendsys_mmapef_field_atfd_atoief_len |
| 327 | func ef_step_time(prefix: *u8, unit: *u8, rid: *u8, idx: i64) -> i64 |
| 341 | func ef_cook_time(prefix: *u8, unit: *u8, rid: *u8, out: *i64) -> i64 |
| 356 | func ef_time_cost(seconds: i64, cents_per_hour: i64) -> i64 |
| 364 | func ef_alt_key(rid: *u8, kind: *u8, out: *u8) -> i64 |
| 373 | func ef_add_alt(prefix: *u8, rid: *u8, kind: *u8, price_cents: i64, minutes: i64, source: *u8, date: *u8) -> i64 |
| 386 | func ef_alt_field(prefix: *u8, rid: *u8, kind: *u8, f: i64, out: *u8) -> i64 |
| 392 | func ef_alt_num(prefix: *u8, rid: *u8, kind: *u8, f: i64) -> i64 |
| 401 | func ef_opt(out: *i64, slot: i64, kind: *u8, money: i64, seconds: i64, rate: i64) -> i64 |
| 427 | func ef_compare(prefix: *u8, unit: *u8, rid: *u8, scratch_money: i64, scratch_complete: i64, out: *i64, max: i64) -> i64 |
| 460 | func ef_best(out: *i64, n: i64) -> i64 |
| 478 | func ef_unranked(out: *i64, n: i64) -> i64 |
| 487 | func ef_time_is_valued(prefix: *u8, unit: *u8) -> i64 |