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
1 change: 1 addition & 0 deletions .agents/skills
53 changes: 53 additions & 0 deletions .ai/LANGUAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# MDCMS vocabulary

Use these terms exactly. Do not coin synonyms. If a needed concept is missing, add it here in the same change that introduces it to the code.

## Domain terms

| Term | Meaning | Don't say |
| ---------------- | ---------------------------------------------------------------------------------- | --------------------------- |
| **Document** | A single piece of editable content (a row in `documents`) | entry, item, record, post |
| **Content type** | A schema definition for documents (e.g. `BlogPost`) | model, collection, kind |
| **Field** | A typed property on a content type | column, attribute, property |
| **Reference** | A field whose value points to another document | relation, link, fk |
| **Project** | A top-level isolation boundary (a tenant) | workspace, org, site |
| **Environment** | A named state within a project (e.g. `draft`, `prod`) | branch, stage |
| **Locale** | A language/region pair on a translatable document | language, i18n target |
| **Schema** | The combined typed surface (content types + fields) for a project | config, definition |
| **Module** | A first-party or third-party extension that mounts server, CLI, or Studio surfaces | plugin, addon, extension |
| **Manifest** | A module's declaration file (`manifest.ts`) | descriptor, config |

## Operations

| Term | Meaning | Don't say |
| --------------- | -------------------------------------------------------------------- | -------------------------------- |
| **Pull** | Fetch documents from server to local files (CLI direction) | download, sync down, fetch |
| **Push** | Upload local file changes to server | upload, sync up, deploy |
| **Sync** | Two-sided reconciliation (push + pull with conflict resolution) | use only when actually two-sided |
| **Publish** | Move a draft document to the published environment | release, ship |
| **Schema sync** | Reconcile local `mdcms.config.ts` schema with server schema registry | schema migrate, schema deploy |

## Authorization

| Term | Meaning | Don't say |
| ----------------------- | ------------------------------------------------------------------------------------- | ----------------------------------- |
| **API key** | Long-lived bearer token for non-interactive clients | token, secret |
| **Session** | Browser-based interactive auth | cookie, login |
| **Loopback OAuth flow** | CLI's browser-based auth handoff using OAuth2 with a localhost callback (`127.0.0.1`) | device flow, OAuth flow, login flow |
| **Scope** | A permission claim attached to a key or session | permission, role, capability |

## Codebase shape

| Term | Meaning | Don't say |
| ----------------------------- | ------------------------------------------------------------------- | --------------------------------------------------------- |
| **Workspace** | A Bun + Nx package in the monorepo | project (overloaded), package (use only for npm packages) |
| **`@mdcms/source` condition** | Custom export condition that resolves to TypeScript source for dev | source export, dev export |
| **Studio** | The embeddable React component (`@mdcms/studio`) | admin UI, dashboard, panel |
| **Studio review** | `apps/studio-review` — internal contract-consumer for preview mocks | studio test, review app |

## Forbidden

- "Entry" — always use **document**.
- "Workspace" when referring to **project** — they're different concepts. ("Workspace" is fine when referring to a Bun + Nx package in the monorepo.)
- "Plugin" when referring to a first-party **module** — modules are first-class. (Third-party concepts that genuinely call themselves plugins, e.g. TipTap plugins, can keep that name.)
- "Device flow" for the CLI auth handoff — it is a **loopback OAuth flow** (RFC 8252), not RFC 8628 device authorization grant.
63 changes: 63 additions & 0 deletions .ai/memory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Memory

Persistent team knowledge about MDCMS — the product, the architecture, the integrations, the major efforts in flight, and the lessons we've already learned. Read this when you need _durable_ knowledge. For _volatile_ state (what someone is working on right now, current sprint, blockers), use the issue tracker — that's the right tool for it, and trying to mirror that state here causes merge conflicts on every concurrent PR.

## Layout

