# Wiring: foundation (for the integrator) Everything below is applied outside the foundation directories. The foundation area edited none of `main.cpp`, `src/cli`, `Makefile`, `PARITY.md`. ## 1. Makefile `ALL_SRCS` already picks up every `src/**/*.cpp` (store, session, config, plexer, repl). Needed changes: - Link line (`$(BIN)`, `detz_test`, `static`): `-lsqlite3` is enough; nothing else new. `-pthread` is not required by the foundation code. - Replace the stale rule $(BUILD)/store_test: tests/store_test.cpp src/store/store.cpp ... tests/store_test.cpp src/store/store.cpp -lsqlite3 -lcurl -o $@ by `$(BUILD)/store_test: tests/store_test.cpp $(ALL_SRCS)` compiled like `detz_test` (the new `Store` needs the other store, session and config sources). - Test target: add `include tests/foundation.mk` (or call `$(MAKE) -f tests/foundation.mk test` from `make test`). It builds `build/foundation_probe`, `build/repl_probe`, `build/foundation_test` and runs the three suites. - `plexer.mk` still says `SRCS += src/plexer/plexer.cpp`; the other `*.mk` files are not used by the Makefile. ## 2. main.cpp (REPL, one-shot, debug, profile, --continue) ```cpp #include "repl/boot.h" #include "repl/repl.h" ... auto& o = cmd.opts; detz::repl::Boot::configure_debug(o.debug, o.debug_format); // before anything else if (!detz::repl::Boot::valid_profile(o.profile.value_or(o.telegram ? "telegram" : "default"))) return 1; detz::repl::BootOptions boot; boot.profile = o.profile.value_or(o.telegram ? "telegram" : "default"); boot.yes = o.yes; boot.yolo = o.yolo; boot.autonomous = o.auto_; boot.no_opencode = o.no_opencode; boot.version = detz::kVersion; boot.mode = !o.prompt.empty() ? "one-shot" : "repl"; boot.seal_factory = ...; // adapter from the Seal owner, see section 4 boot.catalogs = {skill_catalog, tool_catalog, scope_changed, profile_reset}; // agent module hooks std::unique_ptr app; try { app = detz::repl::Boot::open(boot); } catch (const detz::store::SealError& e) { std::fprintf(stderr, "seal error: %s\n", e.what()); return 2; } MyAgentHost host(*app->conversation, ...); // implements detz::repl::ReplHost, appends messages to app->conversation detz::repl::Repl::Options ro; ro.version = detz::kVersion; ro.resume = o.resume; ro.telegram = o.telegram; ro.scheduler = o.scheduler; detz::repl::Repl repl(*app->conversation, host, ro); if (!o.prompt.empty()) return repl.oneshot(join(o.prompt, " ")); // exit codes 0, 1, 130 as tai.py return repl.run(); ``` Per-step order is tai.py `main`: debug sink, `--version` etc., then boot, then `--env`, `--eval-retrieval`, `--stats-rag`, `--scheduler`, `--telegram`, `--tui-local`, prompt, REPL. `Boot::open` prints `vault schema upgraded: ...` and `settings from vault: ...` like python. `Repl::run` and `Repl::oneshot` close the store. `--plexer-wrap` and `--mplex` are not foundation code; `plexer::PlexerControllerFactory::detect()` is what the plexer tools call. ## 3. Agent module hooks - Implement `detz::repl::ReplHost` (`src/repl/repl_host.h`): `run_turn` must call `conversation.begin_turn(goal)`, append `user`/`assistant`/`tool` messages with `conversation.add_message` (or push Json into `conversation.messages()`), call `note_step()`/`note_tool()` while working, and `close_turn()` at the end (the equivalent of `Agent.run_turn`). Throw `repl::TurnInterrupted` (or poll `repl::Interrupt::requested()`) on Ctrl-C. - `Conversation::switch_profile/switch_bot` call `Catalogs::profile_reset` (clear read files and secret grants) and `scope_changed` (`chat.scope = (profile, bot)`). - `session::DirDefaults::apply(conversation, hooks)` replaces `apply_dir_defaults`; call it after `Boot::open` and print the returned line dimmed when non-empty. - Record tools in `src/agent/exec.cpp` use the removed stub API. Replace (python names, ids are `mem:<16 hex>` strings): auto st = detz::store::Store::Open(workdir + "/detz.db"); record_save : std::string id = st->add_record(kind, title, content); return ok(id); record_read : auto r = st->get_record(id); if (!r) return err("not found"); return ok(r->get_string("content")); record_search : for (auto& r : st->search_records(query)) ... (fields id, kind, title, size, reads, updated, tags) search : for (auto& h : st->fts_search(query)) ... (fields item, kind, sub, title, snippet, rank) record_delete : return ok(st->delete_record(id) ? "deleted" : "not found"); In the real app the tools should share the booted `Store` (`*app->store`) and the current profile instead of opening `detz.db`. - Large tool output: `detz::store::Spill::spill_or_truncate(&store, kind, title, text)`. - Debug events: `detz::store::DebugLog::Global().event(level, component, event, message, profile, bot, chat, meta)`; no-op while disabled. ## 4. Seal `src/seal` is outside this area. Provide a class deriving `detz::store::SealPort` (`enabled`, `default_key`, `lock`, `unlock`; prefix `tai1$`) and return it from `BootOptions::seal_factory(home, passphrase, default_key)`. The factory also owns the python behaviours not ported here: `.seal` file handling, `rotate_seal` and the default-key to `TAI_PASSPHRASE` upgrade. Until then the vault is plain text when `TAI_PASSPHRASE` is unset and `Boot::open` refuses an explicit passphrase. ## 5. CLI facts the foundation relies on `src/cli/cli.h` `Options` already has `profile`, `debug`, `debug_format`, `resume`, `yes`, `yolo`, `auto_`, `no_opencode`, `telegram`, `scheduler`, `prompt`. No change needed. ## 6. PARITY.md / CHANGELOG Symbols covered are listed in `docs/foundation.md` ("Symbol map"); the foundation area did not touch `PARITY.md` or `CHANGELOG.md`.