nx_meal_emit.nx source
↩ module page · 187 lines · 11035 B
1// nx_meal_emit.nx -- MAIN: SEED the meal stores and RENDER the page. Idempotent end to end (rule 10): every
2// seed is a value-compare put that writes nothing when unchanged, and the render simply overwrites its
3// artifact. Run it twice, get the same estate.
4//
5// IT OWNS STORES AND MARKUP, AND NOTHING ELSE. Preserving the origin page is nx_meal_capture's job, in its own
6// organ, because the decompressor it needs drags nx_runtime/nx_tier/nx_deflate/nx_crc32 in behind it and that
7// destabilised the seg-store seeding here -- untouched code that had been green minutes before. Splitting on
8// the job boundary (rule 9) was the fix, not a workaround: this organ reads the finished WARC and never
9// decodes anything.
10//
11// It writes the page to a FILE and stops. Publishing is a separate deliberate act through nx_site_publish --
12// a renderer that also deploys is a renderer you cannot safely run just to look at the output.
13//
14// argv[1] = output html path (default /tmp/kitchen.html)
15// license_tier: ORIGINAL No hw writes (Rule 26).
16import "_hdl_build/nx_meal_page.nx"
17import "_hdl_build/nx_meal_source.nx"
18import "_hdl_build/nx_meal_cost.nx"
19import "_hdl_build/nx_meal_plan.nx"
20import "_hdl_build/nx_adnet_slot.nx"
21import "nx_syscalls.nx"
22const ME_MAGIC_1099: i64 = 1099
23const ME_MAGIC_1249: i64 = 1249
24
25const ME_PAGE: i64 = 262144
26const ME_WARC: i64 = 4194304
27const ME_MODE: i64 = 420
28const ME_DAY: i64 = 20671
29
30// CONFIG SEAM (rule 11): the floor a price must clear BELOW the area median before the page calls it a
31// bargain. One named constant rather than a literal buried in a comparison; belongs in svc-config the moment
32// a second surface needs it.
33const ME_SALE_FLOOR_CENTS: i64 = 50
34
35// the loopback port nx_site_intake_serve binds (proxied by the edge row `nishifamily.com /intake 8033`)
36const ME_INTAKE_PORT: i64 = 8033
37
38func me_p(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
39func me_n(v: i64) -> i64 {
40 let b: *u8 = sys_mmap(32)
41 let t: *u8 = sys_mmap(32)
42 var m: i64 = v
43 var k: i64 = 0
44 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m }
45 if m == 0 { t[0] = 48 as u8; k = 1 }
46 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
47 var i: i64 = 0
48 while i < k { b[i] = t[k - 1 - i]; i = i + 1 }
49 sys_write(1, b, k)
50 return 0
51}
52
53// Is anything actually LISTENING for the contact forms? A TCP connect is a MEASUREMENT; a config flag is a
54// belief. The page renders a form only when this returns 1, so a dead form can never ship by accident.
55func me_port_open(port: i64) -> i64 {
56 let fd: i64 = sys_socket(2, 1, 0)
57 if fd < 0 { return 0 }
58 let addr: *u8 = sys_mmap(16)
59 var i: i64 = 0
60 while i < 16 { addr[i] = 0 as u8; i = i + 1 }
61 addr[0] = 2 as u8
62 addr[2] = ((port >> 8) & 255) as u8
63 addr[3] = (port & 255) as u8
64 addr[4] = 127 as u8
65 addr[7] = 1 as u8
66 let r: i64 = sys_connect(fd, addr, 16)
67 sys_close(fd)
68 if r < 0 { return 0 }
69 return 1
70}
71
72func me_write_file(path: *u8, buf: *u8, n: i64) -> i64 {
73 let fd: i64 = sys_openat_wr(path, ME_MODE)
74 if fd < 0 { return 0 - 1 }
75 sys_write(fd, buf, n)
76 sys_close(fd)
77 return n
78}
79
80func main(argc: i64, argv: *i64) -> i64 {
81 let meal: *u8 = "knowledge/store/meal-" as *u8
82 let inv: *u8 = "knowledge/store/inv-" as *u8
83 let price: *u8 = "knowledge/store/price-" as *u8
84 let warcpath: *u8 = "knowledge/store/meal.warc" as *u8
85 let adconf: *u8 = "meal_ad_inventory.conf" as *u8
86 let host: *u8 = "nishifamily.com" as *u8
87 let url: *u8 = "https://en.wikibooks.org/wiki/Cookbook:Beef_Stew" as *u8
88
89 var outpath: *u8 = "/tmp/kitchen.html" as *u8
90 if argc > 1 { outpath = argv[1] as *u8 }
91
92 // ---------------- SEED: recipes ----------------
93 mp_add_recipe(meal, "beefstew" as *u8, "Classic Beef Stew" as *u8, "Chuck browned hard, then three hours at a bare tremble until the connective tissue gives up and the broth turns glossy." as *u8, 6, "beef onion carrot potato celery garlic thyme stock redwine" as *u8)
94 mp_add_recipe(meal, "potroast" as *u8, "Sunday Pot Roast" as *u8, "The same braise, left whole and carved at the table." as *u8, 6, "beef onion carrot potato celery garlic stock" as *u8)
95 mp_add_recipe(meal, "veggiesoup" as *u8, "Garden Vegetable Soup" as *u8, "Everything the stew uses except the beef and the wine." as *u8, 4, "onion carrot celery garlic thyme stock potato" as *u8)
96 mp_add_recipe(meal, "aromaticstock" as *u8, "Aromatic Stock" as *u8, "The base every braise on this page is built from." as *u8, 8, "onion carrot celery garlic thyme stock" as *u8)
97 mp_add_recipe(meal, "steakfrites" as *u8, "Steak Frites" as *u8, "Same beef, same potato, thirty minutes instead of three hours." as *u8, 2, "beef potato garlic thyme" as *u8)
98
99 // ---------------- SEED: the ingredient catalogue ----------------
100 // Ids are join keys shared by pantry rows, price observations and recipe lists, so they stay terse and
101 // lowercase. Their human names live HERE, not in the pantry -- otherwise an ingredient this kitchen does
102 // not happen to stock would have no name at all and the page would print a raw key at a reader.
103 mp_add_ingredient(meal, "beef" as *u8, "Beef chuck" as *u8)
104 mp_add_ingredient(meal, "onion" as *u8, "Yellow onion" as *u8)
105 mp_add_ingredient(meal, "carrot" as *u8, "Carrot" as *u8)
106 mp_add_ingredient(meal, "potato" as *u8, "Waxy potato" as *u8)
107 mp_add_ingredient(meal, "celery" as *u8, "Celery" as *u8)
108 mp_add_ingredient(meal, "garlic" as *u8, "Garlic" as *u8)
109 mp_add_ingredient(meal, "thyme" as *u8, "Thyme" as *u8)
110 mp_add_ingredient(meal, "stock" as *u8, "Beef stock" as *u8)
111 mp_add_ingredient(meal, "redwine" as *u8, "Dry red wine" as *u8)
112
113 // ---------------- SEED: units (family / person / restaurant, one engine) ----------------
114 mp_add_unit(meal, "wests" as *u8, "The West Family" as *u8, "family" as *u8, "wests" as *u8, "seattle" as *u8)
115 mp_add_unit(meal, "ana" as *u8, "Ana" as *u8, "person" as *u8, "ana" as *u8, "seattle" as *u8)
116 mp_add_unit(meal, "spicehouse" as *u8, "Sichuan Spice House" as *u8, "restaurant" as *u8, "spicehouse" as *u8, "portland" as *u8)
117
118 // ---------------- SEED: the family's pantry ----------------
119 iv_add(inv, "wests" as *u8, "onion" as *u8, "Yellow onion" as *u8, "produce" as *u8, 4, "each" as *u8, "pantry" as *u8, 90, 0, ed_empty())
120 iv_add(inv, "wests" as *u8, "carrot" as *u8, "Carrot" as *u8, "produce" as *u8, 6, "each" as *u8, "crisper" as *u8, 140, 0, ed_empty())
121 iv_add(inv, "wests" as *u8, "celery" as *u8, "Celery" as *u8, "produce" as *u8, 1, "bunch" as *u8, "crisper" as *u8, 180, 0, ed_empty())
122 iv_add(inv, "wests" as *u8, "garlic" as *u8, "Garlic" as *u8, "produce" as *u8, 3, "bulb" as *u8, "pantry" as *u8, 90, 0, ed_empty())
123 iv_add(inv, "wests" as *u8, "thyme" as *u8, "Thyme" as *u8, "herb" as *u8, 1, "bunch" as *u8, "fridge door" as *u8, 240, 0, ed_empty())
124 iv_add(inv, "wests" as *u8, "stock" as *u8, "Beef stock" as *u8, "pantry" as *u8, 2, "quart" as *u8, "pantry" as *u8, 380, 0, ed_empty())
125
126 // ---------------- SEED: area stores + dated price observations ----------------
127 pr_add_store(price, "bigmart" as *u8, "BigMart Rainier" as *u8, "BigMart" as *u8, "seattle" as *u8, "WA" as *u8, "weekly circular, 2026-08-04" as *u8)
128 pr_add_store(price, "corner" as *u8, "Corner Market" as *u8, "independent" as *u8, "seattle" as *u8, "WA" as *u8, "shelf tag" as *u8)
129 pr_add_store(price, "pikeplace" as *u8, "Pike Place Butcher" as *u8, "independent" as *u8, "seattle" as *u8, "WA" as *u8, "counter board" as *u8)
130
131 pr_set_price(price, "bigmart" as *u8, "beef" as *u8, ME_MAGIC_1099, "2026-08-04" as *u8, "lb" as *u8, "weekly circular" as *u8)
132 pr_set_price(price, "bigmart" as *u8, "potato" as *u8, 349, "2026-08-04" as *u8, "5 lb bag" as *u8, "weekly circular" as *u8)
133 pr_set_price(price, "bigmart" as *u8, "redwine" as *u8, 899, "2026-08-04" as *u8, "750 ml" as *u8, "weekly circular" as *u8)
134 pr_set_price(price, "corner" as *u8, "beef" as *u8, ME_MAGIC_1249, "2026-08-05" as *u8, "lb" as *u8, "shelf tag" as *u8)
135 pr_set_price(price, "corner" as *u8, "potato" as *u8, 279, "2026-08-05" as *u8, "5 lb bag" as *u8, "shelf tag" as *u8)
136 pr_set_price(price, "corner" as *u8, "redwine" as *u8, ME_MAGIC_1099, "2026-08-05" as *u8, "750 ml" as *u8, "shelf tag" as *u8)
137 // Pike Place has the CHEAPEST beef and wine in the area -- and no potatoes at all. The coverage guard is
138 // what stops it winning a basket it cannot fill.
139 pr_set_price(price, "pikeplace" as *u8, "beef" as *u8, 899, "2026-08-06" as *u8, "lb" as *u8, "counter board" as *u8)
140 pr_set_price(price, "pikeplace" as *u8, "redwine" as *u8, 799, "2026-08-06" as *u8, "750 ml" as *u8, "counter board" as *u8)
141
142 // ---------------- SEED: provenance ----------------
143 ms_record(meal, "beefstew" as *u8, "Classic Beef Stew" as *u8, "Wikibooks Cookbook" as *u8, url, "CC BY-SA 3.0" as *u8, ME_DAY, 0)
144 me_p(" seed: complete\n" as *u8)
145
146 // ---------------- the preserved archive (written by nx_meal_capture; read-only here) ----------------
147 let warc: *u8 = sys_mmap(ME_WARC)
148 var wl: i64 = wa_load(warcpath, warc, ME_WARC)
149 if wl < 0 { wl = 0 }
150 if ms_is_archived(warc, wl, url) == 1 {
151 me_p(" archive: source preserved, " as *u8); me_n(ms_archive_bytes(warc, wl, url)); me_p(" bytes retrievable\n" as *u8)
152 } else {
153 me_p(" archive: source NOT preserved -- the page will say the link can rot\n" as *u8)
154 }
155
156 // ---------------- the sponsor inventory, host-gated exactly as the ad slot demands ----------------
157 let box: *i64 = sys_mmap(16) as *i64
158 box[0] = 0
159 let adinv: *u8 = sys_read_file(adconf, box)
160 var adlen: i64 = box[0]
161 if adlen > 0 {
162 if aslot_host_ok(adinv, adlen, host) == 0 {
163 adlen = 0
164 me_p(" sponsor: no @host row for this vhost -- slot omitted (fail-closed)\n" as *u8)
165 }
166 }
167
168 // ---------------- RENDER ----------------
169 let page: *u8 = sys_mmap(ME_PAGE)
170 let intake_live: i64 = me_port_open(ME_INTAKE_PORT)
171 if intake_live == 1 {
172 me_p(" intake: :8033 is answering -- the contact forms will render\n" as *u8)
173 } else {
174 me_p(" intake: :8033 NOT listening -- forms SUPPRESSED, the page will say so rather than post into the void\n" as *u8)
175 }
176 let n: i64 = mpg_render(page, meal, inv, price, "beefstew" as *u8, "wests" as *u8, warc, wl, adinv, adlen, sys_now_realtime_sec(), ME_SALE_FLOOR_CENTS, "kitchen" as *u8, "/kitchen/beef-stew" as *u8, intake_live)
177 if me_write_file(outpath, page, n) < 0 {
178 me_p("NX-MEAL-EMIT FAIL cannot write " as *u8); me_p(outpath); me_p("\n" as *u8)
179 sys_exit(1)
180 return 1
181 }
182 me_p("NX-MEAL-EMIT OK bytes=" as *u8); me_n(n)
183 me_p(" warc=" as *u8); me_n(wl)
184 me_p(" path=" as *u8); me_p(outpath); me_p("\n" as *u8)
185 sys_exit(0)
186 return 0
187}