| Path | Contents | Update cadence |
| ------------------------------------ | --------------------------------------------------------------------- | -------------------------------------------------------- |
| [`product.md`](product.md) | Vision, audience, scope, why MDCMS exists | Rarely — only when product itself changes |
| [`architecture.md`](architecture.md) | System patterns, invariants, hard rules | When architecture decisions change |
| [`stack.md`](stack.md) | Runtime, deps, infrastructure, constraints | On dependency upgrades or infra changes |
| [`lessons.md`](lessons.md) | Append-only dev-time pitfalls | Append on discovery |
| [`topics/`](topics/) | Cross-cutting domain knowledge — auth flow, sync, multi-tenancy, etc. | When a topic stabilizes or changes meaningfully |
| [`integrations/`](integrations/) | External systems we depend on (Docker, GitHub Actions) | When config or auth changes |
| [`initiatives/`](initiatives/) | One file per major team effort — active and completed | When an initiative kicks off, hits a milestone, or wraps |

Vocabulary lives at [`../LANGUAGE.md`](../LANGUAGE.md). Architecture decision rationale lives at `docs/adrs/` (canonical product docs). Specs live at `docs/specs/`. Per-package guidance lives at `apps/*/AGENTS.md` and `packages/*/AGENTS.md`.

## How an agent should read this

```text
First-time / onboarding
└─ product.md → architecture.md → stack.md → skim topics/ index → skim initiatives/ active

Starting a feature
└─ architecture.md (invariants) → topics/ (relevant cross-cutting) → initiatives/ (does this fit something?)
→ docs/specs/<owning>.md (canonical scope, NOT in memory/)

Debugging a thing that broke
└─ lessons.md (have we seen this?) → topics/ (cross-cutting flow) → integrations/ (external system?)

Wondering why something is the way it is
└─ architecture.md (patterns) → docs/adrs/ (decision rationale) → initiatives/ (which effort drove this?)
```

## How to update

