Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "primer-brain",
"version": "0.1.3",
"version": "0.1.4",
"description": "An open learner record + MCP server. The memory layer for AI tutoring. No fixed interface \u2014 the tutor builds one per child, per moment.",
"license": "Apache-2.0",
"type": "module",
Expand Down
16 changes: 16 additions & 0 deletions src/db/migrations/003_artifacts.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,22 @@
-- reviewing an artifact writes evidence back the same way any other observation does
-- (`observation.kind = 'artifact_review'`).

-- Some early records predate the artifact table entirely. The baseline schema
-- normally creates it before migrations run, but this keeps the upgrade safe
-- when a record was made by an older standalone binary.
CREATE TABLE IF NOT EXISTS artifact (
id TEXT PRIMARY KEY,
learner_id TEXT NOT NULL REFERENCES learner(id) ON DELETE CASCADE,
session_id TEXT,
ts TEXT NOT NULL,
kind TEXT NOT NULL,
path TEXT NOT NULL,
mime TEXT,
caption TEXT,
skill_ids TEXT
);
CREATE INDEX IF NOT EXISTS idx_artifact_learner ON artifact(learner_id, ts DESC);

ALTER TABLE artifact ADD COLUMN duration_ms INTEGER;
ALTER TABLE artifact ADD COLUMN mime_type TEXT;
ALTER TABLE artifact ADD COLUMN prompt TEXT; -- what they were asked to read or do
Expand Down
30 changes: 30 additions & 0 deletions test/migrations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { DatabaseSync } from 'node:sqlite';
import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';

const ROOT = join(import.meta.dirname, '..');

test('the artifact migration upgrades a record from before artifacts existed', () => {
const dir = mkdtempSync(join(tmpdir(), 'primer-pre-artifact-'));
const database = new DatabaseSync(join(dir, 'record.db'));
try {
// This is the only dependency 003 has after 002. The record is otherwise an
// old baseline where audio artifacts had never been introduced.
database.exec('CREATE TABLE setting (key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TEXT NOT NULL, updated_by TEXT)');
database.exec(readFileSync(join(ROOT, 'src/db/migrations/003_artifacts.sql'), 'utf8'));

const columns = database
.prepare('SELECT name FROM pragma_table_info(\'artifact\') ORDER BY name')
.all() as { name: string }[];
assert.deepEqual(
columns.map((column) => column.name),
['bytes', 'caption', 'duration_ms', 'id', 'kind', 'learner_id', 'mime', 'mime_type', 'path', 'prompt', 'reviewed_at', 'reviewed_by', 'session_id', 'skill_ids', 'transcript', 'ts'],
);
} finally {
database.close();
rmSync(dir, { recursive: true, force: true });
}
});
Loading