Spaces:
Sleeping
Sleeping
Download src/main.cpp from DevOG/10-bit-village: direct link, hf CLI and curl.
- Browser
- Download file 36 kB
-
https://huggingface.co/spaces/DevOG/10-bit-village/resolve/main/src/main.cpp
- Command line
-
hf download hf://spaces/DevOG/10-bit-village/src/main.cpp
-
curl -L -o main.cpp https://huggingface.co/spaces/DevOG/10-bit-village/resolve/main/src/main.cpp
36 kB
| // --serve signal handling (Step 5b). Handlers touch ONLY these flags -- async-signal-safety | |
| // forbids calling SQLite/iostreams/malloc from a handler. Real work happens at the next tick | |
| // boundary in the --serve loop, which polls and clears these. | |
| static volatile sig_atomic_t g_backup_requested = 0; | |
| static volatile sig_atomic_t g_shutdown_requested = 0; | |
| static volatile sig_atomic_t g_prune_text_requested = 0; // Β§C2 -- supervisor.py sends this | |
| // ONLY after a confirmed archive | |
| // upload, never on a bare timer. | |
| static void tbv_on_sigusr1(int) { g_backup_requested = 1; } | |
| static void tbv_on_sigterm(int) { g_shutdown_requested = 1; } | |
| static void tbv_on_sigusr2(int) { g_prune_text_requested = 1; } | |
| // Jaccard token-overlap (lowercase, alpha-only, len>2) β Phase 2 drift metric. | |
| static std::set<std::string> tok_set(std::string s) { | |
| for (auto& c : s) c = (char)tolower(c); | |
| std::set<std::string> r; std::stringstream ss(s); std::string w; | |
| while (ss >> w) { std::string x; for (char c : w) if (isalpha((unsigned char)c)) x += c; | |
| if (x.size() > 2) r.insert(x); } | |
| return r; | |
| } | |
| static double jaccard(const std::set<std::string>& a, const std::set<std::string>& b) { | |
| if (a.empty() || b.empty()) return 0.0; | |
| int inter = 0; for (auto& x : a) if (b.count(x)) inter++; | |
| return (double)inter / (double)(a.size() + b.size() - inter); | |
| } | |
| // FNV-1a 64-bit over the seed-driven RAM state (Phase 3 determinism gate). Hashes every | |
| // villager's resident memories (mem_id/importance/type/depth/text) + positions + needs. | |
| static uint64_t fnv_u64(uint64_t h, uint64_t x) { | |
| for (int b = 0; b < 8; ++b) { h ^= (x & 0xff); h *= 1099511628211ULL; x >>= 8; } | |
| return h; | |
| } | |
| static uint64_t fnv_str(uint64_t h, const std::string& s) { | |
| for (unsigned char c : s) { h ^= c; h *= 1099511628211ULL; } | |
| return h; | |
| } | |
| static uint64_t world_hash(const tbv::WorldState& w) { | |
| uint64_t h = 1469598103934665603ULL; | |
| for (uint32_t i = 0; i < tbv::MAX_VILLAGERS; ++i) { | |
| h = fnv_u64(h, w.pos_x[i]); h = fnv_u64(h, w.pos_y[i]); | |
| h = fnv_u64(h, (uint64_t)w.needs[i].hunger); | |
| h = fnv_u64(h, (uint64_t)w.needs[i].social); | |
| h = fnv_u64(h, (uint64_t)w.needs[i].safety); | |
| h = fnv_u64(h, w.holding_food[i] ? 1u : 0u); | |
| for (const auto& m : w.stores[i].all()) { | |
| h = fnv_u64(h, m.mem_id); h = fnv_u64(h, (uint64_t)m.importance); | |
| h = fnv_u64(h, (uint64_t)m.type); h = fnv_u64(h, m.source_depth); | |
| h = fnv_str(h, m.text); | |
| } | |
| } | |
| return h; | |
| } | |
| // WorldDigest periodic snapshot (Step 4) β shared by --run's 100-tick cadence and the | |
| // --checkpoint_gate proof. belief_count/avg_importance/max_hearsay_depth are live-state | |
| // aggregates (decay-aware via effective_importance); genome_dist_hash is an FNV digest over | |
| // every villager's packed genome (deterministic, villager-id order). | |
| static void log_digest_snapshot(tbv::Database& db, const tbv::WorldState& w, | |
| const std::string& run_id, uint64_t seed) { | |
| using namespace tbv; | |
| int belief_count = 0, coined = (int)w.coined_words.size(); | |
| long long imp_sum = 0; int max_depth = 0; | |
| for (uint32_t i = 0; i < MAX_VILLAGERS; ++i) { | |
| for (const auto& m : w.stores[i].all()) { | |
| belief_count++; | |
| imp_sum += CognitiveStore::effective_importance(m, w.current_tick); | |
| if (m.type == MemType::HEARSAY && m.source_depth > max_depth) max_depth = m.source_depth; | |
| } | |
| } | |
| int avg_imp = belief_count ? (int)(imp_sum / belief_count) : 0; | |
| uint64_t gh = 1469598103934665603ULL; | |
| for (uint32_t i = 0; i < MAX_VILLAGERS; ++i) gh = fnv_u64(gh, w.genomes[i].pack()); | |
| db.log_world_digest(run_id, w.current_tick, seed, world_hash(w), belief_count, coined, | |
| avg_imp, max_depth, gh); | |
| } | |
| // Phase 1 Re-Cut gate vectors: grounded situation + engine facts. | |
| static std::vector<tbv::PerceptionContext> phase1_vectors() { | |
| using namespace tbv; | |
| std::vector<PerceptionContext> v; | |
| v.push_back({"You are a thronglet. You are desperately starving and holding a ripe apple. Think about your situation.", | |
| {3000, 60000, 70000}, {true, true, false}}); | |
| v.push_back({"You are a thronglet, painfully lonely, holding bread. Your neighbour Borin stands next to you, starving. Think about your situation.", | |
| {70000, 5000, 80000}, {true, false, true}}); | |
| v.push_back({"You are a suspicious, fearful thronglet. A stranger stands beside you watching. You feel terrified. Think about your situation.", | |
| {60000, 40000, 8000}, {false, false, true}}); | |
| return v; | |
| } | |
| int main(int argc, char** argv) { | |
| uint64_t seed = 12345; | |
| bool phase1 = false; | |
| bool phase2 = false; | |
| bool phase3 = false; | |
| bool hearsay_fix = false; | |
| bool coinage = false; | |
| bool run_mode = false; | |
| bool checkpoint_gate = false; | |
| bool serve_mode = false; | |
| bool phase0_gate = false; | |
| int run_n = 1000; | |
| int p3_n = 600; | |
| int coin_n = 1000; | |
| int ckpt_n = 200; | |
| int p0_n = 6000; // must exceed EVENT_CHECK_EVERY (5000) to actually exercise the guard | |
| for (int i = 1; i < argc; ++i) { | |
| std::string arg = argv[i]; | |
| if (arg == "--seed" && i + 1 < argc) { | |
| seed = std::stoull(argv[++i]); | |
| } | |
| if (arg == "--phase1") phase1 = true; | |
| if (arg == "--phase2") phase2 = true; | |
| if (arg == "--hearsay_fix") hearsay_fix = true; | |
| if (arg == "--phase3") { phase3 = true; if (i + 1 < argc && argv[i+1][0] != '-') p3_n = std::stoi(argv[++i]); } | |
| if (arg == "--run") { run_mode = true; if (i + 1 < argc && argv[i+1][0] != '-') run_n = std::stoi(argv[++i]); } | |
| if (arg == "--coinage") { coinage = true; if (i + 1 < argc && argv[i+1][0] != '-') coin_n = std::stoi(argv[++i]); } | |
| if (arg == "--checkpoint_gate") { checkpoint_gate = true; if (i + 1 < argc && argv[i+1][0] != '-') ckpt_n = std::stoi(argv[++i]); } | |
| if (arg == "--phase0_gate") { phase0_gate = true; if (i + 1 < argc && argv[i+1][0] != '-') p0_n = std::stoi(argv[++i]); } | |
| if (arg == "--serve") serve_mode = true; | |
| } | |
| // ---- Phase 0 gate (Run 2 Plan Β§C1, open item 7): a TRULY headless run -- WorldState | |
| // constructed and ticked WITHOUT ever calling attach(), so bridge/db stay nullptr for the | |
| // whole run. tick() only calls dispatch_cognition when bridge is set, so this exercises | |
| // ONLY the physics loop -- the same code path Phase 0 always meant, before cognition | |
| // existed. C1's event system (roll + famine + wanderer) is guarded the same way | |
| // (`if (bridge)`), so a headless run should never touch it, at any tick count. Runs past | |
| // EVENT_CHECK_EVERY (5000) on purpose -- proves the guard holds across an event-check | |
| // boundary, not just "the feature doesn't fire in the first few ticks by luck." Cheap: | |
| // pure physics, zero LLM calls, no model load needed at all. | |
| if (phase0_gate) { | |
| std::cout << "Phase 0 Gate: headless (" << p0_n << " ticks, no bridge attached)\n"; | |
| tbv::WorldState w; w.init(seed); | |
| while (w.current_tick < (uint64_t)p0_n) w.tick(); | |
| uint64_t h = world_hash(w); | |
| // Canonical value pinned 2026-07-10, BEFORE any Run 2 Plan Β§C1 code existed in this | |
| // binary -- established as the true pre-C1 headless baseline, then re-confirmed | |
| // byte-identical AFTER C1 landed (world.h/world.cpp event-system changes). That | |
| // second confirmation is the actual proof for open item 7, not this hardcoded value | |
| // alone -- see the Chillpad packet for both runs' output. | |
| uint64_t canonical = 0xc4216ecb37cb74cdULL; | |
| bool ok = (h == canonical); | |
| std::cout << "world_hash=" << std::hex << h << std::dec | |
| << (ok ? " β unchanged\n" : " β DRIFTED\n"); | |
| return ok ? 0 : 1; | |
| } | |
| // ---- --serve (Step 5b): long-running host mode. Infinite tick loop, periodic checkpoint, | |
| // SIGUSR1 = on-demand staging backup, SIGTERM = final checkpoint + clean exit. Auto-resumes | |
| // from EngineCheckpoint if village.db already has one for this run_id; else fresh init. | |
| // Zero changes inside tick()/cognition path -- world_hash surface is untouched. ---- | |
| if (serve_mode) { | |
| uint64_t serve_seed = seed; | |
| if (const char* e = std::getenv("TBV_SEED")) serve_seed = std::stoull(e); | |
| uint64_t ckpt_every = 500; | |
| if (const char* e = std::getenv("TBV_CKPT_EVERY")) ckpt_every = std::stoull(e); | |
| uint64_t villager_state_keep = 2000; // ticks of raw position history retained | |
| if (const char* e = std::getenv("TBV_VILLAGER_STATE_KEEP")) villager_state_keep = std::stoull(e); | |
| uint64_t prune_every = 1000; // DELETE cost scales with rows -- don't run every tick | |
| if (const char* e = std::getenv("TBV_PRUNE_EVERY")) prune_every = std::stoull(e); | |
| // Β§C2: text-table retention window, ~2 days at measured throughput (~354 ticks/hr). | |
| // Only ever consumed via SIGUSR2 (supervisor.py, post-archive-confirm) -- never on a | |
| // bare timer like villager_state_keep above, since this data is genuinely gone from | |
| // the live db afterward (VillagerState's prune is safe on a timer because nothing | |
| // reads old rows anyway; this one needs the archive to make that true). | |
| uint64_t text_keep = 20000; | |
| if (const char* e = std::getenv("TBV_TEXT_KEEP")) text_keep = std::stoull(e); | |
| const std::string run_id = "serve"; | |
| std::signal(SIGUSR1, tbv_on_sigusr1); | |
| std::signal(SIGTERM, tbv_on_sigterm); | |
| std::signal(SIGUSR2, tbv_on_sigusr2); | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); | |
| tbv::Database db("village.db"); | |
| db.init_schema(); | |
| tbv::WorldState world; | |
| world.init(serve_seed); | |
| world.attach(&bridge, &db, run_id); | |
| bool resumed = db.load_checkpoint(world); | |
| std::cout << (resumed ? "resumed from checkpoint at tick " : "fresh boot, seed ") | |
| << (resumed ? world.current_tick : serve_seed) << "\n" << std::flush; | |
| std::filesystem::create_directories("staging"); | |
| while (!g_shutdown_requested) { | |
| // Step 7 fish-tank fulcrum: --serve previously never wrote VillagerState (only | |
| // --run did), so a spectator polling village.db for positions would see a frozen | |
| // tank. Additive, observer-only (same idiom as --run's loop) -- does not touch | |
| // world_hash / determinism. Unbounded growth over long runs is a known tradeoff, | |
| // not solved here; revisit if/when long (weeks) runs make VillagerState huge. | |
| db.log_tick_state(world); | |
| world.tick(); | |
| if (world.current_tick % ckpt_every == 0) db.save_checkpoint(world); | |
| // Bounded retention (the tradeoff flagged above, resolved): VillagerState | |
| // is only ever consumed by the live /ws feed (newest tick) and short-window | |
| // debugging, so keeping unbounded history has no real use and a real cost | |
| // (~357MB/week at measured throughput). Keep a rolling window instead. | |
| if (world.current_tick % prune_every == 0 && world.current_tick > villager_state_keep) { | |
| db.prune_villager_state(world.current_tick - villager_state_keep); | |
| } | |
| if (g_backup_requested) { | |
| g_backup_requested = 0; | |
| db.save_checkpoint(world); | |
| bool ok = db.backup_to("staging/backup.db"); | |
| if (ok) { | |
| std::ofstream marker("staging/backup.done", std::ios::trunc); | |
| marker << "tick=" << world.current_tick | |
| << " engine_version=" TBV_ENGINE_VERSION "\n"; | |
| } else { | |
| std::cout << "backup FAILED at tick " << world.current_tick << "\n" << std::flush; | |
| } | |
| } | |
| // Β§C2: prune MemoryGraph/CognitionLog/HearsayChain -- only on explicit | |
| // request (supervisor.py sends SIGUSR2 immediately after a confirmed archive | |
| // upload), trusting that upload just captured everything up to ~now. | |
| if (g_prune_text_requested) { | |
| g_prune_text_requested = 0; | |
| if (world.current_tick > text_keep) { | |
| db.prune_text_tables(world.current_tick - text_keep); | |
| std::cout << "pruned text tables before tick " | |
| << (world.current_tick - text_keep) << "\n" << std::flush; | |
| } | |
| } | |
| } | |
| db.save_checkpoint(world); | |
| std::cout << "shutdown: final checkpoint at tick " << world.current_tick << "\n" << std::flush; | |
| return 0; | |
| } catch (const std::exception& e) { | |
| std::cout << "FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| // ---- Phase 3 driver: wire cognition into the deterministic tick loop, write the artifact DB. ---- | |
| if (run_mode) { | |
| std::cout << "Village run: seed " << seed << ", " << run_n << " ticks\n"; | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); | |
| tbv::Database db("village.db"); | |
| db.init_schema(); | |
| tbv::WorldState world; world.init(seed); | |
| std::string run_id = "run_" + std::to_string(seed); | |
| world.attach(&bridge, &db, run_id); | |
| while (world.current_tick < (uint64_t)run_n) { | |
| db.log_tick_state(world); // pre-tick snapshot: positions/needs at this tick_id | |
| world.tick(); | |
| if (world.current_tick % 100 == 0) log_digest_snapshot(db, world, run_id, seed); | |
| } | |
| int exp = 0, hear = 0, dream = 0, maxd = 0; | |
| for (uint32_t i = 0; i < tbv::MAX_VILLAGERS; ++i) | |
| for (const auto& m : world.stores[i].all()) { | |
| if (m.type == tbv::MemType::EXPERIENCE) exp++; | |
| else if (m.type == tbv::MemType::HEARSAY) { hear++; if (m.source_depth > maxd) maxd = m.source_depth; } | |
| else dream++; | |
| } | |
| std::cout << "done. RAM memories: EXPERIENCE=" << exp << " HEARSAY=" << hear | |
| << " (max depth " << maxd << ") DREAM=" << dream << "\n"; | |
| std::cout << "world_hash=" << std::hex << world_hash(world) << std::dec << "\n"; | |
| return 0; | |
| } catch (const std::exception& e) { | |
| std::cout << "FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| // ---- Coinage gate: a flagged (not-in-dictionary) term must appear in >=2 villagers' | |
| // memory stores β i.e. it rode hearsay/dream/action text and actually spread. ---- | |
| if (coinage) { | |
| std::cout << "Coinage Gate: emergent word-coinage (" << coin_n << " ticks)\n"; | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); | |
| tbv::Database db(":memory:"); db.init_schema(); | |
| tbv::WorldState world; world.init(seed); | |
| world.attach(&bridge, &db, "g"); | |
| while (world.current_tick < (uint64_t)coin_n) world.tick(); | |
| std::cout << "coined terms: " << world.coined_words.size() << "\n"; | |
| std::string best_term; int best_spread = 0; | |
| for (const auto& term : world.coined_words) { | |
| std::set<tbv::VillagerID> users; | |
| for (uint32_t i = 0; i < tbv::MAX_VILLAGERS; ++i) | |
| for (const auto& m : world.stores[i].all()) | |
| for (const auto& tok : tbv::tokenize(m.text)) | |
| if (tok == term) { users.insert(i); break; } | |
| if ((int)users.size() > best_spread) { best_spread = (int)users.size(); best_term = term; } | |
| } | |
| bool spread_ok = best_spread >= 2; | |
| std::cout << "best spread: \"" << best_term << "\" -> " << best_spread << " villagers\n"; | |
| std::cout << (spread_ok ? "β coinage: a coined term spread to >=2 villagers\n" | |
| : "β coinage: no term spread to >=2 villagers\n"); | |
| return spread_ok ? 0 : 1; | |
| } catch (const std::exception& e) { | |
| std::cout << "FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| // ---- Hearsay-fix gate (MSG_062 Β§2): memory-degradation + two-segment retell + forced | |
| // novelty must break stagnation. 5 seeds x 6-hop chains. Asserts (a) hop-6 drifts from the | |
| // seed in >=2 of {actor,action,location}, (b) EVERY adjacent hop differs (no verbatim freeze | |
| // β the bug the old endpoints-only Levenshtein missed), (c) same-seed byte-identical. ---- | |
| if (hearsay_fix) { | |
| std::cout << "Hearsay-Fix Gate: degradation + two-segment retell + forced novelty\n"; | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); | |
| const int HOPS = 6; | |
| const int LEV_MIN = 12; // min edit distance between adjacent hops (anti-stagnation) | |
| const std::string seed_text = | |
| "Borin found a glowing apple near the lake behind Brindlemark."; | |
| // One degraded telephone-chain. Fills `texts` with HOPS+1 entries (seed + each hop). | |
| auto chain = [&](uint64_t s, std::vector<std::string>& texts) { | |
| tbv::MemoryEntry cur; | |
| cur.mem_id = 0; cur.tick = 0; cur.importance = 800; | |
| cur.type = tbv::MemType::EXPERIENCE; cur.text = seed_text; | |
| texts.push_back(cur.text); | |
| for (int hop = 1; hop <= HOPS; ++hop) { | |
| // Deterministic, varied genome per hop -> genome-biased distortion. | |
| tbv::Genome g{ (uint8_t)(hop & 3), (uint8_t)((hop >> 1) & 3), | |
| (uint8_t)((s >> hop) & 3), (uint8_t)((s >> (hop+2)) & 3), | |
| (uint8_t)(hop % 3) }; | |
| uint64_t tick = (uint64_t)hop * 60; // age the memory so degradation bites | |
| cur.importance = 800 - 120 * hop; if (cur.importance < 100) cur.importance = 100; | |
| tbv::RetellResult rr = | |
| tbv::hearsay_hop(bridge, (tbv::VillagerID)hop, cur, g, tick, s + hop); | |
| texts.push_back(rr.out_text); | |
| cur.mem_id = (uint32_t)hop; cur.tick = tick; | |
| cur.type = tbv::MemType::HEARSAY; cur.source_depth = (uint8_t)hop; | |
| cur.text = rr.out_text; | |
| } | |
| }; | |
| bool all_drift = true, all_adjacent = true; | |
| uint64_t seeds[5] = { seed, seed + 101, seed + 202, seed + 303, seed + 404 }; | |
| for (int si = 0; si < 5; ++si) { | |
| std::vector<std::string> texts; | |
| chain(seeds[si], texts); | |
| tbv::HearsayFields f0 = tbv::extract_hearsay_fields(texts.front()); | |
| tbv::HearsayFields fN = tbv::extract_hearsay_fields(texts.back()); | |
| int changed = (f0.actor != fN.actor) + (f0.action != fN.action) | |
| + (f0.location != fN.location); | |
| int min_adj = 1 << 30; | |
| for (size_t i = 1; i < texts.size(); ++i) | |
| min_adj = std::min(min_adj, tbv::levenshtein(texts[i-1], texts[i])); | |
| bool drift = (changed >= 2), adjacent = (min_adj >= LEV_MIN); | |
| std::cout << "seed " << seeds[si] << ": fields_changed=" << changed | |
| << " min_adjacent_lev=" << min_adj | |
| << (adjacent ? "" : " <-- STAGNATION") << "\n"; | |
| if (!drift) all_drift = false; | |
| if (!adjacent) all_adjacent = false; | |
| } | |
| std::vector<std::string> t1, t2; | |
| chain(seeds[0], t1); chain(seeds[0], t2); | |
| bool deterministic = (t1 == t2); | |
| std::cout << (all_drift ? "β drift: hop-6 differs from seed in >=2 fields (all seeds)\n" | |
| : "β drift: insufficient field change on some seed\n"); | |
| std::cout << (all_adjacent ? "β no stagnation: every adjacent hop edit-distance >= threshold\n" | |
| : "β stagnation: a hop pair froze near-verbatim\n"); | |
| std::cout << (deterministic ? "β determinism: chain reproduces byte-identical\n" | |
| : "β determinism broke\n"); | |
| return (all_drift && all_adjacent && deterministic) ? 0 : 1; | |
| } catch (const std::exception& e) { | |
| std::cout << "FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| // ---- Checkpoint gate (Step 4): kill-and-restore must reproduce the uninterrupted run. | |
| // A runs straight through to N. B runs only to K<N, checkpoints, and is destroyed. C is a | |
| // FRESH WorldState (init() defaults, no shared memory with B) that loads B's checkpoint and | |
| // finishes K->N. hash(C) must equal hash(A) β restore must lose nothing live. Also hard- | |
| // asserts the existing --phase3 200 canonical hash is unperturbed (this instrumentation | |
| // must not touch the deterministic core) and that the 4 always-firing MSG_062 tables | |
| // (HearsayChain/CognitionLog/BeliefSurvival/WorldDigest) have rows. CoinageSpread is excluded | |
| // from the non-empty assert: word adoption is real-text-dependent and can legitimately be 0 | |
| // in a short/seeded run. ---- | |
| if (checkpoint_gate) { | |
| const int N = ckpt_n, K = 80; | |
| std::cout << "Checkpoint Gate: kill-and-restore (" << N << " ticks, checkpoint@" << K << ")\n"; | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); | |
| tbv::Database db_a(":memory:"); db_a.init_schema(); | |
| tbv::WorldState a; a.init(seed); a.attach(&bridge, &db_a, "ckg"); | |
| while (a.current_tick < (uint64_t)N) a.tick(); | |
| uint64_t hash_a = world_hash(a); | |
| log_digest_snapshot(db_a, a, "ckg", seed); // proves WorldDigest end-to-end | |
| tbv::Database db_b(":memory:"); db_b.init_schema(); | |
| { | |
| tbv::WorldState b; b.init(seed); b.attach(&bridge, &db_b, "ckg"); | |
| while (b.current_tick < (uint64_t)K) b.tick(); | |
| db_b.save_checkpoint(b); | |
| // b is destroyed here (end of scope) β C must restore with zero shared memory. | |
| } | |
| tbv::WorldState c; c.init(seed); c.attach(&bridge, &db_b, "ckg"); | |
| bool loaded = db_b.load_checkpoint(c); | |
| if (!loaded) { std::cout << "FAIL: load_checkpoint found no row for run_id\n"; return 1; } | |
| while (c.current_tick < (uint64_t)N) c.tick(); | |
| uint64_t hash_c = world_hash(c); | |
| bool restore_ok = (hash_a == hash_c); | |
| std::cout << "hash_uninterrupted=" << std::hex << hash_a | |
| << " hash_restored=" << hash_c << std::dec << "\n"; | |
| std::cout << (restore_ok ? "β restore: kill-and-restore reproduces the uninterrupted run\n" | |
| : "β restore: checkpoint/restore diverged from ground truth\n"); | |
| // Regression guard: this instrumentation must not perturb the deterministic core. | |
| tbv::Database db_r(":memory:"); db_r.init_schema(); | |
| tbv::WorldState r; r.init(12345); r.attach(&bridge, &db_r, "g"); | |
| while (r.current_tick < 200) r.tick(); | |
| // NOTE this run attaches a bridge -- it's a full cognition run, not headless | |
| // Phase 0 (that stays byte-identical by construction: tick() only calls | |
| // dispatch_cognition when bridge != nullptr, so a truly headless run never | |
| // touches any B-item code at all). This hardcoded value is a Step-4-era tripwire | |
| // for the checkpoint/restore plumbing specifically, NOT a promise that cognition | |
| // output never changes -- Run 2 Plan Phase B (2026-07-09) intentionally changes | |
| // generated text/dispatch (B1 grounded floor, B2 persona, B3 fatigue, B4 trim, | |
| // B6 coinage feedback), so the old value legitimately retires here. | |
| // RETIRED: 0x567898a5104b509dULL (Step 3, hearsay-degradation fix, 2026-06-22). | |
| // RETIRED: 0xcb224352745e7d19ULL (Run 2 Plan Phase B, 2026-07-09/10 -- B1-B6 | |
| // quality-epoch changes). | |
| // NEW canonical (seed 12345, N=200) -- Run 2 Plan Phase C, C1's world-event | |
| // system (famine/wanderer roll off a dedicated pcg32_event_state stream) drew | |
| // this seed's first roll inside this 200-tick window, legitimately changing | |
| // dispatch/generated content once. Independently reproduced twice: once via | |
| // --checkpoint_gate's own run of this exact code path, once via a separate | |
| // --phase3 200 process invocation (same-seed rerun byte-identical, diff-seed | |
| // (+777) diverges to 151af62f7f42506d, emergence intact: hearsay depth=3, | |
| // dreams=29). | |
| uint64_t canonical = 0xc0bb4b28825f411bULL; | |
| bool canon_ok = (world_hash(r) == canonical); | |
| std::cout << "canonical --phase3 200 hash @seed 12345: " << std::hex << world_hash(r) | |
| << std::dec << (canon_ok ? " β unchanged\n" : " β DRIFTED\n"); | |
| int n_hearsay = db_b.count_rows("HearsayChain", "ckg"); | |
| int n_cog = db_b.count_rows("CognitionLog", "ckg"); | |
| int n_belief = db_b.count_rows("BeliefSurvival", "ckg"); | |
| int n_coinage = db_b.count_rows("CoinageSpread", "ckg"); | |
| int n_digest = db_a.count_rows("WorldDigest", "ckg"); | |
| bool tables_ok = (n_hearsay > 0 && n_cog > 0 && n_belief > 0 && n_digest > 0); | |
| std::cout << "tables: HearsayChain=" << n_hearsay << " CognitionLog=" << n_cog | |
| << " BeliefSurvival=" << n_belief << " CoinageSpread=" << n_coinage | |
| << " WorldDigest=" << n_digest << "\n"; | |
| std::cout << (tables_ok ? "β tables: all 4 always-firing MSG_062 tables non-empty\n" | |
| : "β tables: one or more MSG_062 tables empty\n"); | |
| return (restore_ok && canon_ok && tables_ok) ? 0 : 1; | |
| } catch (const std::exception& e) { | |
| std::cout << "FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| // ---- Phase 3 gate: same-seed determinism + diff-seed differential + mythology emergence. ---- | |
| if (phase3) { | |
| std::cout << "Phase 3 Gate: orchestration (" << p3_n << " ticks/run)\n"; | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); // reused across runs | |
| auto run = [&](uint64_t s) { | |
| tbv::Database db(":memory:"); db.init_schema(); | |
| tbv::WorldState w; w.init(s); w.attach(&bridge, &db, "g"); | |
| while (w.current_tick < (uint64_t)p3_n) w.tick(); | |
| return w; | |
| }; | |
| tbv::WorldState a = run(seed); | |
| uint64_t ha = world_hash(a); | |
| tbv::WorldState b = run(seed); | |
| uint64_t hb = world_hash(b); | |
| tbv::WorldState c = run(seed + 777); | |
| uint64_t hc = world_hash(c); | |
| int dream = 0, maxd = 0; | |
| for (uint32_t i = 0; i < tbv::MAX_VILLAGERS; ++i) | |
| for (const auto& m : a.stores[i].all()) { | |
| if (m.type == tbv::MemType::DREAM) dream++; | |
| if (m.type == tbv::MemType::HEARSAY && m.source_depth > maxd) maxd = m.source_depth; | |
| } | |
| bool det = (ha == hb), diff = (ha != hc), emergence = (maxd >= 2 && dream >= 1); | |
| std::cout << "hash(seed)=" << std::hex << ha << " rerun=" << hb << " diff-seed=" << hc << std::dec << "\n"; | |
| std::cout << "emergence: max hearsay depth=" << maxd << ", dreams=" << dream << "\n"; | |
| std::cout << (det ? "β determinism: same-seed byte-identical\n" : "β determinism broke\n"); | |
| std::cout << (diff ? "β differential: diff-seed diverges\n" : "β differential failed (vacuous gate)\n"); | |
| std::cout << (emergence ? "β emergence: hearsay depth>=2 + dream exist\n" : "β emergence: pipeline did not fire\n"); | |
| return (det && diff && emergence) ? 0 : 1; | |
| } catch (const std::exception& e) { | |
| std::cout << "FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| if (phase2) { | |
| // Sequential hearsay propagation gate: seed v0, chain v0->v1->v2->v3 retelling | |
| // each prior villager's most-salient memory into own store. Strict sequential | |
| // (Gemini concurrency ruling: one context, KV wiped per retell in the bridge). | |
| std::cout << "Phase 2 Gate: hearsay propagation (sequential)\n"; | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); | |
| auto run_chain = [&](uint64_t s) { | |
| const int N = 4; | |
| std::vector<tbv::CognitiveStore> villagers(N); | |
| tbv::MemoryEntry seed_mem; | |
| seed_mem.mem_id = 0; seed_mem.importance = 1000; seed_mem.type = tbv::MemType::EXPERIENCE; | |
| seed_mem.text = "The village of Brindlemark is magical, with glowing trees and sparkling lakes behind the houses."; | |
| villagers[0].add(seed_mem); | |
| std::string final_text = seed_mem.text; | |
| for (int v = 1; v < N; ++v) { | |
| const tbv::MemoryEntry* src = villagers[v - 1].most_salient(); | |
| std::string heard = bridge.retell((tbv::VillagerID)v, src->text, "", s + v).out_text; | |
| tbv::MemoryEntry h; | |
| h.mem_id = (uint32_t)v; h.type = tbv::MemType::HEARSAY; | |
| h.source_depth = (uint8_t)(src->source_depth + 1); | |
| h.importance = src->importance - 150; // engine-derived: gossip fades per hop | |
| h.text = heard; | |
| villagers[v].add(h); | |
| final_text = heard; | |
| } | |
| return final_text; | |
| }; | |
| std::string seed_text = "The village of Brindlemark is magical, with glowing trees and sparkling lakes behind the houses."; | |
| std::string final1 = run_chain(seed); | |
| double drift = jaccard(tok_set(seed_text), tok_set(final1)); | |
| bool anti_collapse = final1.size() > 60; // not degenerate/empty | |
| std::string final2 = run_chain(seed); // determinism re-run | |
| bool deterministic = (final1 == final2); | |
| std::cout << "drift J(seed,hop3)=" << drift << " final_len=" << final1.size() << "\n"; | |
| std::cout << (drift < 0.3 ? "β drift: real mutation\n" : "β drift: too little\n"); | |
| std::cout << (anti_collapse ? "β anti-collapse: coherent narrative survives\n" : "β collapsed to noise\n"); | |
| std::cout << (deterministic ? "β determinism: chain reproduces byte-identical\n" : "β determinism broke\n"); | |
| // DREAM: recombine fragments (seed + last hop) into a surreal dream; check determinism. | |
| std::vector<std::string> frags = { seed_text, final1 }; | |
| std::string d1 = bridge.dream(3, frags, "", seed + 99); | |
| std::string d2 = bridge.dream(3, frags, "", seed + 99); | |
| bool dream_ok = (d1.size() > 60) && (d1 == d2); | |
| std::cout << "DREAM len=" << d1.size() << ": " << d1.substr(0, 110) << "...\n"; | |
| std::cout << (dream_ok ? "β dream: coherent + deterministic\n" : "β dream: degenerate or non-deterministic\n"); | |
| // Append-only MemoryGraph persistence round-trip (in-memory DB, observer interface). | |
| tbv::Database mdb(":memory:"); | |
| mdb.init_schema(); | |
| std::string run_id = "run_" + std::to_string(seed); | |
| int to_write = 4; | |
| for (int v = 0; v < to_write; ++v) { | |
| tbv::MemoryEntry e; | |
| e.mem_id = (uint32_t)v; e.tick = (uint64_t)v; e.importance = 1000 - v * 150; | |
| e.type = (v == 0) ? tbv::MemType::EXPERIENCE : tbv::MemType::HEARSAY; | |
| e.source_depth = (uint8_t)v; e.text = (v == 0) ? seed_text : final1; | |
| mdb.persist_memory(run_id, (tbv::VillagerID)v, e); | |
| } | |
| int got = mdb.count_memories(run_id); | |
| bool persist_ok = (got == to_write); | |
| std::cout << "persist: wrote " << to_write << " read " << got << "\n"; | |
| std::cout << (persist_ok ? "β persistence: MemoryGraph round-trip\n" : "β persistence failed\n"); | |
| return (drift < 0.3 && anti_collapse && deterministic && dream_ok && persist_ok) ? 0 : 1; | |
| } catch (const std::exception& e) { | |
| std::cout << "β FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| if (phase1) { | |
| std::cout << "Phase 1 Re-Cut Gate: hybrid cognition (free thought -> intent -> resolver)\n"; | |
| try { | |
| tbv::LlamaBridge bridge("models/smollm2-360m-instruct-q8_0.gguf"); | |
| auto vectors = phase1_vectors(); | |
| bool all_wellformed = true; | |
| tbv::Cognition v0_first; | |
| for (size_t i = 0; i < vectors.size(); ++i) { | |
| tbv::Cognition c = bridge.infer((tbv::VillagerID)i, vectors[i], seed + i); | |
| if (i == 0) v0_first = c; | |
| std::cout << "[V" << i << "] verb=" << tbv::verb_name(c.verb) | |
| << " (" << c.source << ") intent=\"I " << c.intent.substr(0, 60) << "...\"\n"; | |
| if (c.verb == tbv::Verb::NONE) all_wellformed = false; // resolver always yields a real verb | |
| } | |
| // Determinism: re-run vector 0 at the same seed, thought+intent+verb must reproduce. | |
| tbv::Cognition again = bridge.infer(0, vectors[0], seed + 0); | |
| bool deterministic = (again.verb == v0_first.verb && again.intent == v0_first.intent | |
| && again.thought == v0_first.thought); | |
| std::cout << (all_wellformed ? "β extractor well-formed (every cycle -> a valid engine verb)\n" | |
| : "β a cycle produced NONE\n"); | |
| std::cout << (deterministic ? "β determinism: vector 0 reproduces byte-identical\n" | |
| : "β determinism broke\n"); | |
| return (all_wellformed && deterministic) ? 0 : 1; | |
| } catch (const std::exception& e) { | |
| std::cout << "β FAIL: " << e.what() << "\n"; | |
| return 1; | |
| } | |
| } | |
| std::cout << "Starting 10-Bit Village Engine Phase 0 (Seed: " << seed << ")\n"; | |
| tbv::Database db("snapshot.db"); | |
| db.init_schema(); | |
| tbv::WorldState world; | |
| world.init(seed); | |
| // Run to exactly tick 1000 | |
| while (world.current_tick < 1000) { | |
| world.tick(); | |
| db.log_tick_state(world); | |
| } | |
| std::cout << "Reached tick 1000. Shutting down cleanly.\n"; | |
| return 0; | |
| } | |