- **product.md / architecture.md / stack.md** — directly, in the PR that changes the underlying reality. Docs, not state. Reviewed in PR.
- **lessons.md** — append at the bottom on discovery. Lead with the rule, then `Why:` and `How to apply:`.
- **topics/** — add a new file when a cross-cutting concept stabilizes; update existing files when the flow changes. One topic per file.
- **integrations/** — add a new file when integrating a new external system; update on config/auth changes.
- **initiatives/** — create on kickoff, update on milestones, mark `Status: completed` on wrap. One initiative per file (no shared write surface = no merge conflicts).

## Why this shape (and not something more sophisticated)

Two reference systems we considered:

- **Hermes Agent** — minimalist 2-file (MEMORY.md + USER.md, ~3.6KB total), frozen-snapshot system-prompt injection, FTS5 over session history, designed for solo use.
- **Magic Context** — sophisticated 4-layer SQLite + vector embeddings + 5 access tools, designed for solo use.

Both are user-local. Neither is committed to a team git repo. SQLite databases don't merge cleanly; user-home memory doesn't share with the team.

Our system trades runtime sophistication (no semantic search yet, no auto-injection beyond `@AGENTS.md`) for **team-shareability and version control**. If we later want semantic search, the right move is an MCP server that indexes these markdown files — the files stay the source of truth.

## What this is _not_

- Not a substitute for issue tracking or todo lists — both belong in the issue tracker.
- Specs live at `docs/specs/`, not here.
- Don't treat this as a changelog — git log and PR descriptions cover that.
- Operator runbooks belong in package READMEs.
- Personal journals (Hermes-style `USER.md`) stay in user-home, not in the repo.

This is the **shared mental model** the team and its agents maintain about MDCMS.
56 changes: 56 additions & 0 deletions .ai/memory/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# System patterns

Architecture invariants and key technical decisions. Update when one changes.

## Source of truth

- **Database is canonical**, not the filesystem. The server is the only thing that owns truth.
- The CLI's local files are a working copy. Pull/push reconciles against the server, not vice versa.
- Schema (content types + fields) lives in the server's schema registry. The local `mdcms.config.ts` is the developer's authoring surface; `schema sync` reconciles it to the registry.

## Module system

- First-party modules live under `packages/modules/<module-id>/`.
- Each module ships `manifest.ts`, `server/index.ts`, `cli/index.ts`. Some also ship `studio/index.tsx`.
- The `installedModules` registry in `packages/modules/src/index.ts` is **deterministic and sorted by `manifest.id`**. Don't introduce ordering coupling.
- Server and CLI each have their own `module-loader.ts`. Both auto-mount at startup.
- Cross-module dependencies go through declared interfaces in `@mdcms/shared`, never direct imports between module packages.

## Package boundaries (hard rules)

- `@mdcms/shared` exports types, validators, pure utilities. **No runtime side effects, no HTTP, no DB.**
- `@mdcms/sdk` is read-only. Bearer-token client. **No write methods.**
- `@mdcms/cli` owns push/pull/sync logic and the loopback OAuth flow.
- `@mdcms/studio` runs inside the host app's process — embedded React component, not a separate page.
- `@mdcms/server` is the only thing that talks to the database.

## Conditional exports

Every package uses `@mdcms/source` as a custom condition pointing to TypeScript source for development. `import` and `default` point to `dist/` for production. **Don't break this convention** — dev-time source imports rely on it.

## Validation

- All inputs validated with **Zod 4** at module boundaries.
- Content schemas use **Standard Schema** for ecosystem interop.
- No double-validation; once a value is parsed, downstream code trusts the type.

## Tests

- **`*.test.ts`** — unit tests, co-located with source.
- **`*.contract.test.ts`** — Drizzle schema validated against actual SQL migrations. Catches drift between ORM definitions and migration outputs.
- **Integration:** `bun run integration` runs Docker health + migration check.
- **CI gate:** `bun run ci:required`.

## Studio review app

`apps/studio-review` is a maintained internal consumer of Studio + backend contracts, used to keep preview mocks aligned. Whenever a contract changes, update `apps/studio-review` handlers/fixtures/tests in the same commit. Don't let it drift.

## Standalone specs

Files under `docs/specs/` are **standalone canonical product documentation**. No task IDs, no external planning references, no "this task" language. Spec rationale either stays self-contained or moves to an ADR.

## Multi-tenant boundaries

- **Project** is the isolation unit. Every persistable entity carries `project_id`.
- **Environment** is a state within a project (e.g. `draft`, `prod`). Reads default to the published environment unless explicit.
- Tenant scoping is enforced at the route layer — every authenticated request resolves a project context before reaching domain code.
30 changes: 30 additions & 0 deletions .ai/memory/initiatives/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Initiatives

A file per major team effort — active and completed. Initiatives are bigger than tasks (which live in the issue tracker) and longer-running than features. Examples: a multi-quarter platform migration, an open-source release, a customer-driven SLA push.

## File format

`YYYY-MM-DD-kebab-case-name.md` for the start date.

Each file has these sections:

- **Status:** `active` | `completed` | `paused` | `abandoned`
- **Goal** — one paragraph, what success looks like
- **Why** — the constraint or opportunity that drove this
- **Scope** — in / out, with explicit "out" list
- **Key decisions** — bullets, with cross-refs to ADRs/specs/PRs
- **Outcome** (added when status moves off `active`) — what actually shipped, what lessons came out

Keep each file under ~200 lines. If it grows beyond that, split it into a parent + sub-initiatives.

## Active

(none yet)

## Completed

(none yet)

## Why initiatives instead of just commits + Jira

Jira tickets are scoped tasks. Commits are atomic changes. Neither captures the **why** of a multi-week / multi-month effort, the **scope boundary** that prevents drift, or the **outcomes** in a queryable form. A future agent (or new team member) asking "why did MDCMS adopt X?" gets a better answer from an initiative file than from twenty Jira tickets and forty commits.
18 changes: 18 additions & 0 deletions .ai/memory/integrations/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Integrations

External systems MDCMS depends on or integrates with. One file per system. Document what we use it for, how it's configured, and what failure modes look like — so a new contributor (or agent) can debug or replicate the setup without reverse-engineering it from config files.

## Index

- [`docker-stack.md`](docker-stack.md) — Local infrastructure (postgres, redis, minio, mailhog) via `docker-compose.yml`.
- [`github-actions.md`](github-actions.md) — CI gates and workflow files in `.github/workflows/`.

## Format

For each integration:

1. **What it is + why we use it.**
2. **Configuration** — where the config lives, what's in it, what's local-only vs committed.
3. **How agents interact with it** (if applicable) — MCP servers, CLI tools, auth setup.
4. **Failure modes** — common breakages and how to recognize them.
5. **Cross-refs.**
52 changes: 52 additions & 0 deletions .ai/memory/integrations/docker-stack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Docker stack

## What it is + why

Local infrastructure for development. MDCMS depends on PostgreSQL, Redis, MinIO (S3-compatible object storage), and MailHog (SMTP catch-all for dev email testing). All four run via `docker-compose.yml` at the repo root.

## Configuration

```bash
docker compose up -d --build # Bring up the stack
docker compose down # Tear down
docker compose logs -f <service> # Tail a service
```

Services:

- **postgres** — PostgreSQL 16. Used for content, auth, sessions, and audit logs.
- **redis** — Provisioned for caching, queues, and rate-limiting. Not currently a session store.
- **minio** — Media uploads (S3-compatible).
- **mailhog** — Dev-only SMTP capture; web UI at `localhost:8025`.

The server expects this stack on default ports. Ports and credentials live in `docker-compose.yml` and the server's `.env` (local-only).

## How agents interact

Mostly indirectly — agents run server commands that assume the stack is up:

```bash
bun --cwd apps/server run start # Server on :4000, expects docker stack up
bun run integration # Runs Docker health + migration check
```

For a clean re-run during debugging:

```bash
docker compose down -v && docker compose up -d --build
```

The `-v` flag drops volumes — useful when starting from a clean DB state, **not safe** if you want to preserve seeded content.

## Failure modes

- **Port conflicts.** If Postgres 5432 or Redis 6379 are already in use locally, `docker compose up` fails. Check with `lsof -i :5432`.
- **Volume corruption from killed containers.** `docker compose down -v` resets state.
- **Stale image after dependency upgrades.** `--build` rebuilds; without it, you might run an outdated server image.
- **MinIO bucket missing.** First-time setup may need explicit bucket creation (`mc mb local/mdcms`); check server logs for `NoSuchBucket`.

## Cross-refs

- Compose file: `docker-compose.yml`
- Per-package: `apps/server/AGENTS.md`
- Related: [`../stack.md`](../stack.md) for the broader stack context.
38 changes: 38 additions & 0 deletions .ai/memory/integrations/github-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# GitHub Actions

## What it is + why

CI gates running on push and PR. Defined in `.github/workflows/`. The required gate is `bun run ci:required` — anything that fails this blocks merge.

## Configuration

Workflow files live at `.github/workflows/*.yml`. Each workflow declares triggers (push, pull_request, schedule) and jobs.

## Required gate

`bun run ci:required` runs:

1. `bun run format:check` — Prettier check.
2. `bun run check` — Build + typecheck combined.
3. `bun run unit` — Unit tests via `bun test` orchestrated by Nx.
4. `bun run integration` — Docker health + migration check.

No git hook auto-runs this locally; running it manually before pushing avoids the round-trip wait of seeing failures in CI.

## How agents interact

- Read workflow files to understand what CI runs.
- Run `ci:required` locally before pushing — if it fails, the PR will too.
- Use `gh pr checks` or `gh run list` to inspect a PR's CI status without leaving the terminal.

## Failure modes

- **Format check failing** — almost always a missed `bun run format` before commit. Run it, commit the diff.
- **Typecheck failing on a PR but not locally** — usually means dependencies got out of sync; `bun install` and re-run.
- **Integration step timing out** — Docker stack startup is slow on cold caches. Local runs may pass while CI fails. Inspect the run logs for the specific service that didn't come up.

## Cross-refs

- Workflows: `.github/workflows/`
- AGENTS.md "Working in this repo" section — describes the local pre-push procedure.
- Per-package AGENTS.md — package-specific test/build commands.
19 changes: 19 additions & 0 deletions .ai/memory/lessons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Lessons

Append a new entry whenever you discover a non-obvious pitfall. Lead with the rule, then a `Why:` line, then a `How to apply:` line. Keep entries one short paragraph each — link to a commit or PR for full context if needed.

Entries are reverse-chronological (newest first).

---

<!-- Example shape (delete when first real entry is added):

## YYYY-MM-DD — short rule

**Rule:** State the takeaway in one sentence.

**Why:** The incident or constraint that produced this rule.

**How to apply:** When does this kick in. Where to look. What to do differently.

-->
Loading
Loading