Full port of the Store (17 tables, FTS5, per-profile slices, sessions, turn state, bots, settings, MCP registry), DebugLog, SETTINGS_CATALOG and boot_settings, ShellSession (as ShellContext), Conversation persistence, Repl/Boot, and the PlexerController family with FlowValidator/PlexerFlow. Verified by replaying 643 operations through tai.py and the C++ code (results, tables, FTS, schema, files identical) and by restarting the REPL under per-pane shells and checking the vault. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
5.7 KiB
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):-lsqlite3is enough; nothing else new.-pthreadis 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 likedetz_test(the newStoreneeds the other store, session and config sources). -
Test target: add
include tests/foundation.mk(or call$(MAKE) -f tests/foundation.mk testfrommake test). It buildsbuild/foundation_probe,build/repl_probe,build/foundation_testand runs the three suites. -
plexer.mkstill saysSRCS += src/plexer/plexer.cpp; the other*.mkfiles are not used by the Makefile.
2. main.cpp (REPL, one-shot, debug, profile, --continue)
#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<detz::repl::BootedSession> 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_turnmust callconversation.begin_turn(goal), appenduser/assistant/toolmessages withconversation.add_message(or push Json intoconversation.messages()), callnote_step()/note_tool()while working, andclose_turn()at the end (the equivalent ofAgent.run_turn). Throwrepl::TurnInterrupted(or pollrepl::Interrupt::requested()) on Ctrl-C. -
Conversation::switch_profile/switch_botcallCatalogs::profile_reset(clear read files and secret grants) andscope_changed(chat.scope = (profile, bot)). -
session::DirDefaults::apply(conversation, hooks)replacesapply_dir_defaults; call it afterBoot::openand print the returned line dimmed when non-empty. -
Record tools in
src/agent/exec.cppuse the removed stub API. Replace (python names, ids aremem:<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 openingdetz.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.