2026-10-25 05:06:43 +01:00
# misc modules: skills, sched, telegram, rag, install (parity spec)
Source of truth: `../tai.py` . Every class here is a literal port; method names keep the Python names so the
parity ledger matches them. Behavior is proven by `tests/misc_diff.py` (18 scenarios, byte-identical
transcripts plus file trees against the real tai.py). Build: `make -f tests/misc.mk all` , run:
`python3 tests/misc_diff.py` .
## Shared helpers (src/skills, namespace detz::misc)
| tai.py construct | C++ | notes |
|---|---|---|
| json.dumps / json.loads | `Json` | ensure_ascii, ", " / ": " separators, insertion-ordered objects, big ints keep digits, JSONDecodeError text |
| datetime.fromisoformat, astimezone, isoformat | `DateTime` | CPython port: week dates, 24:00, stray char before offset, gap/fold local times |
| str.splitlines / strip / len / slice, short_error | `PyStr` | code point semantics, Unicode whitespace and Nd digits (`PyUnicode` , generated table) |
| os / shutil / subprocess | `PyFs` , `PyProc` | universal newlines on text reads, OSError text `[Errno N] ...: 'path'` |
| hashlib.sha256 | `Sha256` | |
| LOG.event / LOG.context | `LogSink` | port, default drops |
| normalize_tag(s) | `TagNames` | |
## skills (src/skills) vs skills.md
| tai.py | C++ |
|---|---|
| parse_skill_front, parse_skill_file, skill_scope_dir, default_skill_scope, skill_extra_files, skill_install_meta_path, load/save_skill_install_meta, is_external_skill_root, skill_provenance_note, discover_skills, skill_catalog, create_skill_prompt | `SkillFiles` |
| BUNDLED_SKILLS, BUNDLED_SKILL_UPSTREAM, SKILL_BLUEPRINTS | `BundledData` (generated `bundled_data.cpp` , byte-identical; regenerate with `python3 -I src/skills/gen_bundled.py` ) |
| bundled_skill_digest, bundled_skill_pristine, write_bundled_skill, remove_bundled_skill, op_skills, skills line of status_lines | `BundledSkills` |
| Tools.run_load_skill, run_create_skill, find_skill_root, resolve_skill_source, run_install_skill | `SkillTool` (+ `SkillHost` port, zlib `ZipArchive` ) |
## sched (src/sched) vs scheduler section of tai.py
| tai.py | C++ |
|---|---|
| parse_schedule_at, parse_duration, format_delay, local_display, now_iso | `ScheduleTime` |
| Store.add_schedule / remove_schedule / list_schedules | `ScheduleVault` (+ `SealPort` , `TagPort` ) |
| format_schedule_lines, note_schedule_result, watch_schedule, schedule_running, scheduler_tick, run_scheduler_loop, start_scheduler | `Scheduler` (+ `AgentPort` , `StopFlag` ) |
| Tools.run_schedule / run_unschedule / run_schedules, normalize_tags | `ScheduleTool` (+ `ScheduleHost` ) |
## telegram (src/telegram)
| tai.py | C++ |
|---|---|
| class Telegram | `Telegram` (+ `TelegramTransport` , libcurl `CurlTransport` ) |
| telegram_env_path, load_telegram_token, telegram_send, telegram_transcribe, handle_telegram_update, _handle_telegram_update_inner, run_telegram_bot | `TelegramBot` (+ `TelegramAgent` , `BoxPort` ) |
Wire format (request bodies, paths, headers) is compared against a scripted local Bot API.
## rag (src/rag)
| tai.py | C++ |
|---|---|
| chunk_lines, collapse_repeated_lines, Store.add_chunks / get_chunks_by_ids | `Chunker` , `ChunkStore` |
| fts_escape, fts_match, fts_match_or, fts_match_prefix, fts_match_variants, fts_rank, Store.fts_search | `FtsQuery` , `FtsSearch` |
| class RetrievalEval, the `--eval-retrieval` body | `RetrievalEval` (`run_eval` ) |
| Tools.run_search | `RagTool` (+ `RagHost` ) |
`--stats-rag` does not exist in tai.py (no such argument); nothing is ported for it.
## install (src/install)
| tai.py | C++ |
|---|---|
| rc block helpers, hook_present and friends | `RcBlocks` , `Installer::hook_present` etc. |
| unit_dir, service_state, activate_service, remove_service_unit | `Systemd` |
| Install, venv_dir, venv_python, ensure_venv, script_version, shell_rc_path, shell_present | `Install` |
| op_binary ... op_skills, status_lines, vault_line, install_report, install_self, install_telegram, uninstall_telegram, Tools.run_install | `Installer` (+ `ContainerPort` , `InstallIo` ) |
| next_version, release_bump, atomic_write, sealed_write, quarantine, backups_dir, prune_backups, snapshot_self, backup_has_digest, ensure_self_backup, do_release, capped_text, trunc_marker, read_capped, Tools.run_release | `SelfRelease` , `ReleaseTool` |
| Store.audit_event / audit_history / audit_get / audit_search / tag_counts, Tools.run_audit / run_restore / run_tags | `AuditVault` , `AuditTool` |
2026-10-27 18:22:24 +01:00
### `--install` of the native binary (what it does today)
- `--install` runs `Installer::install_self` : `op_binary("upgrade")` , bash hook (plus zsh/fish hook per `$SHELL` ), venv, bundled skills. It always copies, so a second run refreshes.
- Target: `$HOME/.local/bin/tai.py` (same path as tai.py, the shell hooks in `installconsts.cpp` alias `tai` to it). The native binary replaces the Python product at that path; it is not installed as `detz` . A copy that is running (`ETXTBSY` ) is written beside the target and renamed.
- `OpsAdapter` sets `InstallContext::version_reader` . It returns the running version only if the file at the given path exists, otherwise nothing, so a fresh install reports `binary: installed <v>` and an existing one `binary: refreshed <old> -> <new>` . Before this fix the reader ignored the path and a fresh install printed `refreshed X -> X` . Limit: the reader cannot read the version of the installed binary, so `<old>` is the running version.
- Verified by running `build/detz --install` against a throwaway `HOME` : binary byte-identical to `build/detz` , hook, venv and six skills created. The message fix itself is not yet verified by a run (build was still in progress when this was written).
2026-10-25 05:06:43 +01:00
## Known gaps
- Sealed vaults: the Seal crypto and the sealed events memdb belong to the store module; sealed paths are
reachable through `SealPort` /`MemEventsPort` but only the passphrase-less seal is verified against tai.py.
- `Store.upsert_file_record` , `delete_file_record` , canonical tag folding (`singular_noun` ) are store-module work
(`FileRecordPort` , `TagPort` ); the tests use a plain implementation.
- Box restore (`box_restore` ), container probes and `ensure_box` are reached through ports; fakes are verified,
the real podman path is not.
- urllib error text for network failures other than HTTP status, timeout, DNS and refused is approximated.
- JSON numbers beyond int64 keep exact digits; floats beyond that use double.
- Copying a native binary in `op_binary` cannot read a version from it; set `InstallContext::version_reader` .
2026-10-27 18:22:24 +01:00
- `--install` installs under the name `tai.py` , not `detz` ; renaming needs `installconsts.cpp` hooks changed too.