Add Phase 1 foundation: Go module, core types, DB infrastructure, config

Establish the project foundation with three packages:
- core: shared types (Header, Metadata, Value, ObjectType, UUID generation)
- db: SQLite migration framework, connection management (WAL, FK, busy
  timeout), transaction helpers (StartTX/EndTX), time conversion
- config: runtime configuration (DB path, blob store, Minio, gRPC addr)

Includes initial schema migration (001_initial.sql) with 13 tables covering
shared infrastructure, bibliographic data, and artifact repository. Full test
coverage for all packages, strict linting (.golangci.yaml), and Makefile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-21 09:46:08 -07:00
parent 98990c6d76
commit bb2c7f7ef3
13 changed files with 1038 additions and 4 deletions

View File

@@ -0,0 +1,128 @@
-- Migration 001: Initial schema
-- Shared infrastructure, bibliographic tables, and artifact repository.
-- Polymorphic key-value metadata. The id column references any object's UUID.
CREATE TABLE IF NOT EXISTS metadata
(
id TEXT NOT NULL,
mkey TEXT NOT NULL,
contents TEXT NOT NULL,
type TEXT NOT NULL,
PRIMARY KEY (mkey, contents, type),
UNIQUE (id, mkey)
);
CREATE INDEX IF NOT EXISTS idx_metadata_id ON metadata (id);
-- Shared tag pool (used by both artifacts and knowledge graph nodes).
CREATE TABLE IF NOT EXISTS tags
(
id TEXT NOT NULL PRIMARY KEY,
tag TEXT NOT NULL UNIQUE
);
-- Shared category pool.
CREATE TABLE IF NOT EXISTS categories
(
id TEXT NOT NULL PRIMARY KEY,
category TEXT NOT NULL UNIQUE
);
-- Publishers for bibliographic citations.
CREATE TABLE IF NOT EXISTS publishers
(
id TEXT UNIQUE NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
address TEXT,
UNIQUE (name, address)
);
-- Bibliographic citations.
CREATE TABLE IF NOT EXISTS citations
(
id TEXT PRIMARY KEY,
doi TEXT,
title TEXT NOT NULL,
year INTEGER NOT NULL,
published TEXT NOT NULL,
publisher TEXT NOT NULL,
source TEXT NOT NULL,
abstract TEXT,
FOREIGN KEY (publisher) REFERENCES publishers (id)
);
CREATE INDEX IF NOT EXISTS idx_citations_doi ON citations (id, doi);
-- Many-to-one: multiple authors per citation.
CREATE TABLE IF NOT EXISTS authors
(
citation_id TEXT NOT NULL,
author_name TEXT NOT NULL,
FOREIGN KEY (citation_id) REFERENCES citations (id)
);
-- Artifact repository.
CREATE TABLE IF NOT EXISTS artifacts
(
id TEXT PRIMARY KEY,
type TEXT NOT NULL,
citation_id TEXT NOT NULL,
latest TEXT NOT NULL,
FOREIGN KEY (citation_id) REFERENCES citations (id)
);
-- Many-to-many junction: artifacts <-> tags.
CREATE TABLE IF NOT EXISTS artifact_tags
(
artifact_id TEXT NOT NULL,
tag_id TEXT NOT NULL,
FOREIGN KEY (artifact_id) REFERENCES artifacts (id),
FOREIGN KEY (tag_id) REFERENCES tags (id)
);
-- Many-to-many junction: artifacts <-> categories.
CREATE TABLE IF NOT EXISTS artifact_categories
(
artifact_id TEXT NOT NULL,
category_id TEXT NOT NULL,
FOREIGN KEY (artifact_id) REFERENCES artifacts (id),
FOREIGN KEY (category_id) REFERENCES categories (id)
);
-- Temporal index linking artifacts to snapshots by datetime.
CREATE TABLE IF NOT EXISTS artifacts_history
(
artifact_id TEXT NOT NULL,
snapshot_id TEXT NOT NULL UNIQUE,
datetime TEXT NOT NULL,
PRIMARY KEY (artifact_id, datetime),
FOREIGN KEY (artifact_id) REFERENCES artifacts (id)
);
-- Snapshot records with storage and content timestamps.
CREATE TABLE IF NOT EXISTS artifact_snapshots
(
artifact_id TEXT NOT NULL,
id TEXT UNIQUE PRIMARY KEY,
stored_at INTEGER NOT NULL,
datetime TEXT NOT NULL,
citation_id TEXT NOT NULL,
source TEXT NOT NULL,
FOREIGN KEY (artifact_id) REFERENCES artifacts (id),
FOREIGN KEY (id) REFERENCES artifacts_history (snapshot_id)
);
-- Blob registry. Actual content lives in the CAS on disk.
CREATE TABLE IF NOT EXISTS blobs
(
snapshot_id TEXT NOT NULL,
id TEXT NOT NULL UNIQUE PRIMARY KEY,
format TEXT NOT NULL,
FOREIGN KEY (snapshot_id) REFERENCES artifact_snapshots (id)
);
-- Schema version tracking.
CREATE TABLE IF NOT EXISTS schema_version
(
version INTEGER NOT NULL,
applied TEXT NOT NULL
);
INSERT INTO schema_version (version, applied) VALUES (1, datetime('now'));