diff options
| author | soryu <soryu@soryu.co> | 2026-05-02 15:07:33 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-05-02 15:07:33 +0100 |
| commit | 760516b2e7b97fa389fb3902e8d2314eea052ff0 (patch) | |
| tree | 69eb1dd212ef924ee9e451d8d88806f899c03e84 /makima/migrations | |
| parent | e11759447b1ac00becfb1e979e488f7f9c9cf478 (diff) | |
| download | soryu-760516b2e7b97fa389fb3902e8d2314eea052ff0.tar.gz soryu-760516b2e7b97fa389fb3902e8d2314eea052ff0.zip | |
feat: multi-document directives with ephemeral task lifecycle (#119)
* feat: soryu-co/soryu - makima: Fix folder/file naming and breadcrumb hash bugs
* WIP: heartbeat checkpoint
* WIP: heartbeat checkpoint
* WIP: heartbeat checkpoint
* feat: soryu-co/soryu - makima: Frontend: render multiple documents per directive folder
* WIP: heartbeat checkpoint
* WIP: heartbeat checkpoint
* WIP: heartbeat checkpoint
* Fix DirectiveRevision import in openapi.rs after merge
* Fix document-directives.tsx merge artifacts and add inactive status
Diffstat (limited to 'makima/migrations')
| -rw-r--r-- | makima/migrations/20260502000000_create_directive_documents.sql | 50 | ||||
| -rw-r--r-- | makima/migrations/20260503000000_add_document_id_to_tasks.sql | 59 |
2 files changed, 109 insertions, 0 deletions
diff --git a/makima/migrations/20260502000000_create_directive_documents.sql b/makima/migrations/20260502000000_create_directive_documents.sql new file mode 100644 index 0000000..99871b5 --- /dev/null +++ b/makima/migrations/20260502000000_create_directive_documents.sql @@ -0,0 +1,50 @@ +-- Multi-document model under directives. +-- +-- A directive is a folder that owns N documents (the user calls these +-- "directive contracts"). Each document has its own markdown body, status, +-- and may end up attached to a PR. Multiple documents can be active at the +-- same time. When a doc ships (PR raised) it transitions to 'shipped'; +-- editing a shipped doc later flips it back to 'active' (handled in repo +-- update logic — wired into handlers in a later step). +-- +-- Today, directives.goal IS the single contract document, so we backfill +-- one document per existing directive populated from directives.goal so the +-- existing UI keeps working. + +CREATE TABLE directive_documents ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + directive_id UUID NOT NULL REFERENCES directives(id) ON DELETE CASCADE, + title VARCHAR(500) NOT NULL DEFAULT '', + body TEXT NOT NULL DEFAULT '', + status VARCHAR(32) NOT NULL DEFAULT 'draft' + CHECK (status IN ('draft', 'active', 'shipped', 'archived')), + pr_url TEXT, + pr_branch TEXT, + shipped_at TIMESTAMPTZ, + archived_at TIMESTAMPTZ, + version INTEGER NOT NULL DEFAULT 1, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX idx_directive_documents_directive_id + ON directive_documents(directive_id); +CREATE INDEX idx_directive_documents_status + ON directive_documents(directive_id, status); + +-- Backfill: one document per existing directive, copying the current goal +-- so the existing UI keeps showing what it shows today. Directives that +-- already have a PR url are seeded as 'shipped' (with shipped_at set), +-- everything else as 'active'. COALESCE protects against any unexpected +-- NULL goal rows even though the column is currently NOT NULL. +INSERT INTO directive_documents ( + directive_id, title, body, status, pr_url, shipped_at +) +SELECT + d.id, + d.title, + COALESCE(d.goal, ''), + CASE WHEN d.pr_url IS NOT NULL THEN 'shipped' ELSE 'active' END, + d.pr_url, + CASE WHEN d.pr_url IS NOT NULL THEN NOW() ELSE NULL END +FROM directives d; diff --git a/makima/migrations/20260503000000_add_document_id_to_tasks.sql b/makima/migrations/20260503000000_add_document_id_to_tasks.sql new file mode 100644 index 0000000..71d122d --- /dev/null +++ b/makima/migrations/20260503000000_add_document_id_to_tasks.sql @@ -0,0 +1,59 @@ +-- Attach tasks and directive_steps to a specific directive_document. +-- +-- Until now, directive-driven tasks and steps were only linked to the +-- directive itself (tasks.directive_id, directive_steps.directive_id). The +-- document model that landed in 20260502000000_create_directive_documents.sql +-- introduced N documents per directive, each with its own draft → active → +-- shipped → archived lifecycle. To make the sidebar match the goal — each +-- document carries its own tasks/ subfolder, and a shipped document's task +-- history visually moves with it under shipped/ — we need a per-document +-- foreign key on tasks and steps. +-- +-- The column is nullable + ON DELETE SET NULL so: +-- * Old, pre-document tasks can keep working with directive_document_id = NULL. +-- * Deleting a document detaches its tasks (it does not cascade-delete them), +-- because task output is valuable history we don't want to lose. +-- +-- Backfill rule: every existing directive currently has exactly one document +-- (created by the previous migration's INSERT). We attach all of that +-- directive's existing tasks and steps to that one document. Directives with +-- zero documents are a no-op — the subquery returns NULL, the column stays +-- NULL, and nothing breaks. + +ALTER TABLE tasks + ADD COLUMN directive_document_id UUID + REFERENCES directive_documents(id) ON DELETE SET NULL; + +CREATE INDEX idx_tasks_directive_document_id + ON tasks(directive_document_id); + +ALTER TABLE directive_steps + ADD COLUMN directive_document_id UUID + REFERENCES directive_documents(id) ON DELETE SET NULL; + +CREATE INDEX idx_directive_steps_directive_document_id + ON directive_steps(directive_document_id); + +-- Backfill: each task whose directive_id is set inherits the directive's +-- single existing document (the oldest one — the one created by the previous +-- migration's backfill). Tasks not associated with any directive stay NULL. +UPDATE tasks t + SET directive_document_id = ( + SELECT id FROM directive_documents d + WHERE d.directive_id = t.directive_id + ORDER BY d.created_at ASC + LIMIT 1 + ) + WHERE t.directive_id IS NOT NULL; + +-- Same for directive_steps. directive_id is NOT NULL on this table, so we +-- don't need an explicit guard — the LIMIT-1 subquery returns NULL for any +-- directive that happens to have no documents (defensive against bad data), +-- which is fine because the column is nullable. +UPDATE directive_steps s + SET directive_document_id = ( + SELECT id FROM directive_documents d + WHERE d.directive_id = s.directive_id + ORDER BY d.created_at ASC + LIMIT 1 + ); |
