board_os ↔ coding-os.db Coupling Contract
Purpose: Defines the read/write contract between board_os and the shared SQLite owned by thinking_os. Read when: Modifying board_os DB access or thinking_os tables that board_os reads. Skip when: Pure UI / scaffold edits. Read next: docs-system.md, mcp-tool-inventory.md
Nav: Section Index | Docs Index
Why this doc exists: board_os is registered through thinking_os/server.py and
shares the same SQLite connection. Any schema migration or API change in either subsystem
can silently break the other. This document defines the coupling contract and the rules
that keep the two subsystems decoupled at the right abstraction layers.
Coupling points
| Point | File | Nature |
|---|---|---|
| MCP registration | src/core/thinking_os/server.py |
register_board_tools(mcp, conn) wires board_os tools onto the same MCP server instance |
| Shared connection | src/core/thinking_os/server.py |
Passes the sqlite3.Connection from db.init_db() into board_os tools |
| Envelope helpers | src/core/board_os/mcp_tools.py |
Imports ok, fail, safe_tool from thinking_os.tools._shared (installed package, no sys.path injection) |
| Agent detection | src/core/board_os/_agent_runtime.py |
Reads src/adapters/<agent>/adapter.yaml::runtime_env_markers via cli.adapter_registry |
| Task doc FTS | src/core/board_os/parser.py |
Uses from thinking_os import task_parser (installed package); bare import task_parser fallback for script invocation |
Invariants
-
Single DB, dual writer. thinking_os writes to
memories,metrics,document_chunks,graph_*tables. board_os writes toboard_tasks,work_log. They MUST NOT touch each other's tables directly — cross-subsystem reads go through the MCP tool layer. -
Connection ownership. The
sqlite3.Connectionis owned bythinking_os/server.py(created bydb.init_db). board_os tools receive it as an argument and MUST NOT callconn.close()or change the journal mode. -
Schema migrations are append-only (Rule 9). New tables for board_os get a migration version ≥ current thinking_os migration count. Never edit past migrations.
-
Envelope contract (Rule 14). All board_os MCP tools MUST use
ok(data)/fail(category, msg)fromsrc/core/thinking_os/tools/_shared.py. Never return raw dicts. -
agent-detection SSOT.
src/core/board_os/_agent_runtime.py::detect_agent()is the single source of truth. The MCP tool_agent_label()must delegate to it, not re-implement.
Adding a new board_os MCP tool
- Add the function to
src/core/board_os/mcp_tools.py. - Decorate with
@safe_tool(imported from_shared). - Register via
register_board_tools— no new@mcp.tool()decorators outside this function. - Follow the
PURPOSE / INPUT / OUTPUT / DEPENDENCIES / NOTESdocstring convention (Rule 12). - Add to
docs/governance/mcp-tool-inventory.mdif it modifies state.
DB schema split
thinking_os tables:
memories, memory_edits, metrics, learning_events,
document_chunks, document_chunks_fts,
roles, personas, formulas,
graph_nodes, graph_edges_v12, graph_evidence_v12
(all managed by src/core/thinking_os/database.py migrations)
board_os tables:
board_tasks, work_log
(added by db.py migrations >= v8, owned by board_os)
Testing the coupling
# board_os unit tests (use the same in-memory db as thinking_os tests)
uv run --extra rag --with aiohttp --with pytest-asyncio pytest src/core/board_os/tests/ -q
# Verify MCP server boots with both subsystems registered
python src/core/thinking_os/server.py --test
This document is a governance artifact — AGENTS.md Rule 19 applies: keep it in sync when coupling points change. See also mcp-tool-inventory.md.