Skip to main content

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 behaviour 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

├── loops/ # Custom loop configs (optional)
│ ├── dev.yaml # Overrides the bundled dev loop
│ └── docs.yaml # Custom documentation loop

├── jobs/ # Loop execution records
│ └── {job_id}/
│ └── {loop_name}-{task_id}.json

├── loop-state.json # Active loop pointer

└── releases/ # Drafted release notes (optional)
└── v0.3.0.md

File descriptions

config.md

Written by /dmx/init. Contains:

  • Workflow mode: jira, github-issues, or none
  • 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 behaviour and flows: why the project exists, how it works, who uses it. Written by /dmx/init, updated by /dmx/update-memory when user-facing behaviour 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). 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 tests

loops/*.yaml

Custom loop configs. Any file here overrides the bundled loop with the same name. See Loop Configuration.

jobs/{job_id}/{loop_name}-{task_id}.json

Full state for a single loop run. The job ID is the ticket ID from spec.md's frontmatter, falling back to the branch name. The task ID is a UUID4 assigned when the loop starts.

{
"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, complete, or failed.

loop-state.json

The active run pointer — points to the currently running or paused loop. Read by loop_continue and loop_advance to know which job/task to resume. It's a flat pointer, not a full state snapshot (that lives in the corresponding jobs/ file):

{
"active_job_id": "DM-123",
"active_task_id": "550e8400-e29b-41d4-a716-446655440000",
"active_loop_name": "dev"
}

The file is deleted once the active loop reaches a terminal state (no chained loop follows).

releases/{version}.md

Drafted release notes created by draft-release-note. Read by release-merge and create-release. Stored here so the notes can be reviewed and edited before the release is published.