From b168f1cc073c64632945ae57eb5249ec7e7a0704 Mon Sep 17 00:00:00 2001 From: Lovebug MCP Date: Mon, 11 May 2026 21:22:45 +0000 Subject: [PATCH] =?UTF-8?q?mcp:=20project-plan=20=E2=80=94=20herbylab=20Pr?= =?UTF-8?q?oject=20Management?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Homelab/herbylab-project-management.md | 174 ++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 Sources/Homelab/herbylab-project-management.md diff --git a/Sources/Homelab/herbylab-project-management.md b/Sources/Homelab/herbylab-project-management.md new file mode 100644 index 0000000..46714a7 --- /dev/null +++ b/Sources/Homelab/herbylab-project-management.md @@ -0,0 +1,174 @@ +--- +created: '2026-05-11' +path: Sources/Homelab +project: herbylab-project-management +status: active +tags: +- mcp +- automation +- n8n +- obsidian +type: project-plan +updated: '2026-05-11' +--- + +## Goal + +Extend `herbylab` MCP to manage projects and notes as first-class artifacts backed by Postgres, with Trello as the visual projection. Vault remains the immutable content store in Phase 1; Postgres becomes sole source in a later phase. + +Solves the pain of status hygiene and project visibility — 44+ vault entries, almost all flagged `active`, no aggregate view, no real query surface, frontmatter status edits feel wrong. + +## Locked Decisions + +**Three-layer model.** Mirrors Trellis: +- Vault md files — content, narrative, immutable. Feeds wiki-vault. +- Postgres (`herbylab` schema) — state, metadata, queryable index. +- Trello — visual projection of project state. Read-mostly. + +**Single MCP, not split.** Project tools live alongside vault tools in `herbylab`. One connector, one tool surface. + +**Polymorphic tool shape.** `create_artifact(kind, ...)` and `update_artifact(kind, slug, ...)` dispatch on `kind`. Adds future artifact types without growing the tool surface. + +**Postgres co-located with Trellis.** Same instance, separate schema (`herbylab.*`). Trellis's schema-namespace pattern leaves room for this by design. + +**Phase 1: dual-write, immutable entries.** +- `create_artifact` writes Postgres row + vault file atomically. +- No content updates via MCP — entries immutable once created. +- `update_artifact` only mutates metadata (status, tags, title, summary, domain). +- Vault stays the read surface for Obsidian and wiki-vault. +- New phases/addenda get new files, matching existing pattern (`ob1-deployment` + `ob1-main-deployment`). + +**Phase N: vault retires, Postgres becomes sole source.** +- Viewer TBD — wiki-vault adaptation or new web UI served from Postgres. +- Out of scope for this plan. + +**Source of truth for status: Postgres.** Status changes mid-conversation via `update_artifact` — never a file edit. + +**Status propagates via NOTIFY → n8n → Trello.** Postgres trigger fires `pg_notify('project_changed', ...)` on insert/update. n8n LISTENs, projects to Trello. + +**Frontmatter scope simplifies.** Vault frontmatter holds identity (`project`, `type`, `path`, `tags`, `created`). Status and `updated` leave frontmatter — Postgres concerns. + +## Schema (Phase 1) + +```sql +CREATE SCHEMA herbylab; + +CREATE TYPE herbylab.artifact_kind AS ENUM ('note', 'project'); +CREATE TYPE herbylab.project_status AS ENUM ('active', 'paused', 'blocked', 'completed', 'archived'); +CREATE TYPE herbylab.project_domain AS ENUM ('homelab', 'dev', 'venture', 'reference'); + +CREATE TABLE herbylab.artifacts ( + slug TEXT NOT NULL, + kind herbylab.artifact_kind NOT NULL, + title TEXT NOT NULL, + domain herbylab.project_domain, + tags TEXT[] DEFAULT '{}', + vault_path TEXT NOT NULL, + body TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + PRIMARY KEY (kind, slug) +); + +CREATE TABLE herbylab.projects ( + slug TEXT PRIMARY KEY, + status herbylab.project_status NOT NULL DEFAULT 'active', + summary TEXT, + trello_card_id TEXT, + FOREIGN KEY (kind, slug) REFERENCES herbylab.artifacts (kind, slug) + DEFERRABLE INITIALLY DEFERRED +); + +CREATE INDEX idx_artifacts_kind ON herbylab.artifacts(kind); +CREATE INDEX idx_artifacts_tags ON herbylab.artifacts USING GIN(tags); +CREATE INDEX idx_projects_status ON herbylab.projects(status); +``` + +`body` stores the markdown blob. Vault file holds the same content. Dual-write keeps them in lockstep; immutability eliminates drift risk. + +## NOTIFY trigger + +```sql +CREATE OR REPLACE FUNCTION herbylab.notify_project_change() RETURNS TRIGGER AS $$ +DECLARE payload JSON; +BEGIN + payload := json_build_object( + 'op', TG_OP, + 'slug', COALESCE(NEW.slug, OLD.slug), + 'status', COALESCE(NEW.status, OLD.status) + ); + PERFORM pg_notify('project_changed', payload::text); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER projects_notify +AFTER INSERT OR UPDATE OR DELETE ON herbylab.projects +FOR EACH ROW EXECUTE FUNCTION herbylab.notify_project_change(); +``` + +## Tool surface + +**Vault tools (existing, unchanged):** +- `save_vault_note` +- `get_vault_project` +- `check_vault_file` +- `list_vault_projects` +- `get_vault_schema` + +**Artifact tools (new):** +- `create_artifact(kind, slug, title, vault_path, content, domain?, tags?, summary?)` — writes Postgres row + vault file. For `kind="project"`, also inserts `projects` row with default `status='active'`. +- `update_artifact(kind, slug, **fields)` — mutates metadata only (status, title, tags, summary, domain). Never touches body or vault file. +- `get_artifact(kind, slug)` — returns row + linked file path. +- `list_artifacts(kind?, status?, domain?, tag?)` — filtered query. + +`list_vault_projects` deprecates in favor of `list_artifacts` once cutover completes. + +## Open Items + +- [ ] Postgres deployment shape — depends on Trellis decision (LXC vs container) +- [ ] Migration tooling — match whatever Trellis lands on +- [ ] Trello board layout — single board with status lists vs board per domain +- [ ] Duplicate cleanup before backfill: `ob1-deployment` + `ob1-main-deployment`, `editor-stack-upgrade` (Homelab + Venture), `content-hub-phase5-architecture` + `-planning` +- [ ] Naming pin-down: memory has both `herbylab` and `herby-dev` — settle on one + +## Phases + +### Phase 1 — Infrastructure +- [ ] Postgres instance available with `herbylab` schema on the Trellis-shared instance +- [ ] Migration tooling chosen (match Trellis decision) +- [ ] Apply schema, types, indexes, NOTIFY trigger + +### Phase 2 — MCP tools +- [ ] `create_artifact` (dual-write, transactional) +- [ ] `update_artifact` (metadata only) +- [ ] `get_artifact`, `list_artifacts` (read paths) +- [ ] Tag normalization mirroring Trellis pattern + +### Phase 3 — Trello projection +- [ ] Trello board structure: lists for `active`, `paused`, `blocked`, `completed`. Labels for domain. +- [ ] n8n workflow: Postgres LISTEN node on `project_changed` +- [ ] Branch on op: INSERT → create card, UPDATE → move/edit card, DELETE → archive +- [ ] Store `trello_card_id` back to `projects` row on creation + +### Phase 4 — Backfill and cutover +- [ ] Script walks existing vault project-plan files, populates `artifacts` + `projects` rows +- [ ] Status defaults to current frontmatter value (42 of 44 are `active`) +- [ ] Manual triage pass in Trello — drag to honest columns +- [ ] Strip `status` / `updated` from vault frontmatter going forward +- [ ] Backfill of existing frontmatter optional + +### Phase N — Vault retirement +- Deferred. Revisit once Phase 1–4 are operational and read patterns are clearer. + +## Notes + +**Why dual-write in Phase 1.** Keeps two readers (Obsidian, wiki-vault) functional during transition. Immutability of entries kills the main drift risk — entries land once, neither side updates content. Update tool only touches metadata, which lives only in Postgres. + +**Why immutable entries.** Matches existing behavior — new phases get new files (`-phase2`, `-addendum`) rather than edits. Codifying this as a constraint removes a class of bugs and makes Phase N (Postgres-only) trivially achievable later. + +**Why Postgres co-located with Trellis.** Trellis's plan explicitly chose schema-namespacing to leave room for consolidation. Same instance, separate schemas, no new infrastructure. Backup/recovery story is shared. + +**Why not Trellis itself for project tracking.** Trellis is for conversation threads — coarse session-level checkpoints, append-only events, paused/resumed work. Projects are different: durable, named, status-bearing, visualized externally. Different lifecycle, different reader, different surface. + +**Why the polymorphic tool shape.** Avoids tool sprawl. Adding a future artifact type (task, decision log, etc.) is a `kind` value, not a new MCP tool. Tradeoff is a fatter schema in the tool description. \ No newline at end of file