Directory Structure
.dmx/: the project memory bank
The .dmx/ directory is committed to your repository. It contains project configuration, the memory bank, branch-level specs and plans, loop state, and execution records — all as flat files directly under .dmx/.
.dmx/
├── config.md # Project configuration
├── projectbrief.md # Project goals and scope
├── productContext.md # User-facing behavior and flows
├── systemPatterns.md # Architecture decisions, code conventions
├── techContext.md # Tech stack, dependencies, build system
├── activeContext.md # Learning inbox: open learnings, decisions, session notes
│
├── spec.md # Current branch: feature spec
├── tasks.md # Current branch: implementation plan
├── shared-sources.yaml # Org-wide shared sources (optional)
│
├── loops/ # Custom loop configs (optional)
│ ├── dev.yaml # Overrides the bundled dev loop
│ └── docs.yaml # Custom documentation loop
│
├── skills/ # Custom skills (optional)
│ ├── commit.md # Overrides the bundled commit skill in loops
│ └── security-review/ # Folder-shaped skill
│ └── SKILL.md
│
├── vendor/ # Vendored shared sources, written by /dmx/sync
│ └── {name}/
│ ├── .lock.json # Resolved commit SHA
│ ├── loops/
│ ├── skills/
│ └── validators/
│
├── jobs/ # Loop execution records (also the active-run signal)
│ └── {job_id}/
│ ├── {loop_name}-{task_id}.json
│ └── validation-report.json # Written by the validate skill
│
└── releases/ # Drafted release notes (optional)
└── v0.3.0.mdFile descriptions
config.md
Written by /dmx/init. Contains:
- Workflow mode:
jira,github-issues, ornone - Ticketing configuration (project key, board URL)
- Branch naming conventions (feature prefix, bug prefix)
- Protected branches (integration branch, production branch)
- Auto-detected tech stack
projectbrief.md
Project goals and scope. Written by /dmx/init from your README and project files. Durable — rarely updated after the initial setup.
productContext.md
User-facing behavior and flows: why the project exists, how it works, who uses it. Written by /dmx/init, updated by /dmx/create-pr and /dmx/update-memory when user-facing behavior changes.
systemPatterns.md
Architecture decisions and code conventions:
# System Patterns
## Repository pattern
All database access goes through repository classes in `src/repositories/`.
Service layer calls repositories, never accesses the DB directly.
## Error handling
Use custom exception classes from `src/exceptions.py`.
HTTP errors are caught in middleware and converted to standard error responses.
## Testing
Unit tests mock at the repository boundary.
Integration tests use a real test database via the `db_session` fixture.
Test files mirror the source structure under `tests/`.techContext.md
Describes the project's tech stack. Example content:
# Tech Context
## Language and runtime
Python 3.12, managed with uv
## Key dependencies
- FastAPI 0.111: web framework
- SQLAlchemy 2.0: ORM (async)
- Pydantic v2: validation
- pytest + pytest-asyncio: testing
## Build and tooling
- uv for package management and running
- ruff for linting and formatting
- mypy for type checking (strict mode)
- GitHub Actions for CI
## Database
PostgreSQL 16 via asyncpg. Migrations with Alembic.activeContext.md
The learning inbox — three sections: ## Open Learnings, ## Open Decisions, ## Session Notes. Updated by commit (light progress notes) and by the loop runtime (a one-line breadcrumb per loop run). create-pr promotes what's left in the inbox before opening the PR. update-memory reads it, promotes durable items into systemPatterns.md, techContext.md, or productContext.md, and clears what it promotes.
spec.md
The feature spec for the current branch. Created by create-ticket or derive-ticket. Read by plan to generate tasks.md. Used by the check_spec_complete and spec_adherence validators.
tasks.md
The phased implementation plan. Created by plan. Read by implement-next-phase and implement-next-task to know what to build. The repeat_until: all_phases_complete condition reads this file to check for unchecked items.
Format:
## Phase 1: Data model
- [x] Add `RateLimit` model with fields: `api_key`, `window_seconds`, `max_requests`
- [x] Write migration for the new table
- [x] Add model tests
## Phase 2: Service layer
- [ ] Implement `RateLimitService.check(api_key)` → bool
- [ ] Implement `RateLimitService.record_request(api_key)`
- [ ] Write service testsshared-sources.yaml
Declares the org-wide shared sources this repo pulls loops, skills, and validators from, each pinned to a tag, branch, or commit SHA. Optional — without it, dmx resolves from the repo and its bundled defaults only.
shared_sources:
- name: org-wide
source: "git::https://github.com/acme/dmx-shared.git//?ref=v1.4.0"loops/*.yaml
Custom loop configs. Any file here overrides the shared-source or bundled loop with the same name. See Loop Configuration.
skills/
Custom skills for this repo. When a loop runs a skill, dmx checks here first, then shared sources, then the bundled skills, so a file here overrides a skill of the same name. Each skill is either a flat {name}.md file or a {name}/SKILL.md folder with optional scripts/, references/, and assets/.
vendor/{name}/
A copy of a shared source's tree, written by /dmx/sync and committed to the repo. .lock.json records the source address and the commit SHA its ref resolved to. Don't edit these files by hand — the next sync replaces them. Change the source repo, or override a file in your own .dmx/loops/, .dmx/skills/, or validators/ instead.
jobs/{job_id}/{loop_name}-{task_id}.json
Full state for a single loop run. The job ID is the ticket from spec.md's frontmatter when the spec's branch matches the current branch and the ticket is a real ID (not none, unknown, or empty). Otherwise it's the current branch name, and a detached HEAD resolves to unknown. The task ID is a UUID4 assigned when the loop starts.
The loop runtime commits this file when the run finishes, together with the session-note breadcrumb it adds to activeContext.md. See Jobs, Tasks & Persistent State.
{
"loop_name": "dev",
"job_id": "DM-123",
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"status": "paused",
"current_skill_index": 1,
"skills": ["implement-next-phase", "commit"],
"skills_completed": ["implement-next-phase"],
"skill_outputs": {
"implement-next-phase": "Phase 1 complete. Added RateLimit model..."
},
"validator_results": [],
"outcome": null,
"iteration_count": 0,
"timestamp": "2026-08-21T09:14:22Z",
"updated_at": "2026-08-21T09:20:11Z"
}status is one of pending, running, paused, iterating, validating, complete, or failed. validating means the loop's validators are running in the background.
There's no separate pointer file for the active run. loop_advance, loop_continue, and loop_status find it by scanning a job's state files for the one with a non-terminal status — pending, running, paused, iterating, or validating. On a feature branch, a Release Loop run that create-pr marked complete before committing, with outcome still null, also counts until /dmx/loop-continue runs its validators. Everything else under jobs/ is historical: completed or failed runs from earlier in the ticket's lifecycle. Only files with loop_name, task_id, and status fields count as task state; artifacts that skills write into the job folder, like validation-report.json, are ignored. This is also what keeps state isolated per branch — the scan is scoped to the job id derived from the current branch/ticket, so switching branches and running a loop there can't touch another branch's paused run.
The Spec Loop is a special case: it creates a brand-new ticket, so it doesn't have a real job id yet when it starts. It runs under a temporary _pending-{task_id} folder until create-ticket writes a spec.md whose branch matches the new branch, at which point the folder is renamed to the real job ID. dmx won't rename it into a job folder that already has loop runs; the error tells you to fix the spec's frontmatter or move that folder aside. If you ever see a _pending-* folder lingering, it means a Spec Loop run didn't finish — resume it with /dmx/loop-continue or delete the folder if it's a stale leftover.
releases/{version}.md
Drafted release notes created by draft-release-note, which commits and pushes the file so it's included in the release-merge PR. Read by release-merge and create-release. Stored here so the notes can be reviewed and edited before the release is published.