Spaces:
Sleeping
Sleeping
File size: 36,017 Bytes
84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 1b4cfab a3f913d 84b52f0 a3f913d 84b52f0 6719c0d 84b52f0 1b4cfab 84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 a3f913d 84b52f0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | #include <iostream>
#include <string>
#include <cstdlib>
#include <vector>
#include "engine/world.h"
#include "infra/db.h"
#include "cognition/llama_bridge.h"
#include "cognition/coinage.h"
#include "cognition/degradation.h" // hearsay_hop, levenshtein, extract_hearsay_fields
#include "cognition/resolver.h"
#include "cognition/cognitive_store.h"
#include <set>
#include <sstream>
#include <csignal>
#include <cstdio>
#include <filesystem>
#include <fstream>
// --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; }
#define TBV_ENGINE_VERSION "65debc3"
// 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;
}
|