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:
mAi
2026-05-15 13:09:32 +02:00
parent 88d9cb88d0
commit 68121c6e51
27 changed files with 513 additions and 0 deletions

16
.claude/CLAUDE.md Normal file
View 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
View 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.

View 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.

View 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
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-clone

1
.claude/skills/mai-coder Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-coder

1
.claude/skills/mai-commit Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-commit

View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-consultant

1
.claude/skills/mai-debrief Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-debrief

1
.claude/skills/mai-enemy Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-enemy

View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-excalidraw

1
.claude/skills/mai-fixer Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-fixer

1
.claude/skills/mai-gitster Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-gitster

1
.claude/skills/mai-head Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-head

1
.claude/skills/mai-init Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-init

1
.claude/skills/mai-inventor Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-inventor

1
.claude/skills/mai-lead Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-lead

1
.claude/skills/mai-maister Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-maister

1
.claude/skills/mai-member Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-member

View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-researcher

1
.claude/skills/mai-think Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-think

1
.claude/skills/mai-web Symbolic link
View File

@@ -0,0 +1 @@
/home/m/.mai/skills/mai-web

4
.m/.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
workers.json
spawn.lock
session.yaml
config.reference.yaml

171
.m/config.yaml Normal file
View 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
View File

@@ -0,0 +1,11 @@
{
"mcpServers": {
"supabase": {
"type": "http",
"url": "http://100.99.98.201:8000/mcp",
"headers": {
"Authorization": "Basic ${SUPABASE_AUTH}"
}
}
}
}

1
AGENTS.md Symbolic link
View File

@@ -0,0 +1 @@
.claude/CLAUDE.md

250
docs/design.md Normal file
View 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 23 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