Status: v0.1 (2026-08-05) — implementation design for the SQL-server storage mode (sync_mode: sql, backend: mysql|mariadb|postgresql). Companion to docs/learn-sync.md (hub model overview) and docs/learn-sync-text.md (the text+git variant). Central library is a shared database; personal libraries stay local.
user A local SQLite (private) ─┐
user B local SQLite (private) ─┼── weekly manual upload ──▶ CENTRAL DB learn_staging (upload area)
user C local SQLite (private) ─┘ │ (INSERT / dump import)
▼
central training Agent reviews (SELECT staging) ──▶ admin confirms ──▶ learn_rules updated (transaction + audit)
│
users pull back (read-only SELECT/merge)
Same hub-and-spoke logic as docs/learn-sync.md, implemented with tables + transactions instead of files.
| Table | Purpose | Written by |
|---|---|---|
learn_staging |
Upload area — one row per uploaded rule proposal | Users (via upload), app layer |
learn_rules |
Public rules — the authoritative shared library | Review step only (transaction) |
learn_reviews |
Review decisions (per proposal: action + reviewer + reason) | Admin/reviewer |
learn_audit |
Append-only change log (before/after, actor, ts) | App layer (trigger or explicit) |
learn_sync_markers |
Per-user last-upload marker (for incremental uploads) | App layer |
Key columns:
-- learn_staging (upload area)
id BIGINT PK, contributor TEXT, uploaded_at TIMESTAMPTZ,
kind TEXT, rule_key TEXT, -- canonical key (YAML/JSON text) + extracted query cols
value TEXT, -- YAML preferred (AI-readable, D-SK8); JSON allowed
trust TEXT, version INT,
status TEXT DEFAULT 'pending', -- pending | reviewed
payload_raw TEXT, -- original bundle row verbatim (YAML preferred; audit)
-- learn_rules (public)
rule_id TEXT PK, kind TEXT, rule_key TEXT UNIQUE, value TEXT, -- YAML preferred
trust TEXT, contributor TEXT, version INT, updated_at TIMESTAMPTZ,
superseded_by TEXT NULL -- tombstone (NULL = active)
-- learn_reviews
id BIGINT PK, staging_id BIGINT, action TEXT, -- ADD | UPDATE | REJECT
reviewer TEXT, reason TEXT, decided_at TIMESTAMPTZ
-- learn_audit
id BIGINT PK, ts TIMESTAMPTZ, actor TEXT, table_name TEXT,
before TEXT, after TEXT, action TEXT -- before/after stored as YAML/JSON text (readable audit)Format note (D-SK8): store rule payloads as YAML text — the AI reviews them directly and diffs are readable. Use dedicated columns (
rule_key,trust,version, ...) for indexed queries; use JSONB only when the DB must query inside the payload (PostgreSQL) — otherwise YAML text is preferred.learn_audit.before/afteras text keeps the audit human/AI-readable.
sync_mode: sqlpersonal layer: local SQLite (.data/learn/private.db) or local files — user's tuned rules, customer-tier, personal habits.
- Trigger: user says "upload my data" / "re-submit" / "submit my library" (or the same intent in the user's own language) or the weekly reminder fires — manual, confirmed (OQ-LS2).
- User identity: unique machine code / user id (
contributor), sanitized[a-zA-Z0-9_-]; every staging row carries it. - Export: the Agent exports the incremental diff of the personal library since the last upload marker (
learn_sync_markers), tagged withcontributor+ date. - Upload: application-layer
INSERTintolearn_staging(or a validated dump import). Direct writes tolearn_rulesare rejected for users. No git branches — identity is a column, not a branch. - Re-upload: new staging rows with fresh
uploaded_at; the earlier pending rows of that contributor are flagged stale (no silent overwrite). - Scoping: customer-specific entries excluded by default (user choice).
- Ingest — training Agent reads new
learn_stagingrows (status = 'pending'), validates JSONB schema, ledger/audit entry. - Compare — against
learn_rules:- same
rule_id→ version/trust comparison - same
rule_keydifferent id → new-rule candidate / conflict pair - contradicts official baseline → reject candidate
- same
- Propose — one
learn_reviewsrow per decision:ADD/UPDATE(with before/after) /REJECT(with reason). - Confirm — admin accepts/rejects each proposal (review UI or CLI).
- Apply — in a single transaction:
learn_rules: insert/update accepted rows (version bump,updated_at), tombstone removalslearn_reviews: mark staging rowsreviewed+ write decisionslearn_audit: append every change (before/after)- Rebuild derived stats (materialized view or app-level aggregates)
- Pull back — users read
learn_rules(read-only) and merge locally; personal overrides stay local.
- Versioning: optimistic locking on
learn_rules.version— an UPDATE includesWHERE version = <expected>; mismatch → proposal conflicts, surfaces in review. - Trust order:
user_rule > stats > llm(independent of version). - Same
rule_key, different contributors → both staged, both surface in review as a conflict pair — never silently resolved by the DB. - Deletes = tombstone (
superseded_byset), never hard-delete — prevents stale syncs resurrecting removed rules. - Stats are derived (view/rebuild), never merged by hand.
- Concurrency: staging and rules are separate tables → uploads never block reviews; applies are atomic transactions (no partial public updates).
- DB account tiers: user accounts =
learn_stagingINSERT +learn_rulesSELECT only; reviewer/admin = full review + apply. Enforced by the application layer AND database grants (defense in depth). - Personal/customer data never enters the central DB unless the user explicitly uploads it (and can exclude customer dimensions).
learn_rulescontains price tiers / cost rules only — no credentials, nolocal_config, nodb_dsn.learn_auditis append-only (trigger-enforced) — full audit trail.- Connection string from
local_config [storage] db_dsn; never committed.
- Uploads (staging INSERTs) and reviews (rule applies) are decoupled → no write contention.
- Transactions give atomic applies; optimistic locking gives safe concurrent reviews.
- Scales beyond text mode (large teams, concurrent uploads, complex queries).
- PostgreSQL recommended for JSONB; MySQL/MariaDB work with JSON column + app-side validation.
- Storage layer (
development-handoff.mdBatch 1) — SQL access primitives live there; DSN from config. skills/csv-data-import/scripts/validate_csv.py→ basis for bundle/JSONB validation before staging.skills/price-crawler/config/normalization.yaml→ shared alias dictionary.
- Upload flow inserts staging rows only; a direct user write to
learn_rulesis rejected (app + DB grant). - Review flow produces decisions; applying a batch is atomic (all-or-nothing).
- Audit trail captures every apply (before/after, actor, ts).
- Pull-back merges read-only; local overrides win.
- OQ-LS1 — Central library location for text mode (in
store/vs separate repo) — this SQL design assumes the DB is the central library; no separate repo needed. Confirm. - OQ-LQ1 — DB choice priority: PostgreSQL (JSONB, recommended) vs MySQL/MariaDB — decide per team infrastructure; both supported in
init-data.sh. - OQ-LQ2 — Audit via DB trigger vs app layer only — recommend trigger (cannot be bypassed by direct SQL).
| Dimension | Text (text+git) | SQL server |
|---|---|---|
| Infrastructure | none (git only) | shared DB server + DSN |
| Concurrency | serialized by review step | concurrent, transactional |
| Scale | small teams | larger teams / existing DB infra |
| Audit | git history | learn_audit (trigger-enforced) |
| Default | ✅ recommended for new teams | when team already runs MySQL/PG |
Both modes share: hub model, weekly manual upload, central training Agent review, admin confirmation, learn_private stays local.