chore: bootstrap projax scaffolding + PRD
Copy the design PRD, .claude config, .m config, .mcp.json, and AGENTS.md symlink from m's main working tree so the worker has the full project context before starting Phase 1 implementation.
This commit is contained in:
16
.claude/CLAUDE.md
Normal file
16
.claude/CLAUDE.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# projax Project Instructions
|
||||||
|
|
||||||
|
<!-- Add your project-specific instructions for AI tools here -->
|
||||||
|
<!-- This file is used by Claude Code, Cursor, Windsurf, and other AI assistants -->
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
Describe your project here.
|
||||||
|
|
||||||
|
## Code Style & Conventions
|
||||||
|
|
||||||
|
Document your coding conventions.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Describe key architectural decisions.
|
||||||
14
.claude/agents/coder.md
Normal file
14
.claude/agents/coder.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Coder Agent
|
||||||
|
|
||||||
|
Implementation-focused agent for writing and refactoring code.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
- Follow existing patterns in the codebase
|
||||||
|
- Write minimal, focused code
|
||||||
|
- Run tests after changes
|
||||||
|
- Commit incrementally with descriptive messages
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
All tools available.
|
||||||
14
.claude/agents/researcher.md
Normal file
14
.claude/agents/researcher.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Researcher Agent
|
||||||
|
|
||||||
|
Exploration and information gathering agent.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
- Search broadly, then narrow down
|
||||||
|
- Document findings in structured format
|
||||||
|
- Cite sources and file paths
|
||||||
|
- Summarize key insights, don't dump raw data
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
Read-only tools preferred. Use Bash only for non-destructive commands.
|
||||||
14
.claude/agents/reviewer.md
Normal file
14
.claude/agents/reviewer.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# Reviewer Agent
|
||||||
|
|
||||||
|
Code review agent for checking quality and correctness.
|
||||||
|
|
||||||
|
## Instructions
|
||||||
|
|
||||||
|
- Check for bugs, security issues, and style violations
|
||||||
|
- Verify test coverage for changes
|
||||||
|
- Suggest improvements concisely
|
||||||
|
- Focus on correctness over style preferences
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
Read-only tools. No file modifications.
|
||||||
1
.claude/skills/mai-clone
Symbolic link
1
.claude/skills/mai-clone
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-clone
|
||||||
1
.claude/skills/mai-coder
Symbolic link
1
.claude/skills/mai-coder
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-coder
|
||||||
1
.claude/skills/mai-commit
Symbolic link
1
.claude/skills/mai-commit
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-commit
|
||||||
1
.claude/skills/mai-consultant
Symbolic link
1
.claude/skills/mai-consultant
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-consultant
|
||||||
1
.claude/skills/mai-debrief
Symbolic link
1
.claude/skills/mai-debrief
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-debrief
|
||||||
1
.claude/skills/mai-enemy
Symbolic link
1
.claude/skills/mai-enemy
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-enemy
|
||||||
1
.claude/skills/mai-excalidraw
Symbolic link
1
.claude/skills/mai-excalidraw
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-excalidraw
|
||||||
1
.claude/skills/mai-fixer
Symbolic link
1
.claude/skills/mai-fixer
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-fixer
|
||||||
1
.claude/skills/mai-gitster
Symbolic link
1
.claude/skills/mai-gitster
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-gitster
|
||||||
1
.claude/skills/mai-head
Symbolic link
1
.claude/skills/mai-head
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-head
|
||||||
1
.claude/skills/mai-init
Symbolic link
1
.claude/skills/mai-init
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-init
|
||||||
1
.claude/skills/mai-inventor
Symbolic link
1
.claude/skills/mai-inventor
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-inventor
|
||||||
1
.claude/skills/mai-lead
Symbolic link
1
.claude/skills/mai-lead
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-lead
|
||||||
1
.claude/skills/mai-maister
Symbolic link
1
.claude/skills/mai-maister
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-maister
|
||||||
1
.claude/skills/mai-member
Symbolic link
1
.claude/skills/mai-member
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-member
|
||||||
1
.claude/skills/mai-researcher
Symbolic link
1
.claude/skills/mai-researcher
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-researcher
|
||||||
1
.claude/skills/mai-think
Symbolic link
1
.claude/skills/mai-think
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-think
|
||||||
1
.claude/skills/mai-web
Symbolic link
1
.claude/skills/mai-web
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
/home/m/.mai/skills/mai-web
|
||||||
4
.m/.gitignore
vendored
Normal file
4
.m/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
workers.json
|
||||||
|
spawn.lock
|
||||||
|
session.yaml
|
||||||
|
config.reference.yaml
|
||||||
171
.m/config.yaml
Normal file
171
.m/config.yaml
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# Project-specific mai configuration
|
||||||
|
# Auto-generated by 'mai init' — run 'mai setup' to customize
|
||||||
|
|
||||||
|
provider: claude
|
||||||
|
providers:
|
||||||
|
claude:
|
||||||
|
api_key: ""
|
||||||
|
model: claude-sonnet-4-20250514
|
||||||
|
base_url: https://api.anthropic.com/v1
|
||||||
|
ollama:
|
||||||
|
host: http://localhost:11434
|
||||||
|
model: llama3.2
|
||||||
|
memory:
|
||||||
|
enabled: true
|
||||||
|
backend: ""
|
||||||
|
path: ""
|
||||||
|
url: postgres://mai_memory.your-tenant-id:maiMem6034supa@100.99.98.201:6543/postgres?sslmode=disable
|
||||||
|
group_id: ""
|
||||||
|
cache_ttl: 5m0s
|
||||||
|
auto_load: true
|
||||||
|
embedding_url: ""
|
||||||
|
embedding_model: ""
|
||||||
|
gitea:
|
||||||
|
url: https://mgit.msbls.de
|
||||||
|
repo: m/projax
|
||||||
|
token: ""
|
||||||
|
sync:
|
||||||
|
enabled: false
|
||||||
|
interval: 0s
|
||||||
|
repos: []
|
||||||
|
auto_queue: false
|
||||||
|
api:
|
||||||
|
api_key: ""
|
||||||
|
basic_auth:
|
||||||
|
username: ""
|
||||||
|
password: ""
|
||||||
|
public_endpoints:
|
||||||
|
- /api/health
|
||||||
|
ui:
|
||||||
|
theme: default
|
||||||
|
show_sidebar: true
|
||||||
|
animation: true
|
||||||
|
persona: true
|
||||||
|
avatar_pack: ""
|
||||||
|
worker:
|
||||||
|
names: []
|
||||||
|
name_scheme: role
|
||||||
|
default_level: standard
|
||||||
|
auto_discard: false
|
||||||
|
max_workers: 5
|
||||||
|
persistent: true
|
||||||
|
head:
|
||||||
|
name: ""
|
||||||
|
capacity:
|
||||||
|
global:
|
||||||
|
max_workers: 5
|
||||||
|
max_heads: 3
|
||||||
|
per_worker:
|
||||||
|
max_tasks_lifetime: 0
|
||||||
|
max_concurrent: 1
|
||||||
|
max_context_tokens: 0
|
||||||
|
per_head:
|
||||||
|
max_workers: 10
|
||||||
|
resources:
|
||||||
|
max_memory_mb: 0
|
||||||
|
max_cpu_percent: 0
|
||||||
|
queue:
|
||||||
|
max_pending: 100
|
||||||
|
stale_task_days: 30
|
||||||
|
workforce:
|
||||||
|
timeouts:
|
||||||
|
task_default: 0s
|
||||||
|
task_max: 0s
|
||||||
|
idle_before_warn: 10m0s
|
||||||
|
idle_before_kill: 30m0s
|
||||||
|
quality_check: 2m0s
|
||||||
|
context:
|
||||||
|
max_tokens_per_worker: 0
|
||||||
|
max_tokens_global: 0
|
||||||
|
warn_threshold: 0.8
|
||||||
|
truncate_strategy: oldest
|
||||||
|
delegation:
|
||||||
|
strategy: skill_match
|
||||||
|
preferred_role: coder
|
||||||
|
auto_delegate: false
|
||||||
|
max_depth: 3
|
||||||
|
allowed_roles:
|
||||||
|
- coder
|
||||||
|
- researcher
|
||||||
|
- fixer
|
||||||
|
peppy:
|
||||||
|
enabled: false
|
||||||
|
style: calm
|
||||||
|
interval: 5m0s
|
||||||
|
emoji: false
|
||||||
|
nudges: true
|
||||||
|
nudge_main: false
|
||||||
|
custom_prompt: ""
|
||||||
|
stall_threshold: 0s
|
||||||
|
restart_enabled: false
|
||||||
|
max_shifts: 0
|
||||||
|
crash_detection: false
|
||||||
|
max_crash_retries: 0
|
||||||
|
quality_gates:
|
||||||
|
enabled: true
|
||||||
|
checks: []
|
||||||
|
preflight:
|
||||||
|
enabled: false
|
||||||
|
type: ""
|
||||||
|
root: ""
|
||||||
|
checks: []
|
||||||
|
guardrails:
|
||||||
|
enabled: false
|
||||||
|
use_defaults: true
|
||||||
|
output:
|
||||||
|
coder_checks: []
|
||||||
|
researcher_checks: []
|
||||||
|
fixer_checks: []
|
||||||
|
custom_checks: {}
|
||||||
|
global_checks: []
|
||||||
|
tools:
|
||||||
|
role_rules: {}
|
||||||
|
deny_patterns: []
|
||||||
|
allow_patterns: []
|
||||||
|
schemas:
|
||||||
|
report_schemas: {}
|
||||||
|
deliverable_schemas: {}
|
||||||
|
modes:
|
||||||
|
yolo: false
|
||||||
|
self_improvement: false
|
||||||
|
autonomous: false
|
||||||
|
verbose: false
|
||||||
|
improve_interval: 0s
|
||||||
|
predict_interval: 0s
|
||||||
|
layouts:
|
||||||
|
head: ""
|
||||||
|
worker: ""
|
||||||
|
roles: {}
|
||||||
|
dog:
|
||||||
|
name: buddy
|
||||||
|
supabase:
|
||||||
|
url: ""
|
||||||
|
role_key: ""
|
||||||
|
anon_key: ""
|
||||||
|
schema: mai
|
||||||
|
storage:
|
||||||
|
backend: ""
|
||||||
|
postgres:
|
||||||
|
url: ""
|
||||||
|
max_conns: 0
|
||||||
|
min_conns: 0
|
||||||
|
max_conn_lifetime: 0s
|
||||||
|
idle:
|
||||||
|
behavior: wait
|
||||||
|
auto_hire: false
|
||||||
|
prompt: ""
|
||||||
|
git:
|
||||||
|
worktrees:
|
||||||
|
enabled: true
|
||||||
|
delete_branch: false
|
||||||
|
dir: .worktrees
|
||||||
|
phase:
|
||||||
|
enabled: false
|
||||||
|
current: ""
|
||||||
|
allowed_roles: {}
|
||||||
|
goal: ""
|
||||||
|
skills: {}
|
||||||
|
editor: nvim
|
||||||
|
log_level: info
|
||||||
|
project_detection: true
|
||||||
|
tone: professional
|
||||||
11
.mcp.json
Normal file
11
.mcp.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"supabase": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "http://100.99.98.201:8000/mcp",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Basic ${SUPABASE_AUTH}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
250
docs/design.md
Normal file
250
docs/design.md
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
# projax — PRD
|
||||||
|
|
||||||
|
**Status:** v1 draft, 2026-05-15
|
||||||
|
**Authors:** m, head (dialogue)
|
||||||
|
**Scope:** Phase-1 build sufficient to live with the system; phases 2–3 listed but deferred.
|
||||||
|
|
||||||
|
## 1. Purpose
|
||||||
|
|
||||||
|
projax is m's personal data backbone for self-management — areas of life, projects within them, and aggregated views over tasks that live elsewhere. It subsumes (over time) the scattered state currently held in `mai.projects`, CalDAV task lists, Gitea issues, and mBrian topic hubs. No interface is canonical; each is a view.
|
||||||
|
|
||||||
|
**Meta-requirement: flexibility.** m's self-model evolves. Identity is by UUID; everything human-readable is renameable. The data model leans on jsonb + array-typed kinds so future re-categorization doesn't require a migration.
|
||||||
|
|
||||||
|
## 2. Model
|
||||||
|
|
||||||
|
### 2.1 Two kinds, freely nestable
|
||||||
|
|
||||||
|
```
|
||||||
|
area ─┐
|
||||||
|
├─ project ─┐
|
||||||
|
│ ├─ project ─┐ (sub-projects allowed, any depth)
|
||||||
|
│ │ └─ …
|
||||||
|
└─ task (external ref)
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Area** — a container without start/end. Long-running domain of life. Areas live at the root (`parent_id = NULL`). Examples: `dev`, `sports`, `home`, `work`, `health`, `finances`, `social`.
|
||||||
|
- **Project** — a bounded effort with (usually) a start and an end. Lives under an area, or under another project. Sub-projects nest to arbitrary depth (`home.spring-clean.bathroom.tiles` is fine if that's how m thinks). Examples: `home.spring-clean`, `dev.prjx`, `sports.giro-okt`.
|
||||||
|
- **Task** — atomic work item. **Lives outside projax** (CalDAV todos, Gitea issues, `mai.tasks`). projax references and aggregates them; it does not own them.
|
||||||
|
|
||||||
|
Areas and projects share one table (`projax.items`) distinguished by the `kind` array column. The tree (`parent_id`) is unconstrained on depth; the only structural rules are:
|
||||||
|
|
||||||
|
- An area's `parent_id` must be `NULL` (areas are roots).
|
||||||
|
- A project's `parent_id` must point to an area or another project (no project at root).
|
||||||
|
- No cycles (enforced by the path trigger).
|
||||||
|
|
||||||
|
Tasks are referenced via `projax.item_links`.
|
||||||
|
|
||||||
|
### 2.2 Identity & naming
|
||||||
|
|
||||||
|
- `id uuid` — canonical, immutable.
|
||||||
|
- `slug text` — local-only segment (no dots). Renameable freely. Examples: `prjx`, `spring-clean`, `upc.deadlines` would be split as parent slug `upc` + child slug `deadlines`.
|
||||||
|
- `path text` — full dot-joined path computed from parent walk. Cached column maintained by trigger; not the source of truth.
|
||||||
|
- Slug convention: lowercase, vowel-elided where natural (`prjx`, `mai`, `mbrn` if m wishes), kebab-allowed for multi-word leaves (`spring-clean`).
|
||||||
|
- Aliases: `aliases text[]` keeps old slugs searchable after rename.
|
||||||
|
|
||||||
|
### 2.3 Lifecycle (thin)
|
||||||
|
|
||||||
|
For projects only — areas don't have lifecycle.
|
||||||
|
|
||||||
|
```
|
||||||
|
active → done → archived
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. Free-text in `content_md` covers the nuance ("waiting on Brian," "paused until June"). No rich state machine; m flagged richer schemes as rot-prone.
|
||||||
|
|
||||||
|
### 2.4 Relationships
|
||||||
|
|
||||||
|
- **Tree** (parent/child within projax): `items.parent_id uuid` self-FK. Areas have `parent_id = NULL`. Projects point at their area or another project. Arbitrary nesting depth.
|
||||||
|
- **External refs** (`projax.item_links`): each row links an `item_id` to a typed external resource — caldav-todo, gitea-issue, mai-task, mai-project, mbrian-node, etc. Used both for aggregating tasks and for soft cross-references.
|
||||||
|
|
||||||
|
## 3. Schema (Postgres, msupabase, schema `projax`)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
create schema if not exists projax;
|
||||||
|
|
||||||
|
create table projax.items (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
kind text[] not null default '{}', -- ['area'] or ['project'] (multi-tag allowed for future)
|
||||||
|
title text not null,
|
||||||
|
slug text not null, -- local segment, no dots
|
||||||
|
path text not null, -- computed, e.g. 'home.spring-clean'
|
||||||
|
parent_id uuid references projax.items(id) on delete restrict,
|
||||||
|
content_md text default '',
|
||||||
|
aliases text[] default '{}',
|
||||||
|
metadata jsonb not null default '{}'::jsonb,
|
||||||
|
status text not null default 'active', -- active | done | archived (projects only)
|
||||||
|
pinned boolean not null default false,
|
||||||
|
archived boolean not null default false,
|
||||||
|
start_time timestamptz,
|
||||||
|
end_time timestamptz,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
updated_at timestamptz not null default now(),
|
||||||
|
deleted_at timestamptz,
|
||||||
|
unique (parent_id, slug)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index items_path_idx on projax.items (path);
|
||||||
|
create index items_kind_idx on projax.items using gin (kind);
|
||||||
|
create index items_parent_idx on projax.items (parent_id);
|
||||||
|
|
||||||
|
create table projax.item_links (
|
||||||
|
id uuid primary key default gen_random_uuid(),
|
||||||
|
item_id uuid not null references projax.items(id) on delete cascade,
|
||||||
|
ref_type text not null, -- 'caldav-todo' | 'gitea-issue' | 'mai-task' | 'mai-project' | 'mbrian-node' | 'url' | ...
|
||||||
|
ref_id text not null, -- opaque external identifier
|
||||||
|
rel text not null default 'contains', -- 'contains' | 'related' | 'blocked-by' | 'derived-from'
|
||||||
|
note text,
|
||||||
|
metadata jsonb not null default '{}'::jsonb,
|
||||||
|
created_at timestamptz not null default now(),
|
||||||
|
unique (item_id, ref_type, ref_id, rel)
|
||||||
|
);
|
||||||
|
|
||||||
|
create index item_links_item_idx on projax.item_links (item_id);
|
||||||
|
create index item_links_ref_idx on projax.item_links (ref_type, ref_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.1 The `path` trigger
|
||||||
|
|
||||||
|
`path` is maintained by trigger on insert/update: walks `parent_id` to the root, joins slugs with `.`. Recomputed for the subtree when a parent is renamed or re-parented. Keeps queries cheap.
|
||||||
|
|
||||||
|
### 3.2 The mai.projects adapter view
|
||||||
|
|
||||||
|
mai.projects stays untouched. projax surfaces it in the unified item stream via a read-only view:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
create or replace view projax.items_unified as
|
||||||
|
select
|
||||||
|
id,
|
||||||
|
kind,
|
||||||
|
title,
|
||||||
|
slug,
|
||||||
|
path,
|
||||||
|
parent_id,
|
||||||
|
content_md,
|
||||||
|
status,
|
||||||
|
pinned,
|
||||||
|
archived,
|
||||||
|
start_time,
|
||||||
|
end_time,
|
||||||
|
'projax'::text as source,
|
||||||
|
created_at,
|
||||||
|
updated_at
|
||||||
|
from projax.items
|
||||||
|
where deleted_at is null
|
||||||
|
|
||||||
|
union all
|
||||||
|
|
||||||
|
select
|
||||||
|
('00000000-0000-0000-0000-' || substr(md5(p.id), 1, 12))::uuid as id, -- deterministic placeholder
|
||||||
|
array['project']::text[] as kind,
|
||||||
|
p.name as title,
|
||||||
|
p.id as slug,
|
||||||
|
'mai.' || p.id as path,
|
||||||
|
null::uuid as parent_id,
|
||||||
|
coalesce(p.goal, '') as content_md,
|
||||||
|
case p.status
|
||||||
|
when 'active' then 'active'
|
||||||
|
when 'sleeping' then 'archived'
|
||||||
|
when 'archived' then 'archived'
|
||||||
|
else 'active'
|
||||||
|
end as status,
|
||||||
|
false as pinned,
|
||||||
|
(p.status = 'archived') as archived,
|
||||||
|
null::timestamptz as start_time,
|
||||||
|
null::timestamptz as end_time,
|
||||||
|
'mai.projects'::text as source,
|
||||||
|
p.created_at,
|
||||||
|
p.updated_at
|
||||||
|
from mai.projects p;
|
||||||
|
```
|
||||||
|
|
||||||
|
UI reads `items_unified`. Writes target `projax.items` only. Once m wants to fully migrate, the `mai.projects` half is dropped from the view and rows are copied across with real UUIDs + proper parent assignment.
|
||||||
|
|
||||||
|
### 3.3 Classification overlay
|
||||||
|
|
||||||
|
For each mai.projects row, m can later promote it into projax-native (assigning area parent, real slug, kind tweak). Until promoted it appears as a top-level orphan project tagged `source=mai.projects`. An admin page surfaces the unmapped set and lets m one-click classify.
|
||||||
|
|
||||||
|
## 4. Interfaces
|
||||||
|
|
||||||
|
### 4.1 Phase 1 — MVP (this build)
|
||||||
|
|
||||||
|
**Web frontend at `https://projax.msbls.de`**, single binary, served by the same Go process that talks to msupabase.
|
||||||
|
|
||||||
|
Pages:
|
||||||
|
|
||||||
|
1. **Tree view** (`/`) — collapsible tree of areas → projects, reads `items_unified`. Status badges, search bar (slug, title, alias, content_md).
|
||||||
|
2. **Item detail** (`/i/{path}`) — full editor for projax-native items (title, slug, parent, kind, status, start/end, content_md). Read-only view for mai.projects-sourced rows with a "Promote to projax" button.
|
||||||
|
3. **New item** (`/new?parent={path}`) — small form, prefilled with parent.
|
||||||
|
4. **Classify orphans** (`/admin/classify`) — list of unmapped mai.projects rows, inline assign-to-area control.
|
||||||
|
|
||||||
|
Auth: shared msupabase login (matches flexsiebels precedent), single-user m.
|
||||||
|
|
||||||
|
### 4.2 Phase 2 — task aggregation
|
||||||
|
|
||||||
|
- **CalDAV ingest** — read-only mirror of m's CalDAV todo lists into `item_links` with `ref_type=caldav-todo`. Per-area mapping (e.g. `home` aggregates from CalDAV list "Home"). Background sync, no writeback initially.
|
||||||
|
- **Gitea ingest** — read-only mirror of issues on linked repos. `mai.projects.repo` field is a hint; per-item override possible.
|
||||||
|
|
||||||
|
### 4.3 Phase 3 — visualization & integrations
|
||||||
|
|
||||||
|
- **Excalidraw view** — visual roadmap, dependencies, area-overview boards. Generated from items_unified.
|
||||||
|
- **MCP** — `mcp__projax__*` so otto and other workers can read/write projax. Pattern follows mcp__mai__.
|
||||||
|
- **Otto-PWA integration** — read-mostly surface for m's day-to-day. Defer until projax has lived long enough to know what otto actually needs.
|
||||||
|
|
||||||
|
## 5. Tech stack
|
||||||
|
|
||||||
|
- **Backend**: Go single binary. `pgx` for Postgres. HTMX-driven HTML rendered server-side (Go `html/template`). No frontend build step. Static assets bundled with `embed`. Matches m's dotfile-stated preferences.
|
||||||
|
- **Database**: msupabase, schema `projax` (new). View `projax.items_unified` reads across `projax.*` + `mai.projects`. RLS off for v1 (single-user).
|
||||||
|
- **Hosting**: Dokploy on mlake, domain `projax.msbls.de`. Tailscale-only network (no public exposure).
|
||||||
|
- **Repo**: `m/projax` (already exists). Branch strategy per project CLAUDE.md (main + short-lived feat/fix branches, no dev branch initially).
|
||||||
|
|
||||||
|
Alternative considered: SvelteKit + Bun (matches flexsiebels). Rejected for v1 — CRUD admin scale doesn't justify the build chain.
|
||||||
|
|
||||||
|
## 6. Migration plan
|
||||||
|
|
||||||
|
Three phases, smallest viable each:
|
||||||
|
|
||||||
|
**1a — Schema + seed**: create `projax.items`, `projax.item_links`, path trigger. Seed the seven day-one areas (`dev`, `sports`, `home`, `work`, `health`, `finances`, `social`) as `kind=['area']`, `parent_id=null`.
|
||||||
|
|
||||||
|
**1b — Adapter view**: deploy `items_unified`. All 28 mai.projects rows now visible in the tree as top-level orphans.
|
||||||
|
|
||||||
|
**1c — Classification UI**: the `/admin/classify` page so m can drag mai.projects rows under areas. Drag = create a projax-native item with `kind=['project']` + `parent_id` set + `item_links` row pointing at the mai.projects row. mai.projects untouched; the projax row owns area assignment + projax metadata.
|
||||||
|
|
||||||
|
After 1c, m can use the system. Test rows in mai.projects either stay as orphans (ignored) or get a `source-filter` to hide them.
|
||||||
|
|
||||||
|
## 7. Out of scope
|
||||||
|
|
||||||
|
- Multi-user (single-tenant, m only)
|
||||||
|
- Mobile-first responsive (desktop browser is enough)
|
||||||
|
- Public exposure (Tailscale only)
|
||||||
|
- Generic SaaS instincts (admin panels, billing, audit logs)
|
||||||
|
- CLI surface (m has explicitly opted out)
|
||||||
|
- Bidirectional CalDAV/Gitea sync in v1 (read-only first)
|
||||||
|
- Real-time collaboration features
|
||||||
|
|
||||||
|
## 8. Open questions (post-PRD)
|
||||||
|
|
||||||
|
- **Path-trigger correctness** under cycle attempts: enforce acyclicity via check in trigger.
|
||||||
|
- **`mai.projects` test row hiding**: drop them from the view via name pattern, or surface them with a "test" tag?
|
||||||
|
- **Classification promotion semantics**: when a mai.projects row is promoted, does the projax item replace it in the unified view, or do both still appear? Default: projax wins, view filters out adapted mai rows.
|
||||||
|
- **Auth**: re-use flexsiebels Supabase auth, or simpler shared-secret cookie? msupabase auth is heavier than v1 needs.
|
||||||
|
- **mBrian topic-hub linkage**: do we auto-suggest mbrian topic links when an item is created with a matching slug? Defer to phase 3.
|
||||||
|
|
||||||
|
## 9. Phase-1 deliverable checklist
|
||||||
|
|
||||||
|
- [ ] `projax.items` + `projax.item_links` migrations in `db/migrations/`
|
||||||
|
- [ ] Path trigger + tests
|
||||||
|
- [ ] `projax.items_unified` view
|
||||||
|
- [ ] Go binary: HTTP server, pgx pool, html/template + HTMX, embed static
|
||||||
|
- [ ] Pages: tree, detail, new, classify
|
||||||
|
- [ ] Auth: msupabase session cookie OR shared-secret (decide in 1a)
|
||||||
|
- [ ] Dockerfile + Dokploy config for `projax.msbls.de`
|
||||||
|
- [ ] Seed migration for the seven day-one areas
|
||||||
|
- [ ] README + run instructions
|
||||||
|
|
||||||
|
## 10. References
|
||||||
|
|
||||||
|
- Project CLAUDE.md (this repo) — purpose, constraints, gated worker flow
|
||||||
|
- `~/.claude/CLAUDE.md` — global conventions (memory, channel routing, git strategy)
|
||||||
|
- `mai.projects` schema (msupabase) — current state being adapted
|
||||||
|
- mBrian `nodes`/`edges` schema — terminology source
|
||||||
|
- otto session 2026-05-15 — inventory motivating this project
|
||||||
Reference in New Issue
Block a user